Automattic / Automattic/node-canvas
Canvas toBuffer() ignores pixelFormat option and returns RGBA instead of RGB16_565
- Dominant language
- JavaScript
- Stars
- 10.7k
- Forks
- 1.2k
- Avg merge
- 4d 8h
- Merged PRs (30d)
- 1
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:
```javascript
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
```javascript
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:
1. **Doubles memory usage** (8MB instead of 4MB for 1920×1080)
2. **Increases CPU usage** due to manual conversion
3. **Reduces performance** significantly on ARM devices
4. **Defeats the purpose** of specifying `pixelFormat: 'RGB16_565'`
## Current Workaround
```javascript
// 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:
1. Respect the `pixelFormat` option
2. Return a buffer with the correct size (width × height × 2 bytes)
3. Contain actual RGB565-encoded pixel data
4. 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()` with `pixelFormat` option is affected
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.