feat: add filter options for images, including black & white and selfie effects, and enhance UI elements
This commit is contained in:
@@ -123,6 +123,65 @@ const addVignette = (ctx: CanvasRenderingContext2D, w: number, h: number, streng
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
};
|
||||
|
||||
const applyBlackAndWhite = (context: CanvasRenderingContext2D, width: number, height: number, volume: number = 5) => {
|
||||
const imageData = context.getImageData(0, 0, width, height);
|
||||
const data = imageData.data;
|
||||
|
||||
// 1. คำนวณ Contrast ตาม Volume (ยิ่งเยอะ ยิ่งตัดกันเข้ม)
|
||||
const contrast = (volume / 10) * 80; // ปรับความเข้มข้น (0-80)
|
||||
const contrastFactor = (259 * (contrast + 255)) / (255 * (259 - contrast));
|
||||
|
||||
// 2. ปรับ Brightness (Exposure) ชดเชยเวลา Contrast สูงๆ แล้วภาพมืด
|
||||
const brightness = volume * 2;
|
||||
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
const r = data[i]!;
|
||||
const g = data[i + 1]!;
|
||||
const b = data[i + 2]!;
|
||||
|
||||
// --- STEP 1: Grayscale Conversion (สูตร Portrait) ---
|
||||
// ปกติ: r*0.3, g*0.59, b*0.11
|
||||
// สูตรนี้: เพิ่มน้ำหนัก Red เป็น 0.4 เพื่อให้ "ผิวคน" ดูสว่างขึ้น (Skin Tone Brightening)
|
||||
let gray = (r * 0.4) + (g * 0.5) + (b * 0.1);
|
||||
|
||||
// --- STEP 2: Apply Contrast & Brightness ---
|
||||
gray = contrastFactor * (gray - 128) + 128 + brightness;
|
||||
|
||||
// --- STEP 3: Clamp Values ---
|
||||
// บังคับค่าให้อยู่ในช่วง 0-255 และกำหนดให้ทั้ง R, G, B เท่ากัน (เพื่อเป็นสีเทา)
|
||||
const finalGray = Math.max(0, Math.min(255, gray));
|
||||
|
||||
data[i] = finalGray; // R
|
||||
data[i + 1] = finalGray; // G
|
||||
data[i + 2] = finalGray; // B
|
||||
// data[i+3] คือ Alpha ไม่ต้องยุ่ง
|
||||
}
|
||||
|
||||
context.putImageData(imageData, 0, 0);
|
||||
|
||||
// --- STEP 4: Noir Effect (แสงฟุ้ง + ขอบมืด) ---
|
||||
// ถ้าปรับ volume เยอะๆ ให้เติมขอบดำ เพื่อให้ดูเป็นสไตล์ Noir/Classic
|
||||
if (volume > 3) {
|
||||
addNoirVignette(context, width, height, volume);
|
||||
}
|
||||
};
|
||||
// ฟังก์ชันสร้างขอบดำสำหรับขาวดำโดยเฉพาะ
|
||||
const addNoirVignette = (ctx: CanvasRenderingContext2D, w: number, h: number, strength: number) => {
|
||||
// ความเข้มของขอบดำ
|
||||
const opacity = (strength / 10) * 0.8; // สูงสุด 0.8 (เข้มกว่าแบบฟิล์ม)
|
||||
const radius = Math.max(w, h) * 0.7; // วงแคบกว่าฟิล์มหน่อย เพื่อเน้นหน้าคนตรงกลาง
|
||||
|
||||
const gradient = ctx.createRadialGradient(w/2, h/2, w/4, w/2, h/2, radius);
|
||||
gradient.addColorStop(0, "rgba(0,0,0,0)");
|
||||
gradient.addColorStop(1, `rgba(0,0,0,${opacity})`);
|
||||
|
||||
ctx.save();
|
||||
ctx.globalCompositeOperation = 'multiply'; // ใช้ Multiply ให้เงามันซึมลงไปในภาพ
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
const drawDateStamp = (context: CanvasRenderingContext2D, width: number, height: number) => {
|
||||
const now = new Date();
|
||||
|
||||
@@ -206,7 +265,16 @@ const captureSinglePhoto = () => {
|
||||
)
|
||||
|
||||
// Apply film effect with default volume
|
||||
applyFilmEffect(context, finalWidth, finalHeight, 5);
|
||||
// applyFilmEffect(context, finalWidth, finalHeight, 2);
|
||||
|
||||
const filter = localStorage.getItem('photobooth-filter') || 'normal'
|
||||
if (filter === 'black&white') {
|
||||
applyBlackAndWhite(context, finalWidth, finalHeight, 3);
|
||||
} else {
|
||||
applyFilmEffect(context, finalWidth, finalHeight, 5); // Default volume = 5
|
||||
}
|
||||
|
||||
|
||||
drawDateStamp(context, finalWidth, finalHeight);
|
||||
|
||||
// แปลงเป็น base64 ด้วย quality 0.7 เพื่อลดขนาด
|
||||
|
||||
Reference in New Issue
Block a user