-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Open
Description
Canvas toBuffer() ignores pixelFormat option and returns RGBA instead of RGB16_565
Issue Summary
When creating a canvas with pixelFormat: 'RGB16_565' and calling toBuffer('raw', { pixelFormat: 'RGB16_565' }), the method returns a 32-bit RGBA buffer instead of the expected 16-bit RGB565 buffer.
Environment
- Node.js version: [Your Node.js version]
- Canvas library version: 2.11.2
- Operating System: Linux (Raspberry Pi) Debian GNU/Linux 12 (bookworm)
- Platform: ARM64
Expected Behavior
When using:
const canvas = createCanvas(1920, 1080, 'image', { pixelFormat: 'RGB16_565' });
const ctx = canvas.getContext('2d', { pixelFormat: 'RGB16_565', alpha: false });
const buffer = canvas.toBuffer('raw', { pixelFormat: 'RGB16_565' });The buffer should contain RGB565 data with:
- Expected size: 1920 × 1080 × 2 = 4,147,200 bytes (16-bit per pixel)
- Expected format: RGB565 (5-bit red, 6-bit green, 5-bit blue)
Actual Behavior
The buffer contains RGBA data with:
- Actual size: 1920 × 1080 × 4 = 8,294,400 bytes (32-bit per pixel)
- Actual format: RGBA (8-bit red, 8-bit green, 8-bit blue, 8-bit alpha)
Reproduction Code
const { createCanvas } = require('canvas');
// Create canvas with RGB16_565 format
const canvas = createCanvas(1920, 1080, 'image', { pixelFormat: 'RGB16_565' });
const ctx = canvas.getContext('2d', {
pixelFormat: 'RGB16_565',
alpha: false
});
// Draw something
ctx.fillStyle = '#FF0000';
ctx.fillRect(0, 0, 100, 100);
// Try to get RGB565 buffer
const buffer = canvas.toBuffer('raw', { pixelFormat: 'RGB16_565' });
// Check buffer size
const expectedSize = 1920 * 1080 * 2; // RGB565 = 2 bytes per pixel
const actualSize = buffer.length;
const bitsPerPixel = (actualSize / (1920 * 1080)) * 8;
console.log(`Expected size (RGB565): ${expectedSize} bytes`);
console.log(`Actual size: ${actualSize} bytes`);
console.log(`Bits per pixel: ${bitsPerPixel}`);
console.log(`Format detected: ${bitsPerPixel === 16 ? 'RGB565' : 'RGBA'}`);Output
Expected size (RGB565): 4147200 bytes
Actual size: 8294400 bytes
Bits per pixel: 32
Format detected: RGBA
Impact
This issue prevents direct framebuffer rendering on embedded systems (like Raspberry Pi) that expect RGB565 format. Currently, we need to manually convert RGBA to RGB565, which:
- Doubles memory usage (8MB instead of 4MB for 1920×1080)
- Increases CPU usage due to manual conversion
- Reduces performance significantly on ARM devices
- Defeats the purpose of specifying
pixelFormat: 'RGB16_565'
Current Workaround
// Manual RGBA to RGB565 conversion
const imageData = ctx.getImageData(0, 0, width, height).data;
const bufferSize = width * height * 2;
const buffer = Buffer.alloc(bufferSize);
let bufferIndex = 0;
for (let i = 0; i < imageData.length; i += 4) {
const r = imageData[i];
const g = imageData[i + 1];
const b = imageData[i + 2];
const r5 = Math.round(r * 31 / 255) & 0x1F;
const g6 = Math.round(g * 63 / 255) & 0x3F;
const b5 = Math.round(b * 31 / 255) & 0x1F;
const rgb565 = (r5 << 11) | (g6 << 5) | b5;
buffer[bufferIndex++] = rgb565 & 0xFF;
buffer[bufferIndex++] = (rgb565 >> 8) & 0xFF;
}Expected Fix
The toBuffer('raw', { pixelFormat: 'RGB16_565' }) method should:
- Respect the
pixelFormatoption - Return a buffer with the correct size (width × height × 2 bytes)
- Contain actual RGB565-encoded pixel data
- Work consistently with the canvas pixel format configuration
Additional Context
This issue is critical for embedded applications that need to write directly to framebuffers in RGB565 format. The current behavior makes the pixelFormat option essentially useless for toBuffer() operations.
Related
- Canvas configuration works correctly for rendering
getImageData()returns RGBA as expected (since it's the standard)- Only
toBuffer()withpixelFormatoption is affected
Metadata
Metadata
Assignees
Labels
No labels