This programming guide is only available in Vietnamese. Switch to Vietnamese to read the full article.

Pixel manipulation cho phép bạn truy cập và sửa đổi từng pixel trên canvas — mở ra khả năng tạo image filters, hiệu ứng đặc biệt và công cụ xử lý ảnh ngay trên trình duyệt.

1. ImageData Object

getImageData() trả về object chứa mảng pixel data dạng Uint8ClampedArray. Mỗi pixel gồm 4 giá trị RGBA (0-255).

imagedata_basics.js
// Lấy pixel data từ canvas
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray

// Data layout: [R, G, B, A, R, G, B, A, ...]
// Mỗi pixel = 4 bytes (Red, Green, Blue, Alpha)
console.log(data.length); // width * height * 4

// Truy cập pixel tại (x, y)
function getPixel(data, width, x, y) {
  const index = (y * width + x) * 4;
  return {
    r: data[index],
    g: data[index + 1],
    b: data[index + 2],
    a: data[index + 3]
  };
}

// Set pixel tại (x, y)
function setPixel(data, width, x, y, r, g, b, a = 255) {
  const index = (y * width + x) * 4;
  data[index]     = r;
  data[index + 1] = g;
  data[index + 2] = b;
  data[index + 3] = a;
}

// Tạo ImageData mới (tất cả pixel transparent đen)
const newImageData = ctx.createImageData(200, 200);
// Hoặc clone size từ imageData có sẵn
const cloned = ctx.createImageData(imageData);

2. Filter: Grayscale & Sepia

Grayscale chuyển ảnh sang đen trắng. Có 2 cách: trung bình đơn giản hoặc dùng hệ số luminance (chính xác hơn). Hãy thử trực tiếp các bộ lọc khác nhau với demo bên dưới — ảnh nguồn được vẽ bằng code (không cần tải file), sau đó áp dụng filter qua việc duyệt từng pixel RGBA:

🎨 Demo tương tác: Image Filters
grayscale_sepia.js
function applyGrayscale(ctx, canvas) {
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;

  for (let i = 0; i < data.length; i += 4) {
    // Luminance formula (ITU-R BT.709)
    const gray = 0.299 * data[i] + 0.587 * data[i+1] + 0.114 * data[i+2];
    data[i]     = gray; // R
    data[i + 1] = gray; // G
    data[i + 2] = gray; // B
    // Alpha giữ nguyên
  }

  ctx.putImageData(imageData, 0, 0);
}

function applySepia(ctx, canvas) {
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;

  for (let i = 0; i < data.length; i += 4) {
    const r = data[i], g = data[i+1], b = data[i+2];

    data[i]     = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189); // R
    data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168); // G
    data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131); // B
  }

  ctx.putImageData(imageData, 0, 0);
}

3. Filter: Brightness, Contrast & Invert

Các filter đơn giản thao tác trực tiếp trên giá trị pixel.

brightness_contrast.js
function applyBrightness(ctx, canvas, amount) {
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;

  for (let i = 0; i < data.length; i += 4) {
    data[i]     = Math.max(0, Math.min(255, data[i] + amount));
    data[i + 1] = Math.max(0, Math.min(255, data[i+1] + amount));
    data[i + 2] = Math.max(0, Math.min(255, data[i+2] + amount));
  }
  ctx.putImageData(imageData, 0, 0);
}

function applyContrast(ctx, canvas, factor) {
  // factor > 1 = tăng contrast, < 1 = giảm
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;

  for (let i = 0; i < data.length; i += 4) {
    data[i]     = Math.max(0, Math.min(255, ((data[i] - 128) * factor) + 128));
    data[i + 1] = Math.max(0, Math.min(255, ((data[i+1] - 128) * factor) + 128));
    data[i + 2] = Math.max(0, Math.min(255, ((data[i+2] - 128) * factor) + 128));
  }
  ctx.putImageData(imageData, 0, 0);
}

function applyInvert(ctx, canvas) {
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;

  for (let i = 0; i < data.length; i += 4) {
    data[i]     = 255 - data[i];
    data[i + 1] = 255 - data[i + 1];
    data[i + 2] = 255 - data[i + 2];
  }
  ctx.putImageData(imageData, 0, 0);
}

4. Convolution Filters: Blur & Edge Detection

Convolution filter áp dụng kernel matrix lên mỗi pixel, tính toán dựa trên pixel lân cận. Đây là nền tảng của blur, sharpen và edge detection.

convolution.js
function applyConvolution(ctx, canvas, kernel, divisor = 1) {
  const src = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const dst = ctx.createImageData(canvas.width, canvas.height);
  const w = canvas.width, h = canvas.height;
  const kSize = Math.sqrt(kernel.length);
  const half = Math.floor(kSize / 2);

  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      let r = 0, g = 0, b = 0;

      for (let ky = 0; ky < kSize; ky++) {
        for (let kx = 0; kx < kSize; kx++) {
          const px = Math.min(w-1, Math.max(0, x + kx - half));
          const py = Math.min(h-1, Math.max(0, y + ky - half));
          const idx = (py * w + px) * 4;
          const weight = kernel[ky * kSize + kx];

          r += src.data[idx]     * weight;
          g += src.data[idx + 1] * weight;
          b += src.data[idx + 2] * weight;
        }
      }

      const idx = (y * w + x) * 4;
      dst.data[idx]     = Math.max(0, Math.min(255, r / divisor));
      dst.data[idx + 1] = Math.max(0, Math.min(255, g / divisor));
      dst.data[idx + 2] = Math.max(0, Math.min(255, b / divisor));
      dst.data[idx + 3] = src.data[idx + 3]; // giữ alpha
    }
  }
  ctx.putImageData(dst, 0, 0);
}

// Box Blur 3x3
const boxBlur = [1,1,1, 1,1,1, 1,1,1];
applyConvolution(ctx, canvas, boxBlur, 9);

// Gaussian Blur 3x3
const gaussianBlur = [1,2,1, 2,4,2, 1,2,1];
applyConvolution(ctx, canvas, gaussianBlur, 16);

// Sobel Edge Detection (horizontal)
const sobelX = [-1,0,1, -2,0,2, -1,0,1];
applyConvolution(ctx, canvas, sobelX);

// Sharpen
const sharpen = [0,-1,0, -1,5,-1, 0,-1,0];
applyConvolution(ctx, canvas, sharpen);

5. Thực hành: Color Picker Tool

Xây dựng công cụ chọn màu: click vào canvas để lấy màu pixel tại vị trí đó. Di chuyển chuột trên demo bên dưới — giá trị RGBA tại con trỏ được đọc trực tiếp qua getImageData(x, y, 1, 1) và hiển thị real-time:

🎨 Demo tương tác: Color Picker (rê chuột)
Vị trí
RGBA
HEX
Màu
color_picker.js
function setupColorPicker(canvas) {
  const ctx = canvas.getContext('2d');

  // Load ảnh lên canvas
  const img = new Image();
  img.crossOrigin = 'anonymous'; // cần cho getImageData
  img.onload = () => {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);
  };
  img.src = 'photo.jpg';

  // Click handler
  canvas.addEventListener('click', (e) => {
    const rect = canvas.getBoundingClientRect();
    const scaleX = canvas.width / rect.width;
    const scaleY = canvas.height / rect.height;

    const x = Math.floor((e.clientX - rect.left) * scaleX);
    const y = Math.floor((e.clientY - rect.top) * scaleY);

    // Lấy pixel tại vị trí click
    const pixel = ctx.getImageData(x, y, 1, 1).data;
    const [r, g, b, a] = pixel;

    // Chuyển sang HEX
    const hex = '#' + [r, g, b].map(v =>
      v.toString(16).padStart(2, '0')
    ).join('');

    console.log(`RGB: (${r}, ${g}, ${b})`);
    console.log(`HEX: ${hex}`);
    console.log(`Alpha: ${a}`);

    // Hiển thị preview swatch
    const swatch = document.getElementById('colorSwatch');
    swatch.style.backgroundColor = hex;
    document.getElementById('colorValue').textContent = hex;
  });
}

6. Câu hỏi trắc nghiệm ôn tập

Trắc nghiệm 1: Pixel index

Pixel tại (x=10, y=5) trên canvas width=100, index trong data array là bao nhiêu?

Trắc nghiệm 2: Uint8ClampedArray

Nếu gán data[i] = 300, giá trị thực tế sẽ là?

Trắc nghiệm 3: Convolution kernel

Box blur 3x3 dùng divisor bằng bao nhiêu?

Tải file code thực hành minh họa bài học

File script tổng hợp các ví dụ về pixel manipulation, filters và color picker:

Tải về canvas_pixels.js

Related Articles

Bài viết liên quan trong series

Lesson 6: Math Foundations for Canvas: Trigonometry, 2D Vectors & Matrices Bài 6: Toán Nền Tảng: Lượng Giác, Vector 2D & Ma Trận Lesson 4: Compositing, Transparency & Clipping in Canvas Bài 4: Compositing, Độ trong suốt & Clipping trong Canvas Back to Canvas Series Overview Quay lại Lộ trình Canvas Series