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.36 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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"}