Julusian / node-elgato-stream-deck

A Node.js library for interfacing with the Elgato Stream Deck. https://julusian.github.io/node-elgato-stream-deck/
https://www.npmjs.com/org/elgato-stream-deck
MIT License
160 stars 21 forks source link

RangeError when calling fillPanelBuffer or fillKeyBuffer #56

Closed ianwijma closed 2 years ago

ianwijma commented 2 years ago

Thanks for the great project!

I'm bumping into some issues with regards of images within node, which I might be the one to blame for as I might miss something. It's with regard of writing an image to a key.

I have a solid coloured test image, which dimensions matches the ICON_SIZE constant, which is 96x96. green

I wrote a small test script that reads that image, and sends it off to the device to display it:

import { openStreamDeck } from "@elgato-stream-deck/node";
import fs from "fs";

const device = await openStreamDeck();

const image = await fs.promises.readFile("assets/green.png", { encoding: 'utf8' });

await device.fillKeyBuffer(1, image, {format: 'rgb'});

But when I run this fairly simple script I get the following error:

/<redacted>/streamdeck-node/node_modules/@elgato-stream-deck/core/dist/models/base.js:90
            throw new RangeError(`Expected image buffer of length ${imageSize}, got length ${imageBuffer.length}`);
                  ^

RangeError: Expected image buffer of length 27648, got length 321
    at StreamDeckXL.fillKeyBuffer (/<redacted>/streamdeck-node/node_modules/@elgato-stream-deck/core/dist/models/base.js:90:19)
    at StreamDeckNode.fillKeyBuffer (/<redacted>/streamdeck-node/node_modules/@elgato-stream-deck/core/dist/proxy.js:46:28)
    at file:///<redacted>/streamdeck-node/tests/image.js:8:14

I understand why this error happens, because the image is tiny. But the image is still 96x96.

After checking out the webhid demo project I decided to make another small script that uses canvas in node to create the image, but I get similar results.

import { openStreamDeck } from "@elgato-stream-deck/node";
import { createCanvas } from '@napi-rs/canvas';

const device = await openStreamDeck();

const canvas = createCanvas(96, 96);
const context = canvas.getContext('2d');
context.fillStyle = '#9A920B'
context.fillRect(0, 0, 96, 96);

const image = await canvas.toBuffer('image/png');

await device.fillKeyBuffer(1, image, {format: 'rgb'});

I had similar issues with the fillPanelBuffer method, where I took a screenshot using puppeteer:

import { openStreamDeck } from "@elgato-stream-deck/node";
import puppeteer from 'puppeteer';

const device = await openStreamDeck();
const DEVICE_WIDTH = device.ICON_SIZE * device.KEY_COLUMNS;
const DEVICE_HEIGHT = device.ICON_SIZE * device.KEY_ROWS;

const browser = await puppeteer.launch({ headless: false, defaultViewport: {width: DEVICE_WIDTH, height: DEVICE_HEIGHT} });
const page = await browser.newPage();
await page.goto('https://google.com/')
const image = await page.screenshot({
    type: 'jpeg',
    clip: {
        x: 0,
        y: 0,
        width: DEVICE_WIDTH,
        height: DEVICE_HEIGHT
    }
});

await device.fillPanelBuffer(image);

But this resulted into a similar error:

/<redacted>/streamdeck-node/node_modules/@elgato-stream-deck/core/dist/models/base.js:105
            throw new RangeError(`Expected image buffer of length ${imageSize}, got length ${imageBuffer.length}`);
                  ^

RangeError: Expected image buffer of length 884736, got length 21664
    at StreamDeckXL.fillPanelBuffer (/<redacted>/streamdeck-node/node_modules/@elgato-stream-deck/core/dist/models/base.js:105:19)
    at StreamDeckNode.fillPanelBuffer (/<redacted>/streamdeck-node/node_modules/@elgato-stream-deck/core/dist/proxy.js:49:28)
    at file:///<redacted>/streamdeck-node/tests/chrome.js:21:14
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Dumping this image does result into a image with correct dimensions: chrome

It could be that I miss some parsing of the image buffer, but I can't seem to find what's wrong.

Julusian commented 2 years ago

It looks like you aren't decoding the ong file to a pixel buffer before passing it into fillKeyBuffer. If you use pngjs or canvas or something to convert the image first, that should fix your issue

ianwijma commented 2 years ago

Awesome, thanks allot! I found out that the library that I use Jimp has the pixel buffer available, so I could use that.

Julusian commented 2 years ago

Yes jimp also works well. There is an example using that too https://github.com/Julusian/node-elgato-stream-deck/blob/master/packages/node/examples/fill-button-when-pressed-jimp.js

ianwijma commented 2 years ago

OMG, yes! I some how missed that folder with examples! Thanks lots again for the awesome lib! :D