From 8bd045ab0b4355fe1fa6362cd30bfd8242759477 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Tue, 15 Nov 2022 18:57:47 +0000 Subject: [PATCH 1/4] wip --- packages/core/src/__tests__/helpers.ts | 2 +- packages/core/src/__tests__/hid.ts | 6 ++--- packages/core/src/__tests__/test.spec.ts | 18 ++++++------- packages/core/src/__tests__/util.spec.ts | 2 +- packages/core/src/device.ts | 6 ++--- packages/core/src/imageWriter/imageWriter.ts | 6 ++--- packages/core/src/imageWriter/types.ts | 4 +-- packages/core/src/models/base-gen2.ts | 5 +++- packages/core/src/models/base.ts | 25 ++++++++++++------- packages/core/src/models/mini.ts | 5 +++- packages/core/src/models/miniv2.ts | 5 +++- packages/core/src/models/original.ts | 5 +++- packages/core/src/models/pedal.ts | 8 ++++-- packages/core/src/models/plus.ts | 9 ++++--- packages/core/src/proxy.ts | 8 +++--- packages/core/src/types.ts | 8 +++--- packages/core/src/util.ts | 12 ++++----- packages/node/examples/text-generation.js | 2 +- packages/node/src/__tests__/helpers.ts | 2 +- .../node/src/__tests__/jpeg-encoding.spec.ts | 4 +-- .../node/src/__tests__/jpeg-library.spec.ts | 4 +-- packages/node/src/device.ts | 8 +++--- packages/node/src/index.ts | 2 +- packages/node/src/jpeg.ts | 4 +-- packages/webhid/src/device.ts | 4 +-- packages/webhid/src/jpeg.ts | 2 +- 26 files changed, 96 insertions(+), 70 deletions(-) diff --git a/packages/core/src/__tests__/helpers.ts b/packages/core/src/__tests__/helpers.ts index cec1c24..3aacd28 100644 --- a/packages/core/src/__tests__/helpers.ts +++ b/packages/core/src/__tests__/helpers.ts @@ -1,7 +1,7 @@ import * as fs from 'fs' import * as path from 'path' -export function readFixtureJSON(fileName: string): Buffer { +export function readFixtureJSON(fileName: string): Uint8Array { const filePath = path.resolve(__dirname, '../../../../fixtures', fileName) const fileData = fs.readFileSync(filePath) return Buffer.from(JSON.parse(fileData.toString()) as Array) diff --git a/packages/core/src/__tests__/hid.ts b/packages/core/src/__tests__/hid.ts index 241f57d..ababed8 100644 --- a/packages/core/src/__tests__/hid.ts +++ b/packages/core/src/__tests__/hid.ts @@ -10,13 +10,13 @@ export class DummyHID extends EventEmitter implements HIDDevice { expect(typeof path).toEqual('string') } - public async sendFeatureReport(_data: Buffer): Promise { + public async sendFeatureReport(_data: Uint8Array): Promise { throw new Error('Method not implemented.') } - public async getFeatureReport(_reportId: number, _reportLength: number): Promise { + public async getFeatureReport(_reportId: number, _reportLength: number): Promise { throw new Error('Method not implemented.') } - public async sendReports(_data: Buffer[]): Promise { + public async sendReports(_data: Uint8Array[]): Promise { throw new Error('Method not implemented.') } public async close(): Promise { diff --git a/packages/core/src/__tests__/test.spec.ts b/packages/core/src/__tests__/test.spec.ts index cb2cda3..b78104d 100644 --- a/packages/core/src/__tests__/test.spec.ts +++ b/packages/core/src/__tests__/test.spec.ts @@ -10,7 +10,7 @@ import { DummyHID } from './hid' import { EncodeJPEGHelper } from '../models/base' function openStreamDeck(path: string, deviceModel: DeviceModelId, userOptions?: OpenStreamDeckOptions): StreamDeck { - const encodeJpegMock: jest.MockedFunction = jest.fn((_b: Buffer, _w: number, _h: number) => { + const encodeJpegMock: jest.MockedFunction = jest.fn((_b: Uint8Array, _w: number, _h: number) => { throw new Error('Not implemented') }) const options: Required = { @@ -115,7 +115,7 @@ function runForDevice(path: string, model: DeviceModelId): void { test('firmwareVersion', async () => { const device = getDevice() - device.getFeatureReport = async (): Promise => { + device.getFeatureReport = async (): Promise => { return Buffer.from([4, 85, 170, 212, 4, 49, 46, 48, 46, 49, 55, 48, 49, 51, 51, 0, 0]) } @@ -125,7 +125,7 @@ function runForDevice(path: string, model: DeviceModelId): void { test('serialNumber', async () => { const device = getDevice() - device.getFeatureReport = async (): Promise => { + device.getFeatureReport = async (): Promise => { return Buffer.from([3, 85, 170, 211, 3, 65, 76, 51, 55, 71, 49, 65, 48, 50, 56, 52, 48]) } @@ -167,7 +167,7 @@ function runForDevice(path: string, model: DeviceModelId): void { test('firmwareVersion-jpeg', async () => { const device = getDevice() - device.getFeatureReport = async (): Promise => { + device.getFeatureReport = async (): Promise => { // prettier-ignore return Buffer.from([ 5, 12, 254, 90, 239, 250, 49, 46, 48, 48, 46, 48, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) } @@ -178,7 +178,7 @@ function runForDevice(path: string, model: DeviceModelId): void { test('serialNumber-jpeg', async () => { const device = getDevice() - device.getFeatureReport = async (): Promise => { + device.getFeatureReport = async (): Promise => { // prettier-ignore return Buffer.from([ 6, 12, 67, 76, 49, 56, 73, 49, 65, 48, 48, 57, 49, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) } @@ -589,7 +589,7 @@ describe('StreamDeck XL', () => { test('firmwareVersion', async () => { const device = getDevice() - device.getFeatureReport = async (): Promise => { + device.getFeatureReport = async (): Promise => { // prettier-ignore return Buffer.from([ 5, 12, 254, 90, 239, 250, 49, 46, 48, 48, 46, 48, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) } @@ -600,7 +600,7 @@ describe('StreamDeck XL', () => { test('serialNumber', async () => { const device = getDevice() - device.getFeatureReport = async (): Promise => { + device.getFeatureReport = async (): Promise => { // prettier-ignore return Buffer.from([ 6, 12, 67, 76, 49, 56, 73, 49, 65, 48, 48, 57, 49, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) } @@ -611,7 +611,7 @@ describe('StreamDeck XL', () => { test('fillImage', async () => { const device = getDevice() - device.encodeJPEG.mockImplementationOnce(async (buffer: Buffer) => { + device.encodeJPEG.mockImplementationOnce(async (buffer: Uint8Array) => { const start = buffer.length / 8 return buffer.subarray(start, start * 2) }) @@ -664,7 +664,7 @@ describe('StreamDeck Original V2', () => { test('fillImage', async () => { const device = getDevice() - device.encodeJPEG.mockImplementationOnce(async (buffer: Buffer) => { + device.encodeJPEG.mockImplementationOnce(async (buffer: Uint8Array) => { const start = buffer.length / 8 return buffer.subarray(start, start * 2) }) diff --git a/packages/core/src/__tests__/util.spec.ts b/packages/core/src/__tests__/util.spec.ts index a2c4f63..895a470 100644 --- a/packages/core/src/__tests__/util.spec.ts +++ b/packages/core/src/__tests__/util.spec.ts @@ -1,6 +1,6 @@ import { transformImageBuffer } from '../util' -function getSimpleBuffer(dim: number, components: 3 | 4): Buffer { +function getSimpleBuffer(dim: number, components: 3 | 4): Uint8Array { const buf = Buffer.alloc(dim * dim * components) for (let i = 0; i < buf.length; i++) { buf[i] = i diff --git a/packages/core/src/device.ts b/packages/core/src/device.ts index 6585a1d..fb48274 100644 --- a/packages/core/src/device.ts +++ b/packages/core/src/device.ts @@ -10,10 +10,10 @@ export interface HIDDevice { close(): Promise - sendFeatureReport(data: Buffer): Promise - getFeatureReport(reportId: number, reportLength: number): Promise + sendFeatureReport(data: Uint8Array): Promise + getFeatureReport(reportId: number, reportLength: number): Promise - sendReports(buffers: Buffer[]): Promise + sendReports(buffers: Uint8Array[]): Promise getDeviceInfo(): Promise } diff --git a/packages/core/src/imageWriter/imageWriter.ts b/packages/core/src/imageWriter/imageWriter.ts index 66a098b..1225654 100644 --- a/packages/core/src/imageWriter/imageWriter.ts +++ b/packages/core/src/imageWriter/imageWriter.ts @@ -4,7 +4,7 @@ import { StreamdeckImageHeaderGenerator, StreamdeckImageWriter, StreamdeckImageW export class StreamdeckOriginalImageWriter implements StreamdeckImageWriter { private readonly headerGenerator = new StreamdeckGen1ImageHeaderGenerator() - public generateFillImageWrites(props: StreamdeckImageWriterProps, byteBuffer: Buffer): Buffer[] { + public generateFillImageWrites(props: StreamdeckImageWriterProps, byteBuffer: Uint8Array): Uint8Array[] { const MAX_PACKET_SIZE = 8191 const PACKET_HEADER_LENGTH = this.headerGenerator.getFillImageCommandHeaderLength() @@ -33,12 +33,12 @@ export class StreamdeckDefaultImageWriter this.headerGenerator = headerGenerator } - public generateFillImageWrites(props: TProps, byteBuffer: Buffer): Buffer[] { + public generateFillImageWrites(props: TProps, byteBuffer: Uint8Array): Uint8Array[] { const MAX_PACKET_SIZE = 1024 const PACKET_HEADER_LENGTH = this.headerGenerator.getFillImageCommandHeaderLength() const MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - PACKET_HEADER_LENGTH - const result: Buffer[] = [] + const result: Uint8Array[] = [] let remainingBytes = byteBuffer.length for (let part = 0; remainingBytes > 0; part++) { diff --git a/packages/core/src/imageWriter/types.ts b/packages/core/src/imageWriter/types.ts index bdf5c70..6c2774f 100644 --- a/packages/core/src/imageWriter/types.ts +++ b/packages/core/src/imageWriter/types.ts @@ -5,13 +5,13 @@ export interface StreamdeckImageWriterProps { } export interface StreamdeckImageWriter { - generateFillImageWrites(props: TProps, byteBuffer: Buffer): Buffer[] + generateFillImageWrites(props: TProps, byteBuffer: Uint8Array): Uint8Array[] } export interface StreamdeckImageHeaderGenerator { getFillImageCommandHeaderLength(): number writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, props: TProps, partIndex: number, isLast: boolean, diff --git a/packages/core/src/models/base-gen2.ts b/packages/core/src/models/base-gen2.ts index 52dbb78..ec19119 100644 --- a/packages/core/src/models/base-gen2.ts +++ b/packages/core/src/models/base-gen2.ts @@ -74,7 +74,10 @@ export abstract class StreamDeckGen2Base extends StreamDeckBase { return val.toString('ascii', 2, end) } - protected async convertFillImage(sourceBuffer: Buffer, sourceOptions: InternalFillImageOptions): Promise { + protected async convertFillImage( + sourceBuffer: Uint8Array, + sourceOptions: InternalFillImageOptions + ): Promise { const byteBuffer = transformImageBuffer( sourceBuffer, sourceOptions, diff --git a/packages/core/src/models/base.ts b/packages/core/src/models/base.ts index 41964bf..4229e91 100644 --- a/packages/core/src/models/base.ts +++ b/packages/core/src/models/base.ts @@ -12,7 +12,7 @@ import { } from '../types' import type { StreamdeckImageWriter } from '../imageWriter/types' -export type EncodeJPEGHelper = (buffer: Buffer, width: number, height: number) => Promise +export type EncodeJPEGHelper = (buffer: Uint8Array, width: number, height: number) => Promise export interface OpenStreamDeckOptions { useOriginalKeyOrder?: boolean @@ -153,15 +153,19 @@ export abstract class StreamDeckInputBase extends EventEmitter } public abstract fillKeyColor(keyIndex: KeyIndex, r: number, g: number, b: number): Promise - public abstract fillKeyBuffer(keyIndex: KeyIndex, imageBuffer: Buffer, options?: FillImageOptions): Promise - public abstract fillPanelBuffer(imageBuffer: Buffer, options?: FillPanelOptions): Promise + public abstract fillKeyBuffer( + keyIndex: KeyIndex, + imageBuffer: Uint8Array, + options?: FillImageOptions + ): Promise + public abstract fillPanelBuffer(imageBuffer: Uint8Array, options?: FillPanelOptions): Promise public async fillLcd(_imageBuffer: Buffer, _sourceOptions: FillImageOptions): Promise { throw new Error('Not supported for this model') } public async fillEncoderLcd( _index: EncoderIndex, - _buffer: Buffer, + _buffer: Uint8Array, _sourceOptions: FillImageOptions ): Promise { throw new Error('Not supported for this model') @@ -169,7 +173,7 @@ export abstract class StreamDeckInputBase extends EventEmitter public async fillLcdRegion( _x: number, _y: number, - _imageBuffer: Buffer, + _imageBuffer: Uint8Array, _sourceOptions: FillLcdImageOptions ): Promise { throw new Error('Not supported for this model') @@ -212,7 +216,7 @@ export abstract class StreamDeckBase extends StreamDeckInputBase { } } - public async fillKeyBuffer(keyIndex: KeyIndex, imageBuffer: Buffer, options?: FillImageOptions): Promise { + public async fillKeyBuffer(keyIndex: KeyIndex, imageBuffer: Uint8Array, options?: FillImageOptions): Promise { this.checkValidKeyIndex(keyIndex) const sourceFormat = options?.format ?? 'rgb' @@ -231,7 +235,7 @@ export abstract class StreamDeckBase extends StreamDeckInputBase { }) } - public async fillPanelBuffer(imageBuffer: Buffer, options?: FillPanelOptions): Promise { + public async fillPanelBuffer(imageBuffer: Uint8Array, options?: FillPanelOptions): Promise { const sourceFormat = options?.format ?? 'rgb' this.checkSourceFormat(sourceFormat) @@ -314,9 +318,12 @@ export abstract class StreamDeckBase extends StreamDeckInputBase { await Promise.all(ps) } - protected abstract convertFillImage(imageBuffer: Buffer, sourceOptions: InternalFillImageOptions): Promise + protected abstract convertFillImage( + imageBuffer: Uint8Array, + sourceOptions: InternalFillImageOptions + ): Promise - private async fillImageRange(keyIndex: KeyIndex, imageBuffer: Buffer, sourceOptions: InternalFillImageOptions) { + private async fillImageRange(keyIndex: KeyIndex, imageBuffer: Uint8Array, sourceOptions: InternalFillImageOptions) { this.checkValidKeyIndex(keyIndex) const byteBuffer = await this.convertFillImage(imageBuffer, sourceOptions) diff --git a/packages/core/src/models/mini.ts b/packages/core/src/models/mini.ts index 30c8782..36eb163 100644 --- a/packages/core/src/models/mini.ts +++ b/packages/core/src/models/mini.ts @@ -23,7 +23,10 @@ export class StreamDeckMini extends StreamDeckGen1Base { super(device, options, miniProperties) } - protected async convertFillImage(sourceBuffer: Buffer, sourceOptions: InternalFillImageOptions): Promise { + protected async convertFillImage( + sourceBuffer: Uint8Array, + sourceOptions: InternalFillImageOptions + ): Promise { const byteBuffer = transformImageBuffer( sourceBuffer, sourceOptions, diff --git a/packages/core/src/models/miniv2.ts b/packages/core/src/models/miniv2.ts index 1b9ebff..f9b69d3 100644 --- a/packages/core/src/models/miniv2.ts +++ b/packages/core/src/models/miniv2.ts @@ -23,7 +23,10 @@ export class StreamDeckMiniV2 extends StreamDeckGen1Base { super(device, options, miniV2Properties) } - protected async convertFillImage(sourceBuffer: Buffer, sourceOptions: InternalFillImageOptions): Promise { + protected async convertFillImage( + sourceBuffer: Uint8Array, + sourceOptions: InternalFillImageOptions + ): Promise { const byteBuffer = transformImageBuffer( sourceBuffer, sourceOptions, diff --git a/packages/core/src/models/original.ts b/packages/core/src/models/original.ts index 73f9935..54a49ba 100644 --- a/packages/core/src/models/original.ts +++ b/packages/core/src/models/original.ts @@ -39,7 +39,10 @@ export class StreamDeckOriginal extends StreamDeckGen1Base { } } - protected async convertFillImage(sourceBuffer: Buffer, sourceOptions: InternalFillImageOptions): Promise { + protected async convertFillImage( + sourceBuffer: Uint8Array, + sourceOptions: InternalFillImageOptions + ): Promise { const byteBuffer = transformImageBuffer( sourceBuffer, sourceOptions, diff --git a/packages/core/src/models/pedal.ts b/packages/core/src/models/pedal.ts index 7a6d46b..a0a3f25 100644 --- a/packages/core/src/models/pedal.ts +++ b/packages/core/src/models/pedal.ts @@ -49,10 +49,14 @@ export class StreamDeckPedal extends StreamDeckInputBase { public async fillKeyColor(_keyIndex: number, _r: number, _g: number, _b: number): Promise { // Not supported } - public async fillKeyBuffer(_keyIndex: number, _imageBuffer: Buffer, _options?: FillImageOptions): Promise { + public async fillKeyBuffer( + _keyIndex: number, + _imageBuffer: Uint8Array, + _options?: FillImageOptions + ): Promise { // Not supported } - public async fillPanelBuffer(_imageBuffer: Buffer, _options?: FillPanelOptions): Promise { + public async fillPanelBuffer(_imageBuffer: Uint8Array, _options?: FillPanelOptions): Promise { // Not supported } public async clearKey(_keyIndex: number): Promise { diff --git a/packages/core/src/models/plus.ts b/packages/core/src/models/plus.ts index d01cc33..8dbabaa 100644 --- a/packages/core/src/models/plus.ts +++ b/packages/core/src/models/plus.ts @@ -135,7 +135,7 @@ export class StreamDeckPlus extends StreamDeckGen2Base { public override async fillEncoderLcd( index: EncoderIndex, - buffer: Buffer, + buffer: Uint8Array, sourceOptions: FillImageOptions ): Promise { if (this.NUM_ENCODERS === 0) throw new Error(`There are no encoders`) @@ -153,7 +153,7 @@ export class StreamDeckPlus extends StreamDeckGen2Base { public override async fillLcdRegion( x: number, y: number, - imageBuffer: Buffer, + imageBuffer: Uint8Array, sourceOptions: FillLcdImageOptions ): Promise { // Basic bounds checking @@ -177,7 +177,10 @@ export class StreamDeckPlus extends StreamDeckGen2Base { await this.device.sendReports(packets) } - private async convertFillLcdBuffer(sourceBuffer: Buffer, sourceOptions: FillLcdImageOptions): Promise { + private async convertFillLcdBuffer( + sourceBuffer: Uint8Array, + sourceOptions: FillLcdImageOptions + ): Promise { const sourceOptions2: InternalFillImageOptions = { format: sourceOptions.format, offset: 0, diff --git a/packages/core/src/proxy.ts b/packages/core/src/proxy.ts index 730e104..c68719b 100644 --- a/packages/core/src/proxy.ts +++ b/packages/core/src/proxy.ts @@ -78,10 +78,10 @@ export class StreamDeckProxy implements StreamDeck { public async fillKeyColor(keyIndex: KeyIndex, r: number, g: number, b: number): Promise { return this.device.fillKeyColor(keyIndex, r, g, b) } - public async fillKeyBuffer(keyIndex: KeyIndex, imageBuffer: Buffer, options?: FillImageOptions): Promise { + public async fillKeyBuffer(keyIndex: KeyIndex, imageBuffer: Uint8Array, options?: FillImageOptions): Promise { return this.device.fillKeyBuffer(keyIndex, imageBuffer, options) } - public async fillPanelBuffer(imageBuffer: Buffer, options?: FillPanelOptions): Promise { + public async fillPanelBuffer(imageBuffer: Uint8Array, options?: FillPanelOptions): Promise { return this.device.fillPanelBuffer(imageBuffer, options) } public async clearKey(keyIndex: KeyIndex): Promise { @@ -109,7 +109,7 @@ export class StreamDeckProxy implements StreamDeck { public async fillEncoderLcd( index: EncoderIndex, - imageBuffer: Buffer, + imageBuffer: Uint8Array, sourceOptions: FillImageOptions ): Promise { return this.device.fillEncoderLcd(index, imageBuffer, sourceOptions) @@ -118,7 +118,7 @@ export class StreamDeckProxy implements StreamDeck { public async fillLcdRegion( x: number, y: number, - imageBuffer: Buffer, + imageBuffer: Uint8Array, sourceOptions: FillLcdImageOptions ): Promise { return this.device.fillLcdRegion(x, y, imageBuffer, sourceOptions) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a3a5664..e3c6a7d 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -102,7 +102,7 @@ export interface StreamDeck extends EventEmitter { * @param {Buffer} imageBuffer The image to write * @param {Object} options Options to control the write */ - fillKeyBuffer(keyIndex: KeyIndex, imageBuffer: Buffer, options?: FillImageOptions): Promise + fillKeyBuffer(keyIndex: KeyIndex, imageBuffer: Uint8Array, options?: FillImageOptions): Promise /** * Fills the whole panel with an image in a Buffer. @@ -110,7 +110,7 @@ export interface StreamDeck extends EventEmitter { * @param {Buffer} imageBuffer The image to write * @param {Object} options Options to control the write */ - fillPanelBuffer(imageBuffer: Buffer, options?: FillPanelOptions): Promise + fillPanelBuffer(imageBuffer: Uint8Array, options?: FillPanelOptions): Promise /** * Fill the whole lcd strip @@ -125,7 +125,7 @@ export interface StreamDeck extends EventEmitter { * @param {Buffer} imageBuffer The image to write * @param {Object} sourceOptions Options to control the write */ - fillEncoderLcd(index: EncoderIndex, imageBuffer: Buffer, sourceOptions: FillImageOptions): Promise + fillEncoderLcd(index: EncoderIndex, imageBuffer: Uint8Array, sourceOptions: FillImageOptions): Promise /** * Fill a region of the lcd strip, ignoring the boundaries of the encoders @@ -134,7 +134,7 @@ export interface StreamDeck extends EventEmitter { * @param {Buffer} imageBuffer The image to write * @param {Object} sourceOptions Options to control the write */ - fillLcdRegion(x: number, y: number, imageBuffer: Buffer, sourceOptions: FillLcdImageOptions): Promise + fillLcdRegion(x: number, y: number, imageBuffer: Uint8Array, sourceOptions: FillLcdImageOptions): Promise /** * Clears the given key. diff --git a/packages/core/src/util.ts b/packages/core/src/util.ts index 409bf5d..50665e3 100644 --- a/packages/core/src/util.ts +++ b/packages/core/src/util.ts @@ -8,13 +8,13 @@ export interface FillImageTargetOptions { } export function transformImageBuffer( - imageBuffer: Buffer, + imageBuffer: Uint8Array, sourceOptions: InternalFillImageOptions, targetOptions: FillImageTargetOptions, destPadding: number, imageWidth: number, imageHeight?: number -): Buffer { +): Uint8Array { if (!imageHeight) imageHeight = imageWidth const byteBuffer = Buffer.alloc(destPadding + imageWidth * imageHeight * targetOptions.colorMode.length) @@ -37,9 +37,9 @@ export function transformImageBuffer( const srcOffset = y2 * sourceOptions.stride + sourceOptions.offset + x2 * sourceOptions.format.length - const red = imageBuffer.readUInt8(srcOffset) - const green = imageBuffer.readUInt8(srcOffset + 1) - const blue = imageBuffer.readUInt8(srcOffset + 2) + const red = imageBuffer[srcOffset] + const green = imageBuffer[srcOffset + 1] + const blue = imageBuffer[srcOffset + 2] const targetOffset = rowOffset + x * targetOptions.colorMode.length if (flipColours) { @@ -61,7 +61,7 @@ export function transformImageBuffer( } export const BMP_HEADER_LENGTH = 54 -export function writeBMPHeader(buf: Buffer, iconSize: number, iconBytes: number, imagePPM: number): void { +export function writeBMPHeader(buf: Uint8Array, iconSize: number, iconBytes: number, imagePPM: number): void { // Uses header format BITMAPINFOHEADER https://en.wikipedia.org/wiki/BMP_file_format // Bitmap file header diff --git a/packages/node/examples/text-generation.js b/packages/node/examples/text-generation.js index 3ef298c..ce490dc 100644 --- a/packages/node/examples/text-generation.js +++ b/packages/node/examples/text-generation.js @@ -12,7 +12,7 @@ openStreamDeck().then((streamDeck) => { const finalBuffer = await sharp(path.resolve(__dirname, `fixtures/github_logo_${streamDeck.ICON_SIZE}.jpg`)) .composite([ { - input: Buffer.from( + input: Uint8Array.from( ` diff --git a/packages/node/src/__tests__/helpers.ts b/packages/node/src/__tests__/helpers.ts index cec1c24..3aacd28 100644 --- a/packages/node/src/__tests__/helpers.ts +++ b/packages/node/src/__tests__/helpers.ts @@ -1,7 +1,7 @@ import * as fs from 'fs' import * as path from 'path' -export function readFixtureJSON(fileName: string): Buffer { +export function readFixtureJSON(fileName: string): Uint8Array { const filePath = path.resolve(__dirname, '../../../../fixtures', fileName) const fileData = fs.readFileSync(filePath) return Buffer.from(JSON.parse(fileData.toString()) as Array) diff --git a/packages/node/src/__tests__/jpeg-encoding.spec.ts b/packages/node/src/__tests__/jpeg-encoding.spec.ts index 755d4f4..e0a1669 100644 --- a/packages/node/src/__tests__/jpeg-encoding.spec.ts +++ b/packages/node/src/__tests__/jpeg-encoding.spec.ts @@ -10,7 +10,7 @@ describe('jpeg-encoding', () => { // jest.resetModules() }) - function addAlphaChannel(raw: Buffer): Buffer { + function addAlphaChannel(raw: Uint8Array): Uint8Array { const pixels = raw.length / 3 const res = Buffer.alloc(pixels * 4) @@ -27,7 +27,7 @@ describe('jpeg-encoding', () => { // Mock jpeg-js so we can see if it got used instead of jpeg-turbo jest.doMock('jpeg-js') const jpegJS: typeof import('jpeg-js') = require('jpeg-js') - mocked(jpegJS.encode).mockImplementation((src) => ({ ...src, data: Buffer.alloc(100) })) + mocked(jpegJS.encode).mockImplementation((src) => ({ ...src, data: Uint8Array.alloc(100) })) const { encodeJPEG } = require('../jpeg') as typeof import('../jpeg') diff --git a/packages/node/src/__tests__/jpeg-library.spec.ts b/packages/node/src/__tests__/jpeg-library.spec.ts index dcc5421..1b93e05 100644 --- a/packages/node/src/__tests__/jpeg-library.spec.ts +++ b/packages/node/src/__tests__/jpeg-library.spec.ts @@ -24,7 +24,7 @@ describe('jpeg-library', () => { // Mock jpeg-js so we can see that it got used instead of jpeg-turbo jest.doMock('jpeg-js') const jpegJS: typeof import('jpeg-js') = require('jpeg-js') - mocked(jpegJS.encode).mockImplementation((src) => ({ ...src, data: Buffer.alloc(100) })) + mocked(jpegJS.encode).mockImplementation((src) => ({ ...src, data: Uint8Array.alloc(100) })) const { encodeJPEG } = require('../jpeg') as typeof import('../jpeg') @@ -42,7 +42,7 @@ describe('jpeg-library', () => { // Mock jpeg-js so we can see that it got used instead of jpeg-turbo jest.doMock('jpeg-js') const jpegJS: typeof import('jpeg-js') = require('jpeg-js') - mocked(jpegJS.encode).mockImplementation((src) => ({ ...src, data: Buffer.alloc(100) })) + mocked(jpegJS.encode).mockImplementation((src) => ({ ...src, data: Uint8Array.alloc(100) })) const { encodeJPEG } = require('../jpeg') as typeof import('../jpeg') diff --git a/packages/node/src/device.ts b/packages/node/src/device.ts index e3a2ebe..3aee1fb 100644 --- a/packages/node/src/device.ts +++ b/packages/node/src/device.ts @@ -77,7 +77,7 @@ export class NodeHIDSyncDevice extends EventEmitter implements HIDDevice { this.device = device this.device.on('error', (error) => this.emit('error', error)) - this.device.on('data', (data: Buffer) => { + this.device.on('data', (data: Uint8Array) => { // Button press if (data[0] === 0x01) { const keyData = data.subarray(1) @@ -90,13 +90,13 @@ export class NodeHIDSyncDevice extends EventEmitter implements HIDDevice { this.device.close() } - public async sendFeatureReport(data: Buffer): Promise { + public async sendFeatureReport(data: Uint8Array): Promise { this.device.sendFeatureReport(data) } - public async getFeatureReport(reportId: number, reportLength: number): Promise { + public async getFeatureReport(reportId: number, reportLength: number): Promise { return Buffer.from(this.device.getFeatureReport(reportId, reportLength)) } - public async sendReports(buffers: Buffer[]): Promise { + public async sendReports(buffers: Uint8Array[]): Promise { for (const data of buffers) { this.device.write(data) } diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 8446cc1..c7ed760 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -70,7 +70,7 @@ export async function openStreamDeck(devicePath: string, userOptions?: OpenStrea const options: Required = { useOriginalKeyOrder: false, - encodeJPEG: async (buffer: Buffer, width: number, height: number) => + encodeJPEG: async (buffer: Uint8Array, width: number, height: number) => encodeJPEG(buffer, width, height, jpegOptions), ...userOptions, } diff --git a/packages/node/src/jpeg.ts b/packages/node/src/jpeg.ts index 957af4f..3fe5d3d 100644 --- a/packages/node/src/jpeg.ts +++ b/packages/node/src/jpeg.ts @@ -24,11 +24,11 @@ const DEFAULT_QUALITY = 95 * @param height Hieght of the image */ export async function encodeJPEG( - buffer: Buffer, + buffer: Uint8Array, width: number, height: number, options: JPEGEncodeOptions | undefined -): Promise { +): Promise { try { // Try using jpeg-turbo if it is available if (jpegTurbo && jpegTurbo.bufferSize && !!jpegTurbo.compressSync) { diff --git a/packages/webhid/src/device.ts b/packages/webhid/src/device.ts index 57944ad..7d5b967 100644 --- a/packages/webhid/src/device.ts +++ b/packages/webhid/src/device.ts @@ -37,11 +37,11 @@ export class WebHIDDevice extends EventEmitter implements CoreHIDDevice { public async sendFeatureReport(data: Buffer): Promise { return this.device.sendFeatureReport(data[0], new Uint8Array(data.subarray(1))) } - public async getFeatureReport(reportId: number, _reportLength: number): Promise { + public async getFeatureReport(reportId: number, _reportLength: number): Promise { const view = await this.device.receiveFeatureReport(reportId) return Buffer.from(view.buffer) } - public async sendReports(buffers: Buffer[]): Promise { + public async sendReports(buffers: Uint8Array[]): Promise { return this.reportQueue.add(async () => { for (const data of buffers) { await this.device.sendReport(data[0], new Uint8Array(data.subarray(1))) diff --git a/packages/webhid/src/jpeg.ts b/packages/webhid/src/jpeg.ts index 0deb133..69e4b94 100644 --- a/packages/webhid/src/jpeg.ts +++ b/packages/webhid/src/jpeg.ts @@ -5,7 +5,7 @@ * @param width Width of the image * @param height Hieght of the image */ -export async function encodeJPEG(buffer: Buffer, width: number, height: number): Promise { +export async function encodeJPEG(buffer: Uint8Array, width: number, height: number): Promise { const blob = await new Promise((resolve, reject) => { const canvas = document.createElement('canvas') canvas.width = width From 4165aefde6b3ccc50ccd26d1cd7522a3091548d9 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Sat, 17 Feb 2024 19:26:07 +0000 Subject: [PATCH 2/4] wip --- packages/core/src/models/base-gen1.ts | 12 +++--- packages/core/src/models/base-gen2.ts | 8 ++-- packages/core/src/models/base.ts | 12 ++++-- packages/core/src/models/pedal.ts | 4 +- packages/core/src/util.ts | 59 +++++++++++++++------------ packages/node/src/jpeg.ts | 2 +- 6 files changed, 54 insertions(+), 43 deletions(-) diff --git a/packages/core/src/models/base-gen1.ts b/packages/core/src/models/base-gen1.ts index b11515d..b521008 100644 --- a/packages/core/src/models/base-gen1.ts +++ b/packages/core/src/models/base-gen1.ts @@ -33,7 +33,7 @@ export abstract class StreamDeckGen1Base extends StreamDeckBase { } // prettier-ignore - const brightnessCommandBuffer = Buffer.from([ + const brightnessCommandBuffer = Uint8Array.from([ 0x05, 0x55, 0xaa, 0xd1, 0x01, percentage, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 @@ -43,7 +43,7 @@ export abstract class StreamDeckGen1Base extends StreamDeckBase { public async resetToLogo(): Promise { // prettier-ignore - const resetCommandBuffer = Buffer.from([ + const resetCommandBuffer = Uint8Array.from([ 0x0b, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 @@ -52,7 +52,7 @@ export abstract class StreamDeckGen1Base extends StreamDeckBase { } public async getFirmwareVersion(): Promise { - let val: Buffer + let val: Uint8Array try { val = await this.device.getFeatureReport(4, 32) } catch (e) { @@ -60,17 +60,17 @@ export abstract class StreamDeckGen1Base extends StreamDeckBase { val = await this.device.getFeatureReport(4, 17) } const end = val.indexOf(0, 5) - return val.toString('ascii', 5, end === -1 ? undefined : end) + return new TextDecoder('ascii').decode(val.subarray(5, end === -1 ? undefined : end)) } public async getSerialNumber(): Promise { - let val: Buffer + let val: Uint8Array try { val = await this.device.getFeatureReport(3, 32) } catch (e) { // In case some devices can't handle the different report length val = await this.device.getFeatureReport(3, 17) } - return val.toString('ascii', 5, 17) + return new TextDecoder('ascii').decode(val.subarray(5, 17)) } } diff --git a/packages/core/src/models/base-gen2.ts b/packages/core/src/models/base-gen2.ts index ec19119..04d1a89 100644 --- a/packages/core/src/models/base-gen2.ts +++ b/packages/core/src/models/base-gen2.ts @@ -64,14 +64,14 @@ export abstract class StreamDeckGen2Base extends StreamDeckBase { public async getFirmwareVersion(): Promise { const val = await this.device.getFeatureReport(5, 32) - const end = val.readUInt8(1) + 2 - return val.toString('ascii', 6, end) + const end = val[1] + 2 + return new TextDecoder('ascii').decode(val.subarray(6, end)) } public async getSerialNumber(): Promise { const val = await this.device.getFeatureReport(6, 32) - const end = val.readUInt8(1) + 2 - return val.toString('ascii', 2, end) + const end = val[1] + 2 + return new TextDecoder('ascii').decode(val.subarray(2, end)) } protected async convertFillImage( diff --git a/packages/core/src/models/base.ts b/packages/core/src/models/base.ts index 4229e91..e5faccf 100644 --- a/packages/core/src/models/base.ts +++ b/packages/core/src/models/base.ts @@ -206,7 +206,9 @@ export abstract class StreamDeckBase extends StreamDeckInputBase { if (keyIndex >= this.NUM_KEYS) { await this.device.sendFeatureReport(Buffer.from([0x03, 0x06, keyIndex, r, g, b])) } else { - const pixels = Buffer.alloc(this.ICON_BYTES, Buffer.from([r, g, b])) + const pixels = new Uint8Array(this.ICON_BYTES) + pixels.fill([r, g, b]) + const keyIndex2 = this.transformKeyIndex(keyIndex) await this.fillImageRange(keyIndex2, pixels, { format: 'rgb', @@ -279,7 +281,9 @@ export abstract class StreamDeckBase extends StreamDeckInputBase { if (keyIndex >= this.NUM_KEYS) { await this.device.sendFeatureReport(Buffer.from([0x03, 0x06, keyIndex, 0, 0, 0])) } else { - const pixels = Buffer.alloc(this.ICON_BYTES, 0) + const pixels = new Uint8Array(this.ICON_BYTES) + pixels.fill(0) + const keyIndex2 = this.transformKeyIndex(keyIndex) await this.fillImageRange(keyIndex2, pixels, { format: 'rgb', @@ -290,7 +294,9 @@ export abstract class StreamDeckBase extends StreamDeckInputBase { } public async clearPanel(): Promise { - const pixels = Buffer.alloc(this.ICON_BYTES, 0) + const pixels = new Uint8Array(this.ICON_BYTES) + pixels.fill(0) + const ps: Array> = [] for (let keyIndex = 0; keyIndex < this.NUM_KEYS; keyIndex++) { ps.push( diff --git a/packages/core/src/models/pedal.ts b/packages/core/src/models/pedal.ts index a0a3f25..9e4d782 100644 --- a/packages/core/src/models/pedal.ts +++ b/packages/core/src/models/pedal.ts @@ -38,12 +38,12 @@ export class StreamDeckPedal extends StreamDeckInputBase { public async getFirmwareVersion(): Promise { const val = await this.device.getFeatureReport(5, 32) const end = val.indexOf(0, 6) - return val.toString('ascii', 6, end === -1 ? undefined : end) + return new TextDecoder('ascii').decode(val.subarray(6, end === -1 ? undefined : end)) } public async getSerialNumber(): Promise { const val = await this.device.getFeatureReport(6, 32) - return val.toString('ascii', 2, 14) + return new TextDecoder('ascii').decode(val.subarray(2, 14)) } public async fillKeyColor(_keyIndex: number, _r: number, _g: number, _b: number): Promise { diff --git a/packages/core/src/util.ts b/packages/core/src/util.ts index 50665e3..086a41f 100644 --- a/packages/core/src/util.ts +++ b/packages/core/src/util.ts @@ -17,7 +17,10 @@ export function transformImageBuffer( ): Uint8Array { if (!imageHeight) imageHeight = imageWidth - const byteBuffer = Buffer.alloc(destPadding + imageWidth * imageHeight * targetOptions.colorMode.length) + const imageBufferView = new DataView(imageBuffer) + + const byteBuffer = new Uint8Array(destPadding + imageWidth * imageHeight * targetOptions.colorMode.length) + const byteBufferView = new DataView(byteBuffer) const flipColours = sourceOptions.format.substring(0, 3) !== targetOptions.colorMode.substring(0, 3) @@ -37,22 +40,22 @@ export function transformImageBuffer( const srcOffset = y2 * sourceOptions.stride + sourceOptions.offset + x2 * sourceOptions.format.length - const red = imageBuffer[srcOffset] - const green = imageBuffer[srcOffset + 1] - const blue = imageBuffer[srcOffset + 2] + const red = imageBufferView.getUint8(srcOffset) + const green = imageBufferView.getUint8(srcOffset + 1) + const blue = imageBufferView.getUint8(srcOffset + 2) const targetOffset = rowOffset + x * targetOptions.colorMode.length if (flipColours) { - byteBuffer.writeUInt8(blue, targetOffset) - byteBuffer.writeUInt8(green, targetOffset + 1) - byteBuffer.writeUInt8(red, targetOffset + 2) + byteBufferView.setUint8(targetOffset, blue) + byteBufferView.setUint8(targetOffset + 1, green) + byteBufferView.setUint8(targetOffset + 2, red) } else { - byteBuffer.writeUInt8(red, targetOffset) - byteBuffer.writeUInt8(green, targetOffset + 1) - byteBuffer.writeUInt8(blue, targetOffset + 2) + byteBufferView.setUint8(targetOffset, red) + byteBufferView.setUint8(targetOffset + 1, green) + byteBufferView.setUint8(targetOffset + 2, blue) } if (targetOptions.colorMode.length === 4) { - byteBuffer.writeUInt8(255, targetOffset + 3) + byteBufferView.setUint8(targetOffset + 3, 255) } } } @@ -62,25 +65,27 @@ export function transformImageBuffer( export const BMP_HEADER_LENGTH = 54 export function writeBMPHeader(buf: Uint8Array, iconSize: number, iconBytes: number, imagePPM: number): void { + const bufView = new DataView(buf) // Uses header format BITMAPINFOHEADER https://en.wikipedia.org/wiki/BMP_file_format // Bitmap file header - buf.write('BM') - buf.writeUInt32LE(iconBytes + 54, 2) - buf.writeInt16LE(0, 6) - buf.writeInt16LE(0, 8) - buf.writeUInt32LE(54, 10) // Full header size + bufView.setUint8(0, 0x42) // B + bufView.setUint8(1, 0x4d) // M + bufView.setUint32(2, iconBytes + 54, true) + bufView.setInt16(6, 0, true) + bufView.setInt16(8, 0, true) + bufView.setUint32(10, 54, true) // Full header size // DIB header (BITMAPINFOHEADER) - buf.writeUInt32LE(40, 14) // DIB header size - buf.writeInt32LE(iconSize, 18) - buf.writeInt32LE(iconSize, 22) - buf.writeInt16LE(1, 26) // Color planes - buf.writeInt16LE(24, 28) // Bit depth - buf.writeInt32LE(0, 30) // Compression - buf.writeInt32LE(iconBytes, 34) // Image size - buf.writeInt32LE(imagePPM, 38) // Horizontal resolution ppm - buf.writeInt32LE(imagePPM, 42) // Vertical resolution ppm - buf.writeInt32LE(0, 46) // Colour pallette size - buf.writeInt32LE(0, 50) // 'Important' Colour count + bufView.setUint32(14, 40, true) // DIB header size + bufView.setInt32(18, iconSize, true) + bufView.setInt32(22, iconSize, true) + bufView.setInt16(26, 1, true) // Color planes + bufView.setInt16(28, 24, true) // Bit depth + bufView.setInt32(30, 0, true) // Compression + bufView.setInt32(34, iconBytes, true) // Image size + bufView.setInt32(38, imagePPM, true) // Horizontal resolution ppm + bufView.setInt32(42, imagePPM, true) // Vertical resolution ppm + bufView.setInt32(46, 0, true) // Colour pallette size + bufView.setInt32(50, 0, true) // 'Important' Colour count } diff --git a/packages/node/src/jpeg.ts b/packages/node/src/jpeg.ts index 3fe5d3d..d3af028 100644 --- a/packages/node/src/jpeg.ts +++ b/packages/node/src/jpeg.ts @@ -41,7 +41,7 @@ export async function encodeJPEG( } if (buffer.length === width * height * 4) { const tmpBuffer = Buffer.alloc(jpegTurbo.bufferSize(encodeOptions)) - return jpegTurbo.compress(buffer, tmpBuffer, encodeOptions) + return jpegTurbo.compress(Buffer.from(buffer.buffer), tmpBuffer, encodeOptions) } } } catch (e) { From 282427cfecea793c8087e6f6f548f741fc3c8b6b Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 11 Jul 2024 19:45:50 +0100 Subject: [PATCH 3/4] wip --- .../core/src/imageWriter/headerGenerator.ts | 70 +++++++++++-------- packages/core/src/imageWriter/imageWriter.ts | 12 ++-- packages/core/src/models/base.ts | 2 +- packages/core/src/models/neo.ts | 6 +- packages/core/src/models/plus.ts | 2 +- packages/core/src/proxy.ts | 2 +- packages/core/src/types.ts | 2 +- packages/core/src/util.ts | 11 ++- packages/node/src/device.ts | 4 +- packages/webhid-demo/src/demo/chase.ts | 2 +- packages/webhid/src/device.ts | 6 +- 11 files changed, 66 insertions(+), 53 deletions(-) diff --git a/packages/core/src/imageWriter/headerGenerator.ts b/packages/core/src/imageWriter/headerGenerator.ts index d795f31..d7b2854 100644 --- a/packages/core/src/imageWriter/headerGenerator.ts +++ b/packages/core/src/imageWriter/headerGenerator.ts @@ -1,3 +1,4 @@ +import { uint8ArrayToDataView } from '../util' import type { StreamdeckImageHeaderGenerator, StreamdeckImageWriterProps } from './types' export class StreamdeckGen1ImageHeaderGenerator implements StreamdeckImageHeaderGenerator { @@ -6,18 +7,19 @@ export class StreamdeckGen1ImageHeaderGenerator implements StreamdeckImageHeader } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, props: StreamdeckImageWriterProps, partIndex: number, isLast: boolean, _bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x01, 1) - buffer.writeUInt16LE(partIndex, 2) - // 3 = 0x00 - buffer.writeUInt8(isLast ? 1 : 0, 4) - buffer.writeUInt8(props.keyIndex + 1, 5) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(2, 0x01) + bufferView.setUint16(2, partIndex, true) + bufferView.setUint8(4, isLast ? 1 : 0) + bufferView.setUint8(5, props.keyIndex + 1) } } @@ -27,18 +29,20 @@ export class StreamdeckGen2ImageHeaderGenerator implements StreamdeckImageHeader } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, props: StreamdeckImageWriterProps, partIndex: number, isLast: boolean, bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x07, 1) - buffer.writeUInt8(props.keyIndex, 2) - buffer.writeUInt8(isLast ? 1 : 0, 3) - buffer.writeUInt16LE(bodyLength, 4) - buffer.writeUInt16LE(partIndex++, 6) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(1, 0x07) + bufferView.setUint8(2, props.keyIndex) + bufferView.setUint8(3, isLast ? 1 : 0) + bufferView.setUint16(4, bodyLength, true) + bufferView.setUint16(4, partIndex++, true) } } @@ -56,21 +60,23 @@ export class StreamdeckPlusLcdImageHeaderGenerator } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, props: StreamdeckPlusHeaderProps, partIndex: number, isLast: boolean, bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x0c, 1) - buffer.writeUInt16LE(props.x, 2) - buffer.writeUInt16LE(props.y, 4) - buffer.writeUInt16LE(props.width, 6) - buffer.writeUInt16LE(props.height, 8) - buffer.writeUInt8(isLast ? 1 : 0, 10) // Is last - buffer.writeUInt16LE(partIndex, 11) - buffer.writeUInt16LE(bodyLength, 13) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(1, 0x0c) + bufferView.setUint16(2, props.x, true) + bufferView.setUint16(4, props.y, true) + bufferView.setUint16(6, props.width, true) + bufferView.setUint16(8, props.height, true) + bufferView.setUint8(10, isLast ? 1 : 0) + bufferView.setUint16(11, partIndex, true) + bufferView.setUint16(13, bodyLength, true) } } @@ -80,17 +86,19 @@ export class StreamdeckNeoLcdImageHeaderGenerator implements StreamdeckImageHead } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, _props: never, partIndex: number, isLast: boolean, bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x0b, 1) - buffer.writeUInt8(0, 2) - buffer.writeUInt8(isLast ? 1 : 0, 3) - buffer.writeUInt16LE(bodyLength, 4) - buffer.writeUInt16LE(partIndex++, 6) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(1, 0x0b) + bufferView.setUint8(2, 0) + bufferView.setUint8(3, isLast ? 1 : 0) + bufferView.setUint16(4, bodyLength, true) + bufferView.setUint16(6, partIndex, true) } } diff --git a/packages/core/src/imageWriter/imageWriter.ts b/packages/core/src/imageWriter/imageWriter.ts index 1225654..bfe934d 100644 --- a/packages/core/src/imageWriter/imageWriter.ts +++ b/packages/core/src/imageWriter/imageWriter.ts @@ -12,13 +12,13 @@ export class StreamdeckOriginalImageWriter implements StreamdeckImageWriter { const packet1Bytes = byteBuffer.length / 2 - const packet1 = Buffer.alloc(MAX_PACKET_SIZE) + const packet1 = new Uint8Array(MAX_PACKET_SIZE) this.headerGenerator.writeFillImageCommandHeader(packet1, props, 0x01, false, packet1Bytes) - byteBuffer.copy(packet1, PACKET_HEADER_LENGTH, 0, packet1Bytes) + packet1.set(byteBuffer.subarray(0, packet1Bytes), PACKET_HEADER_LENGTH) - const packet2 = Buffer.alloc(MAX_PACKET_SIZE) + const packet2 = new Uint8Array(MAX_PACKET_SIZE) this.headerGenerator.writeFillImageCommandHeader(packet2, props, 0x02, true, packet1Bytes) - byteBuffer.copy(packet2, PACKET_HEADER_LENGTH, packet1Bytes) + packet2.set(byteBuffer.subarray(packet1Bytes), PACKET_HEADER_LENGTH) return [packet1, packet2] } @@ -42,7 +42,7 @@ export class StreamdeckDefaultImageWriter let remainingBytes = byteBuffer.length for (let part = 0; remainingBytes > 0; part++) { - const packet = Buffer.alloc(MAX_PACKET_SIZE) + const packet = new Uint8Array(MAX_PACKET_SIZE) const byteCount = Math.min(remainingBytes, MAX_PAYLOAD_SIZE) this.headerGenerator.writeFillImageCommandHeader( @@ -55,7 +55,7 @@ export class StreamdeckDefaultImageWriter const byteOffset = byteBuffer.length - remainingBytes remainingBytes -= byteCount - byteBuffer.copy(packet, PACKET_HEADER_LENGTH, byteOffset, byteOffset + byteCount) + packet.set(byteBuffer.subarray(byteOffset, byteOffset + byteCount), PACKET_HEADER_LENGTH) result.push(packet) } diff --git a/packages/core/src/models/base.ts b/packages/core/src/models/base.ts index e5faccf..9cf9047 100644 --- a/packages/core/src/models/base.ts +++ b/packages/core/src/models/base.ts @@ -160,7 +160,7 @@ export abstract class StreamDeckInputBase extends EventEmitter ): Promise public abstract fillPanelBuffer(imageBuffer: Uint8Array, options?: FillPanelOptions): Promise - public async fillLcd(_imageBuffer: Buffer, _sourceOptions: FillImageOptions): Promise { + public async fillLcd(_imageBuffer: Uint8Array, _sourceOptions: FillImageOptions): Promise { throw new Error('Not supported for this model') } public async fillEncoderLcd( diff --git a/packages/core/src/models/neo.ts b/packages/core/src/models/neo.ts index 41f9c75..761cdfb 100644 --- a/packages/core/src/models/neo.ts +++ b/packages/core/src/models/neo.ts @@ -34,7 +34,7 @@ export class StreamDeckNeo extends StreamDeckGen2Base { } } - public override async fillLcd(imageBuffer: Buffer, sourceOptions: FillImageOptions): Promise { + public override async fillLcd(imageBuffer: Uint8Array, sourceOptions: FillImageOptions): Promise { const size = this.LCD_STRIP_SIZE if (!size) throw new Error(`There is no lcd to fill`) @@ -51,10 +51,10 @@ export class StreamDeckNeo extends StreamDeckGen2Base { } private async convertFillLcdBuffer( - sourceBuffer: Buffer, + sourceBuffer: Uint8Array, size: LcdSegmentSize, sourceOptions: FillImageOptions - ): Promise { + ): Promise { const sourceOptions2: InternalFillImageOptions = { format: sourceOptions.format, offset: 0, diff --git a/packages/core/src/models/plus.ts b/packages/core/src/models/plus.ts index 8dbabaa..9e15c6a 100644 --- a/packages/core/src/models/plus.ts +++ b/packages/core/src/models/plus.ts @@ -122,7 +122,7 @@ export class StreamDeckPlus extends StreamDeckGen2Base { } } - public override async fillLcd(buffer: Buffer, sourceOptions: FillImageOptions): Promise { + public override async fillLcd(buffer: Uint8Array, sourceOptions: FillImageOptions): Promise { const size = this.LCD_STRIP_SIZE if (!size) throw new Error(`There is no lcd to fill`) diff --git a/packages/core/src/proxy.ts b/packages/core/src/proxy.ts index c68719b..f04b3f8 100644 --- a/packages/core/src/proxy.ts +++ b/packages/core/src/proxy.ts @@ -103,7 +103,7 @@ export class StreamDeckProxy implements StreamDeck { return this.device.getSerialNumber() } - public async fillLcd(imageBuffer: Buffer, sourceOptions: FillImageOptions): Promise { + public async fillLcd(imageBuffer: Uint8Array, sourceOptions: FillImageOptions): Promise { return this.device.fillLcd(imageBuffer, sourceOptions) } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e3c6a7d..31b9949 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -117,7 +117,7 @@ export interface StreamDeck extends EventEmitter { * @param {Buffer} imageBuffer The image to write * @param {Object} sourceOptions Options to control the write */ - fillLcd(imageBuffer: Buffer, sourceOptions: FillImageOptions): Promise + fillLcd(imageBuffer: Uint8Array, sourceOptions: FillImageOptions): Promise /** * Fills the lcd strip above an encoder diff --git a/packages/core/src/util.ts b/packages/core/src/util.ts index 086a41f..d4dad33 100644 --- a/packages/core/src/util.ts +++ b/packages/core/src/util.ts @@ -1,3 +1,4 @@ +import { uint8ArrayToDataView } from './util' import { InternalFillImageOptions } from './models/base' export interface FillImageTargetOptions { @@ -17,10 +18,10 @@ export function transformImageBuffer( ): Uint8Array { if (!imageHeight) imageHeight = imageWidth - const imageBufferView = new DataView(imageBuffer) + const imageBufferView = uint8ArrayToDataView(imageBuffer) const byteBuffer = new Uint8Array(destPadding + imageWidth * imageHeight * targetOptions.colorMode.length) - const byteBufferView = new DataView(byteBuffer) + const byteBufferView = uint8ArrayToDataView(byteBuffer) const flipColours = sourceOptions.format.substring(0, 3) !== targetOptions.colorMode.substring(0, 3) @@ -65,7 +66,7 @@ export function transformImageBuffer( export const BMP_HEADER_LENGTH = 54 export function writeBMPHeader(buf: Uint8Array, iconSize: number, iconBytes: number, imagePPM: number): void { - const bufView = new DataView(buf) + const bufView = uint8ArrayToDataView(buf) // Uses header format BITMAPINFOHEADER https://en.wikipedia.org/wiki/BMP_file_format // Bitmap file header @@ -89,3 +90,7 @@ export function writeBMPHeader(buf: Uint8Array, iconSize: number, iconBytes: num bufView.setInt32(46, 0, true) // Colour pallette size bufView.setInt32(50, 0, true) // 'Important' Colour count } + +export function uint8ArrayToDataView(buffer: Uint8Array): DataView { + return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) +} diff --git a/packages/node/src/device.ts b/packages/node/src/device.ts index 3aee1fb..7e47bc4 100644 --- a/packages/node/src/device.ts +++ b/packages/node/src/device.ts @@ -91,14 +91,14 @@ export class NodeHIDSyncDevice extends EventEmitter implements HIDDevice { } public async sendFeatureReport(data: Uint8Array): Promise { - this.device.sendFeatureReport(data) + this.device.sendFeatureReport(Buffer.from(data)) } public async getFeatureReport(reportId: number, reportLength: number): Promise { return Buffer.from(this.device.getFeatureReport(reportId, reportLength)) } public async sendReports(buffers: Uint8Array[]): Promise { for (const data of buffers) { - this.device.write(data) + this.device.write(Buffer.from(data)) } } diff --git a/packages/webhid-demo/src/demo/chase.ts b/packages/webhid-demo/src/demo/chase.ts index 760e3af..f0d6379 100644 --- a/packages/webhid-demo/src/demo/chase.ts +++ b/packages/webhid-demo/src/demo/chase.ts @@ -28,7 +28,7 @@ export class ChaseDemo implements Demo { ctx.fillText(n.toString(), 8, canvas.height * 0.9, canvas.width * 0.8) const id = ctx.getImageData(0, 0, canvas.width, canvas.height) - ps.push(device.fillKeyBuffer(i, Buffer.from(id.data), { format: 'rgba' })) + ps.push(device.fillKeyBuffer(i, id.data, { format: 'rgba' })) ctx.restore() } } diff --git a/packages/webhid/src/device.ts b/packages/webhid/src/device.ts index 7d5b967..f0ec728 100644 --- a/packages/webhid/src/device.ts +++ b/packages/webhid/src/device.ts @@ -34,8 +34,8 @@ export class WebHIDDevice extends EventEmitter implements CoreHIDDevice { return this.device.forget() } - public async sendFeatureReport(data: Buffer): Promise { - return this.device.sendFeatureReport(data[0], new Uint8Array(data.subarray(1))) + public async sendFeatureReport(data: Uint8Array): Promise { + return this.device.sendFeatureReport(data[0], data.subarray(1)) } public async getFeatureReport(reportId: number, _reportLength: number): Promise { const view = await this.device.receiveFeatureReport(reportId) @@ -44,7 +44,7 @@ export class WebHIDDevice extends EventEmitter implements CoreHIDDevice { public async sendReports(buffers: Uint8Array[]): Promise { return this.reportQueue.add(async () => { for (const data of buffers) { - await this.device.sendReport(data[0], new Uint8Array(data.subarray(1))) + await this.device.sendReport(data[0], data.subarray(1)) } }) } From 486c14c3b013c71afebcf05a9ceea7235e03a2ec Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 11 Jul 2024 20:17:35 +0100 Subject: [PATCH 4/4] wip: remove buffer polyfill --- packages/core/src/util.ts | 1 - packages/webhid-demo/package.json | 1 - packages/webhid-demo/webpack.config.js | 5 +---- packages/webhid/README.md | 10 +--------- yarn.lock | 11 ----------- 5 files changed, 2 insertions(+), 26 deletions(-) diff --git a/packages/core/src/util.ts b/packages/core/src/util.ts index d4dad33..84631f0 100644 --- a/packages/core/src/util.ts +++ b/packages/core/src/util.ts @@ -1,4 +1,3 @@ -import { uint8ArrayToDataView } from './util' import { InternalFillImageOptions } from './models/base' export interface FillImageTargetOptions { diff --git a/packages/webhid-demo/package.json b/packages/webhid-demo/package.json index 47536eb..6e1c907 100644 --- a/packages/webhid-demo/package.json +++ b/packages/webhid-demo/package.json @@ -21,7 +21,6 @@ }, "dependencies": { "@elgato-stream-deck/webhid": "6.2.2", - "buffer": "^6.0.3", "html-to-image": "^1.11.11" }, "devDependencies": { diff --git a/packages/webhid-demo/webpack.config.js b/packages/webhid-demo/webpack.config.js index d7a0d10..2ce5911 100644 --- a/packages/webhid-demo/webpack.config.js +++ b/packages/webhid-demo/webpack.config.js @@ -2,7 +2,7 @@ /* eslint-disable node/no-extraneous-require */ const path = require('path') const CopyWebpackPlugin = require('copy-webpack-plugin') -const { ProvidePlugin, DefinePlugin } = require('webpack') +const { DefinePlugin } = require('webpack') module.exports = { // Where to fine the source code @@ -62,9 +62,6 @@ module.exports = { new CopyWebpackPlugin({ patterns: [{ from: path.join(__dirname, '/public'), to: path.join(__dirname, '/dist') }], }), - new ProvidePlugin({ - Buffer: ['buffer', 'Buffer'], - }), new DefinePlugin({ LIB_VERSION: JSON.stringify(process.env.npm_package_version || ''), }), diff --git a/packages/webhid/README.md b/packages/webhid/README.md index b5bccce..d6e0154 100644 --- a/packages/webhid/README.md +++ b/packages/webhid/README.md @@ -19,15 +19,7 @@ This library has nothing to do with the streamdeck software produced by Elgato. ### Important -You need to provide a Buffer polyfill to the browser for this library. We recommend using [buffer](https://www.npmjs.com/package/buffer) which can be added to your webpack config with: - -``` -plugins: [ - new ProvidePlugin({ - Buffer: ['buffer', 'Buffer'], - }), -], -``` +Since v6.3.0, the `buffer` polyfill has no longer been necessary. ## Linux diff --git a/yarn.lock b/yarn.lock index e00425f..a8250e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -476,7 +476,6 @@ __metadata: resolution: "@elgato-stream-deck/webhid-demo@workspace:packages/webhid-demo" dependencies: "@elgato-stream-deck/webhid": "npm:6.2.2" - buffer: "npm:^6.0.3" copy-webpack-plugin: "npm:^11.0.0" cross-env: "npm:^7.0.3" html-to-image: "npm:^1.11.11" @@ -3872,16 +3871,6 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^6.0.3": - version: 6.0.3 - resolution: "buffer@npm:6.0.3" - dependencies: - base64-js: "npm:^1.3.1" - ieee754: "npm:^1.2.1" - checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0 - languageName: node - linkType: hard - "byte-size@npm:8.1.1": version: 8.1.1 resolution: "byte-size@npm:8.1.1"