From d9cc6c12dfa7df86b1f676496d80e7a928fe1224 Mon Sep 17 00:00:00 2001 From: SuperSodaSea Date: Sat, 3 Dec 2022 00:25:02 +0800 Subject: [PATCH 1/2] Optimize trimCanvas --- packages/utils/src/media/trimCanvas.ts | 105 +++++++++--------------- packages/utils/test/trimCanvas.tests.ts | 16 +++- 2 files changed, 52 insertions(+), 69 deletions(-) diff --git a/packages/utils/src/media/trimCanvas.ts b/packages/utils/src/media/trimCanvas.ts index 4bf1a4779a..74b1a13f83 100644 --- a/packages/utils/src/media/trimCanvas.ts +++ b/packages/utils/src/media/trimCanvas.ts @@ -1,11 +1,25 @@ import type { ICanvas } from '@pixi/settings'; -interface Inset +function checkRow(data: Uint8ClampedArray, width: number, y: number) { - top?: number; - left?: number; - right?: number; - bottom?: number; + for (let x = 0, index = 4 * y * width; x < width; ++x, index += 4) + { + if (data[index + 3] !== 0) return false; + } + + return true; +} + +function checkColumn(data: Uint8ClampedArray, width: number, x: number, top: number, bottom: number) +{ + const stride = 4 * width; + + for (let y = top, index = (top * stride) + (4 * x); y <= bottom; ++y, index += stride) + { + if (data[index + 3] !== 0) return false; + } + + return true; } /** @@ -15,82 +29,37 @@ interface Inset * @param {PIXI.ICanvas} canvas - the canvas to trim * @returns {object} Trim data */ -export function trimCanvas(canvas: ICanvas): {width: number; height: number; data?: ImageData} +export function trimCanvas(canvas: ICanvas): { width: number; height: number; data?: ImageData } { - // https://gist.github.com/remy/784508 + // https://gist.github.com/timdown/021d9c8f2aabc7092df564996f5afbbf - let width = canvas.width; - let height = canvas.height; + let { width, height } = canvas; const context = canvas.getContext('2d', { willReadFrequently: true, }); const imageData = context.getImageData(0, 0, width, height); - const pixels = imageData.data; - const len = pixels.length; + const data = imageData.data; - const bound: Inset = { - top: null, - left: null, - right: null, - bottom: null, - }; - let data = null; - let i; - let x; - let y; + let top = 0; + let bottom = height - 1; + let left = 0; + let right = width - 1; - for (i = 0; i < len; i += 4) + while (top < height && checkRow(data, width, top)) ++top; + if (top === height) { - if (pixels[i + 3] !== 0) - { - x = (i / 4) % width; - y = ~~((i / 4) / width); - - if (bound.top === null) - { - bound.top = y; - } - - if (bound.left === null) - { - bound.left = x; - } - else if (x < bound.left) - { - bound.left = x; - } - - if (bound.right === null) - { - bound.right = x + 1; - } - else if (bound.right < x) - { - bound.right = x + 1; - } - - if (bound.bottom === null) - { - bound.bottom = y; - } - else if (bound.bottom < y) - { - bound.bottom = y; - } - } + return { width: 0, height: 0, data: null }; } + while (checkRow(data, width, bottom)) --bottom; + while (checkColumn(data, width, left, top, bottom)) ++left; + while (checkColumn(data, width, right, top, bottom)) --right; - if (bound.top !== null) - { - width = bound.right - bound.left; - height = bound.bottom - bound.top + 1; - data = context.getImageData(bound.left, bound.top, width, height); - } + width = right - left + 1; + height = bottom - top + 1; return { - height, - width, - data, + width, height, + data: context.getImageData(left, top, width, height), }; } diff --git a/packages/utils/test/trimCanvas.tests.ts b/packages/utils/test/trimCanvas.tests.ts index 9b6171fab9..3be6464ad1 100644 --- a/packages/utils/test/trimCanvas.tests.ts +++ b/packages/utils/test/trimCanvas.tests.ts @@ -2,6 +2,20 @@ import { trimCanvas } from '@pixi/utils'; describe('trimCanvas', () => { + it('should trim empty canvas', () => + { + const canvas = document.createElement('canvas'); + + canvas.width = 100; + canvas.height = 50; + + const trimmedImageData = trimCanvas(canvas); + + expect(trimmedImageData.width).toEqual(0); + expect(trimmedImageData.height).toEqual(0); + expect(trimmedImageData.data).toBe(null); + }); + it('should trim the canvas', () => { const canvas = document.createElement('canvas'); @@ -15,7 +29,7 @@ describe('trimCanvas', () => const trimmedImageData = trimCanvas(canvas); - expect(trimmedImageData.width).toEqual(9); + expect(trimmedImageData.width).toEqual(10); expect(trimmedImageData.height).toEqual(5); expect(trimmedImageData.data).toBeInstanceOf(ImageData); }); From ec4031311276cb0aa7c0ccc0519cc2be7111057c Mon Sep 17 00:00:00 2001 From: SuperSodaSea Date: Sun, 4 Dec 2022 17:31:53 +0800 Subject: [PATCH 2/2] Improve test --- packages/utils/test/trimCanvas.tests.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/utils/test/trimCanvas.tests.ts b/packages/utils/test/trimCanvas.tests.ts index 3be6464ad1..c7d053d81d 100644 --- a/packages/utils/test/trimCanvas.tests.ts +++ b/packages/utils/test/trimCanvas.tests.ts @@ -26,11 +26,16 @@ describe('trimCanvas', () => context.fillStyle = '#ff0000'; context.fillRect(10, 20, 10, 5); + context.fillStyle = '#00ff00'; + context.fillRect(15, 25, 10, 5); const trimmedImageData = trimCanvas(canvas); - expect(trimmedImageData.width).toEqual(10); - expect(trimmedImageData.height).toEqual(5); + expect(trimmedImageData.width).toEqual(15); + expect(trimmedImageData.height).toEqual(10); expect(trimmedImageData.data).toBeInstanceOf(ImageData); + expect(trimmedImageData.data.width).toEqual(15); + expect(trimmedImageData.data.height).toEqual(10); + expect(trimmedImageData.data.data).toEqual(context.getImageData(10, 20, 15, 10).data); }); });