From 408fd28d550198decf211a2d9dc1b8e7d251e275 Mon Sep 17 00:00:00 2001 From: duckietm Date: Mon, 2 Mar 2026 13:26:53 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=99=20Camera=20Filters=20are=20now=201?= =?UTF-8?q?00%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camera/src/RoomCameraWidgetManager.ts | 36 +++++++++---- .../__tests__/RoomCameraWidgetManager.test.ts | 52 +++++++++++++++++++ 2 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 packages/camera/src/__tests__/RoomCameraWidgetManager.test.ts diff --git a/packages/camera/src/RoomCameraWidgetManager.ts b/packages/camera/src/RoomCameraWidgetManager.ts index 15a14dd..98572d7 100644 --- a/packages/camera/src/RoomCameraWidgetManager.ts +++ b/packages/camera/src/RoomCameraWidgetManager.ts @@ -6,6 +6,31 @@ import { TextureUtils } from '@nitrots/utils'; import { BLEND_MODES, ColorMatrix, ColorMatrixFilter, Container, Filter, Sprite, Texture } from 'pixi.js'; import { RoomCameraWidgetEffect } from './RoomCameraWidgetEffect'; +const COLOR_MATRIX_OFFSET_INDICES = [4, 9, 14, 19] as const; + +export const normalizeCameraColorMatrix = (matrix: ColorMatrix): ColorMatrix => +{ + const normalized = [ ...matrix ] as ColorMatrix; + + for(const index of COLOR_MATRIX_OFFSET_INDICES) + { + if(Math.abs(normalized[index]) > 1) normalized[index] /= 255; + } + + for(const [ rowStart, offsetIndex ] of [[0, 4], [5, 9], [10, 14]] as const) + { + const rowHasOnlyNegativeWeights = + (normalized[rowStart] <= 0) && + (normalized[rowStart + 1] <= 0) && + (normalized[rowStart + 2] <= 0) && + ((normalized[rowStart] !== 0) || (normalized[rowStart + 1] !== 0) || (normalized[rowStart + 2] !== 0)); + + if((normalized[offsetIndex] === 0) && rowHasOnlyNegativeWeights) normalized[offsetIndex] = 1; + } + + return normalized; +}; + export class RoomCameraWidgetManager implements IRoomCameraWidgetManager { private _effects: Map = new Map(); @@ -26,16 +51,9 @@ export class RoomCameraWidgetManager implements IRoomCameraWidgetManager const cameraEffect = new RoomCameraWidgetEffect(effect.name, effect.minLevel); - if(effect.colorMatrix.length) + if(effect.colorMatrix?.length) { - // Config offsets (indices 4,9,14,19) follow Flash's 0-255 convention. - // PixiJS v8 expects the full matrix in 0-1 space, so normalise them. - const m = [ ...effect.colorMatrix ] as ColorMatrix; - m[4] /= 255; - m[9] /= 255; - m[14] /= 255; - m[19] /= 255; - cameraEffect.colorMatrix = m; + cameraEffect.colorMatrix = normalizeCameraColorMatrix(effect.colorMatrix); } else { diff --git a/packages/camera/src/__tests__/RoomCameraWidgetManager.test.ts b/packages/camera/src/__tests__/RoomCameraWidgetManager.test.ts new file mode 100644 index 0000000..a8866da --- /dev/null +++ b/packages/camera/src/__tests__/RoomCameraWidgetManager.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest'; +import { ColorMatrix } from 'pixi.js'; +import { normalizeCameraColorMatrix } from '../RoomCameraWidgetManager'; + +describe('normalizeCameraColorMatrix', () => +{ + it('keeps explicit negative grayscale bias untouched', () => + { + const matrix: ColorMatrix = [ + -0.5, -0.5, -0.5, 0, 1, + -0.5, -0.5, -0.5, 0, 1, + -0.5, -0.5, -0.5, 0, 1, + 0, 0, 0, 1, 0 + ]; + + expect(normalizeCameraColorMatrix(matrix)).toEqual(matrix); + }); + + it('adds missing white bias for negative grayscale matrices', () => + { + const matrix: ColorMatrix = [ + -0.5, -0.5, -0.5, 0, 0, + -0.5, -0.5, -0.5, 0, 0, + -0.5, -0.5, -0.5, 0, 0, + 0, 0, 0, 1, 0 + ]; + + expect(normalizeCameraColorMatrix(matrix)).toEqual([ + -0.5, -0.5, -0.5, 0, 1, + -0.5, -0.5, -0.5, 0, 1, + -0.5, -0.5, -0.5, 0, 1, + 0, 0, 0, 1, 0 + ]); + }); + + it('normalizes legacy 255-based offsets to pixi range', () => + { + const matrix: ColorMatrix = [ + 1, 0, 0, 0, 255, + 0, 1, 0, 0, 128, + 0, 0, 1, 0, 64, + 0, 0, 0, 1, 255 + ]; + + expect(normalizeCameraColorMatrix(matrix)).toEqual([ + 1, 0, 0, 0, 1, + 0, 1, 0, 0, 128 / 255, + 0, 0, 1, 0, 64 / 255, + 0, 0, 0, 1, 1 + ]); + }); +});