pixi.js
Version:
<p align="center"> <a href="https://pixijs.com" target="_blank" rel="noopener noreferrer"> <img height="150" src="https://files.pixijs.download/branding/pixijs-logo-transparent-dark.svg?v=1" alt="PixiJS logo"> </a> </p> <br/> <p align="center">
1 lines • 9.67 kB
Source Map (JSON)
{"version":3,"file":"getCanvasBoundingBox.mjs","sources":["../../../src/utils/canvas/getCanvasBoundingBox.ts"],"sourcesContent":["import { DOMAdapter } from '../../environment/adapter';\nimport { nextPow2 } from '../../maths/misc/pow2';\nimport { Rectangle } from '../../maths/shapes/Rectangle';\n\nimport type { ICanvas } from '../../environment/canvas/ICanvas';\nimport type { ICanvasRenderingContext2D } from '../../environment/canvas/ICanvasRenderingContext2D';\n\n// Internal canvas for measuring bounds\nlet _internalCanvas: ICanvas | null = null;\nlet _internalContext: ICanvasRenderingContext2D | null = null;\n\nfunction ensureInternalCanvas(width: number, height: number): void\n{\n if (!_internalCanvas)\n {\n _internalCanvas = DOMAdapter.get().createCanvas(256, 128);\n _internalContext = _internalCanvas.getContext('2d', { willReadFrequently: true });\n _internalContext.globalCompositeOperation = 'copy';\n _internalContext.globalAlpha = 1;\n }\n\n if (_internalCanvas.width < width || _internalCanvas.height < height)\n {\n // Use power-of-two dimensions for better performance\n _internalCanvas.width = nextPow2(width);\n _internalCanvas.height = nextPow2(height);\n }\n}\n\nfunction checkRow(data: Uint8ClampedArray, width: number, y: number)\n{\n for (let x = 0, index = 4 * y * width; x < width; ++x, index += 4)\n {\n if (data[index + 3] !== 0) return false;\n }\n\n return true;\n}\n\nfunction checkColumn(data: Uint8ClampedArray, width: number, x: number, top: number, bottom: number)\n{\n const stride = 4 * width;\n\n for (let y = top, index = (top * stride) + (4 * x); y <= bottom; ++y, index += stride)\n {\n if (data[index + 3] !== 0) return false;\n }\n\n return true;\n}\n\n/** @internal */\nexport interface GetCanvasBoundingBoxOptions\n{\n /** The canvas to measure */\n canvas: ICanvas;\n /** Optional. The width to analyze (defaults to canvas.width) */\n width?: number;\n /** Optional. The height to analyze (defaults to canvas.height) */\n height?: number;\n /**\n * Optional. The resolution at which to analyze the canvas, between 0-1.\n * Lower values improve performance for large canvases but may be less precise.\n * Default is 1 (full resolution).\n */\n resolution?: number;\n /** Optional. The rectangle to store the result in. */\n output?: Rectangle;\n}\n\n/**\n * Measures the bounding box of a canvas's visible (non-transparent) pixels.\n *\n * This function analyzes the alpha channel of the canvas pixels to find the smallest\n * rectangle containing all non-transparent pixels. It's useful for optimizing sprite\n * rendering by trimming transparent borders.\n *\n * Uses an internal canvas with `willReadFrequently: true` for efficient pixel data access.\n * This internal canvas is reused between calls for better performance.\n * @example\n * ```typescript\n * // Basic usage - get trim bounds at full resolution\n * const bounds = getCanvasBoundingBox({ canvas: myCanvas });\n * console.log(bounds); // Rectangle{x: 10, y: 5, width: 100, height: 200}\n * // Optimized for performance with lower resolution scanning\n * const fastBounds = getCanvasBoundingBox({\n * canvas: largeCanvas,\n * width: largeCanvas.width,\n * height: largeCanvas.height,\n * resolution: 0.5\n * });\n * // Resolution of 0.5 means scanning at half size, much faster for large canvases\n *\n * // Using custom dimensions - only analyze part of the canvas\n * const partialBounds = getCanvasBoundingBox({ canvas: myCanvas, width: 100, height: 100 });\n * // Only analyzes a 100x100 region starting from top-left\n * ```\n * @param options - The options for measuring the bounding box, including the canvas to measure.\n * @returns The bounding box as a Rectangle containing the visible content.\n * Returns Rectangle.EMPTY if the canvas is completely transparent.\n * @internal\n */\nexport function getCanvasBoundingBox(\n options: GetCanvasBoundingBoxOptions,\n): Rectangle;\n/**\n * @param canvas\n * @param resolution\n * @internal\n * @deprecated since 8.10.0\n */\nexport function getCanvasBoundingBox(canvas: ICanvas, resolution?: number): Rectangle;\n/**\n * @param {...any} args\n * @internal\n */\nexport function getCanvasBoundingBox(...args: [GetCanvasBoundingBoxOptions] | [ICanvas, number?]): Rectangle\n{\n let options = args[0] as GetCanvasBoundingBoxOptions;\n\n if (!options.canvas)\n {\n options = { canvas: args[0] as ICanvas, resolution: args[1] };\n }\n\n const { canvas } = options; // canvas is correctly extracted from options\n\n // Cap resolution at 1\n const resolution = Math.min(options.resolution ?? 1, 1);\n const width = options.width ?? canvas.width;\n const height = options.height ?? canvas.height;\n let output = options.output;\n\n // Ensure internal canvas is large enough\n ensureInternalCanvas(width, height);\n\n if (!_internalContext)\n {\n throw new TypeError('Failed to get canvas 2D context');\n }\n\n // Set up for pixel replacement (no blending)\n _internalContext.drawImage(\n canvas as unknown as CanvasImageSource,\n 0, 0,\n width, height,\n 0, 0,\n width * resolution, height * resolution\n );\n\n // Get the image data at full resolution\n const imageData = _internalContext.getImageData(0, 0, width, height);\n const data = imageData.data;\n\n let left = 0;\n let top = 0;\n let right = width - 1;\n let bottom = height - 1;\n\n while (top < height && checkRow(data, width, top)) ++top;\n if (top === height) return Rectangle.EMPTY;\n while (checkRow(data, width, bottom)) --bottom;\n while (checkColumn(data, width, left, top, bottom)) ++left;\n while (checkColumn(data, width, right, top, bottom)) --right;\n\n ++right;\n ++bottom;\n\n _internalContext.globalCompositeOperation = 'source-over';\n // draw the rect on the canvas\n _internalContext.strokeRect(left, top, right - left, bottom - top);\n _internalContext.globalCompositeOperation = 'copy';\n\n output ??= new Rectangle();\n\n output.set(left / resolution, top / resolution, (right - left) / resolution, (bottom - top) / resolution);\n\n return output;\n}\n\n"],"names":[],"mappings":"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"}