Skip to content

Commit

Permalink
Optimize trimCanvas (#8920)
Browse files Browse the repository at this point in the history
  • Loading branch information
SuperSodaSea authored and bigtimebuddy committed Dec 12, 2022
1 parent d956513 commit 82e8f9a
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 70 deletions.
105 changes: 37 additions & 68 deletions 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;
}

/**
Expand All @@ -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),
};
}
23 changes: 21 additions & 2 deletions packages/utils/test/trimCanvas.tests.ts
Expand Up @@ -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');
Expand All @@ -12,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(9);
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);
});
});

0 comments on commit 82e8f9a

Please sign in to comment.