{"version":3,"file":"canvas.cjs","names":[],"sources":["../../src/imaging/canvas.ts"],"sourcesContent":["/**\n * The drawing surface, and the one thing everyone gets wrong on it.\n *\n * **JPEG has no alpha.** Encoding a transparent PNG as JPEG paints the\n * transparent pixels black. Filling the surface first is the difference\n * between a photo on a white background and one with a black hole in it.\n *\n * What is *not* here is worth recording. The received wisdom for downscaling\n * on a canvas is to halve repeatedly, because a single `drawImage` into a\n * much smaller box was said to alias. That was implemented here, and then\n * measured: on a 512 px checkerboard reduced to 32 px, the stepwise result\n * and the single high-quality draw were **pixel-identical** (standard\n * deviation 0.0 on both) in Chromium and Firefox — while stepwise cost\n * **39.19 ms against 0.13 ms** on a 4000x3000 photo, 300 times more, and\n * allocated three intermediate canvases on a device that may not have the\n * memory. Modern engines honour `imageSmoothingQuality = \"high\"`, which is\n * what this module sets. The halving was deleted rather than kept \"just in\n * case\": unmeasurable benefit at 300x the cost is not insurance, it is\n * ballast.\n */\n\nimport { ImagingUnavailableError } from \"./exceptions\";\n\n/** A canvas this module can draw on, on the main thread or in a worker. */\nexport type Surface = OffscreenCanvas | HTMLCanvasElement;\n\n/** A 2-D context from either surface kind. */\nexport type SurfaceContext = OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;\n\n/**\n * Create a drawing surface, preferring `OffscreenCanvas`.\n *\n * `OffscreenCanvas` works inside a worker, which is where a PWA wants this\n * running: resizing a 12-megapixel photo on the main thread blocks the UI\n * for tens of milliseconds per image.\n *\n * @param width Surface width in pixels.\n * @param height Surface height in pixels.\n * @returns The surface.\n * @throws {@link ImagingUnavailableError} when neither kind exists — a\n *   server render, or a test environment without a canvas.\n */\nexport function createSurface(width: number, height: number): Surface {\n    const safeWidth = Math.max(1, Math.round(width));\n    const safeHeight = Math.max(1, Math.round(height));\n\n    if (typeof OffscreenCanvas !== \"undefined\") {\n        return new OffscreenCanvas(safeWidth, safeHeight);\n    }\n    if (typeof document !== \"undefined\") {\n        const canvas = document.createElement(\"canvas\");\n        canvas.width = safeWidth;\n        canvas.height = safeHeight;\n        return canvas;\n    }\n    throw new ImagingUnavailableError(\n        \"No canvas is available here. This module needs a browser (or a worker \" +\n            \"with OffscreenCanvas); it does not run under plain Node.\",\n    );\n}\n\n/**\n * Get a 2-D context configured for image work.\n *\n * `imageSmoothingQuality = \"high\"` is the setting that makes a steep\n * downscale average its source pixels instead of sampling them sparsely.\n *\n * @param surface The surface to draw on.\n * @param background Optional fill painted before anything else.\n * @returns The context.\n * @throws {@link ImagingUnavailableError} when the context cannot be created.\n */\nexport function getContext(surface: Surface, background?: string): SurfaceContext {\n    const context = surface.getContext(\"2d\") as SurfaceContext | null;\n    if (context === null) {\n        throw new ImagingUnavailableError(\"Could not get a 2-D context from the canvas.\");\n    }\n    context.imageSmoothingEnabled = true;\n    context.imageSmoothingQuality = \"high\";\n    if (background !== undefined) {\n        context.fillStyle = background;\n        context.fillRect(0, 0, surface.width, surface.height);\n    }\n    return context;\n}\n\n/**\n * Draw a bitmap into a surface with high-quality filtering.\n *\n * @param bitmap The source pixels.\n * @param target Destination surface.\n * @param box Where to draw inside the destination.\n * @param background Fill painted before drawing.\n *\n * @tempest-limits param-count — source, destination, destination geometry, and an\n * optional background: the same four things `CanvasRenderingContext2D.drawImage`\n * takes, in the same order. Public surface, and a wrapper over a browser primitive\n * reads best when it keeps that primitive's shape.\n */\nexport function drawScaled(\n    bitmap: ImageBitmap,\n    target: Surface,\n    box: { x: number; y: number; width: number; height: number },\n    background?: string,\n): void {\n    const context = getContext(target, background);\n    context.drawImage(bitmap, box.x, box.y, box.width, box.height);\n}\n"],"mappings":"oCA0CA,SAAgB,EAAc,EAAe,EAAyB,CAClE,IAAM,EAAY,KAAK,IAAI,EAAG,KAAK,MAAM,CAAK,CAAC,EACzC,EAAa,KAAK,IAAI,EAAG,KAAK,MAAM,CAAM,CAAC,EAEjD,GAAI,OAAO,gBAAoB,IAC3B,OAAO,IAAI,gBAAgB,EAAW,CAAU,EAEpD,GAAI,OAAO,SAAa,IAAa,CACjC,IAAM,EAAS,SAAS,cAAc,QAAQ,EAG9C,MAFA,GAAO,MAAQ,EACf,EAAO,OAAS,EACT,CACX,CACA,MAAM,IAAI,EAAA,wBACN,gIAEJ,CACJ,CAaA,SAAgB,EAAW,EAAkB,EAAqC,CAC9E,IAAM,EAAU,EAAQ,WAAW,IAAI,EACvC,GAAI,IAAY,KACZ,MAAM,IAAI,EAAA,wBAAwB,8CAA8C,EAQpF,MANA,GAAQ,sBAAwB,GAChC,EAAQ,sBAAwB,OAC5B,IAAe,IAAA,KACf,EAAQ,UAAY,EACpB,EAAQ,SAAS,EAAG,EAAG,EAAQ,MAAO,EAAQ,MAAM,GAEjD,CACX,CAeA,SAAgB,EACZ,EACA,EACA,EACA,EACI,CAEJ,EAD2B,EAAQ,CACnC,CAAA,CAAQ,UAAU,EAAQ,EAAI,EAAG,EAAI,EAAG,EAAI,MAAO,EAAI,MAAM,CACjE"}