pixel-data-js 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/color.ts","../src/ImageData/serialization.ts","../src/ImageData/blend-modes.ts","../src/ImageData/blit.ts","../src/ImageData/read-write-pixels.ts"],"sourcesContent":["import type { Color32, RGBA } from './_types'\n\n/**\n * Packs RGBA into a 32-bit integer compatible with\n * Little-Endian Uint32Array views on ImageData.\n */\nexport function packColor(r: number, g: number, b: number, a: number): Color32 {\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\nexport function packRGBA({ r, g, b, a }: RGBA): Color32 {\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\nexport const unpackRed = (packed: Color32): number => (packed >>> 0) & 0xFF\nexport const unpackGreen = (packed: Color32): number => (packed >>> 8) & 0xFF\nexport const unpackBlue = (packed: Color32): number => (packed >>> 16) & 0xFF\nexport const unpackAlpha = (packed: Color32): number => (packed >>> 24) & 0xFF\n\nexport function unpackColor(packed: Color32): RGBA {\n return {\n r: (packed >>> 0) & 0xFF,\n g: (packed >>> 8) & 0xFF,\n b: (packed >>> 16) & 0xFF,\n a: (packed >>> 24) & 0xFF,\n }\n}\n\nconst SCRATCH_RGBA: RGBA = { r: 0, g: 0, b: 0, a: 0 }\n\n// uses a scratch arg for memory perf. Be careful about re-use.\nexport function unpackColorTo(packed: Color32, scratch = SCRATCH_RGBA): RGBA {\n scratch.r = (packed >>> 0) & 0xFF\n scratch.g = (packed >>> 8) & 0xFF\n scratch.b = (packed >>> 16) & 0xFF\n scratch.a = (packed >>> 24) & 0xFF\n return scratch\n}\n\nexport function colorDistance(a: Color32, b: Color32): number {\n const dr = (a & 0xFF) - (b & 0xFF)\n const dg = ((a >>> 8) & 0xFF) - ((b >>> 8) & 0xFF)\n const db = ((a >>> 16) & 0xFF) - ((b >>> 16) & 0xFF)\n const da = ((a >>> 24) & 0xFF) - ((b >>> 24) & 0xFF)\n return dr * dr + dg * dg + db * db + da * da\n}\n\n/**\n * Linearly interpolates between two 32-bit colors using a floating-point weight.\n * * This is the preferred method for UI animations or scenarios where high\n * precision is required. It uses the standard `a + t * (b - a)` formula\n * for each channel.\n * @param a - The starting color as a 32-bit integer (AABBGGRR).\n * @param b - The target color as a 32-bit integer (AABBGGRR).\n * @param t - The interpolation factor between 0.0 and 1.0.\n * @returns The interpolated 32-bit color.\n */\nexport function lerpColor32(a: Color32, b: Color32, t: number): Color32 {\n const r = (a & 0xFF) + t * ((b & 0xFF) - (a & 0xFF))\n const g = ((a >>> 8) & 0xFF) + t * (((b >>> 8) & 0xFF) - ((a >>> 8) & 0xFF))\n const b_ = ((a >>> 16) & 0xFF) + t * (((b >>> 16) & 0xFF) - ((a >>> 16) & 0xFF))\n const a_ = ((a >>> 24) & 0xFF) + t * (((b >>> 24) & 0xFF) - ((a >>> 24) & 0xFF))\n\n return ((a_ << 24) | (b_ << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Linearly interpolates between two 32-bit colors using integer fixed-point math.\n * Highly optimized for image processing and real-time blitting. It processes\n * channels in parallel using bitmasks (RB and GA pairs).\n * @note Subject to a 1-bit drift (rounding down) due to fast bit-shift division.\n * @param src - The source (foreground) color as a 32-bit integer.\n * @param dst - The destination (background) color as a 32-bit integer.\n * @param w - The blend weight as a byte value from 0 to 255. Where 0 is 100% dst and 255 is 100% src\n * @returns The blended 32-bit color.\n */export function lerpColor32Fast(src: Color32, dst: Color32, w: number): Color32 {\n const invA = 255 - w;\n\n // Masking Red and Blue: 0x00FF00FF\n // We process R and B in one go, then shift back down\n const rb = (((src & 0x00FF00FF) * w + (dst & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF;\n\n // Masking Green and Alpha: 0xFF00FF00\n // We shift down first to avoid overflow, then shift back up\n const ga = ((((src >>> 8) & 0x00FF00FF) * w + ((dst >>> 8) & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF;\n\n return (rb | (ga << 8)) >>> 0 as Color32;\n}\n\n// Convert 0xAABBGGRR to #RRGGBBAA\nexport function color32ToHex(color: Color32): string {\n const r = (color & 0xFF).toString(16).padStart(2, '0')\n const g = ((color >>> 8) & 0xFF).toString(16).padStart(2, '0')\n const b = ((color >>> 16) & 0xFF).toString(16).padStart(2, '0')\n const a = ((color >>> 24) & 0xFF).toString(16).padStart(2, '0')\n return `#${r}${g}${b}${a}`\n}\n\n/**\n * Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.\n * Example: 0xFF0000FF -> \"rgba(255,0,0,1)\"\n */\nexport function color32ToCssRGBA(color: Color32): string {\n const r = color & 0xFF\n const g = (color >>> 8) & 0xFF\n const b = (color >>> 16) & 0xFF\n const a = (color >>> 24) & 0xFF\n\n const alpha = Number((a / 255).toFixed(3))\n\n return `rgba(${r},${g},${b},${alpha})`\n}\n","import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'\n\nexport function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {\n const binary = String.fromCharCode(...new Uint8Array(buffer))\n return btoa(binary) as Base64EncodedUInt8Array\n}\n\nexport function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray {\n const binary = atob(encoded)\n const bytes = new Uint8ClampedArray(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n/**\n * Serialize for use in JSON. Pixel data is stored as base64 encoded string.\n */\nexport function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData {\n return {\n width: imageData.width,\n height: imageData.height,\n data: base64EncodeArrayBuffer(imageData.data.buffer),\n }\n}\n\nexport function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData {\n if (!imageData) return null as any\n\n return serializeImageData(imageData) as any\n}\n\nexport function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike {\n return {\n width: serialized.width,\n height: serialized.height,\n data: base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array),\n }\n}\n\nexport function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData {\n const data = base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array)\n\n return new ImageData(data as ImageDataArray, serialized.width, serialized.height) as any\n}\n\nexport function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData {\n if (!serialized) return null as any\n return deserializeImageData(serialized) as any\n}\n","import type { BlendColor32, Color32 } from '../_types'\n\nexport const sourceOverColor32: BlendColor32 = (src, dst) => {\n const a = (src >>> 24) & 0xFF\n if (a === 255) return src\n if (a === 0) return dst\n\n // Pattern: (src * a + dst * (255 - a)) >> 8\n // We process RB and G separately so they don't overflow into each other\n const rbMask = 0xFF00FF\n const gMask = 0x00FF00\n\n const sRB = src & rbMask\n const sG = src & gMask\n const dRB = dst & rbMask\n const dG = dst & gMask\n\n const invA = 255 - a\n\n const outRB = ((sRB * a + dRB * invA) >> 8) & rbMask\n const outG = ((sG * a + dG * invA) >> 8) & gMask\n\n // Re-pack with opaque alpha (or calculate combined alpha if needed)\n const outA = (a + (((dst >>> 24) & 0xFF) * invA >> 8))\n return ((outA << 24) | outRB | outG) >>> 0 as Color32\n}\n\n/**\n * Screen: Lightens the destination (inverse of Multiply).\n * Result = 1 - ((1 - Src) * (1 - Dst))\n */\nexport const screenColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = 255 - (((255 - (src & 0xFF)) * (255 - dr)) >> 8)\n const bg = 255 - (((255 - ((src >> 8) & 0xFF)) * (255 - dg)) >> 8)\n const bb = 255 - (((255 - ((src >> 16) & 0xFF)) * (255 - db)) >> 8)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Linear Dodge (Additive): Simply adds the source to the destination.\n * Clamps at 255.\n */\nexport const linearDodgeColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math (Additive with clamping)\n const br = Math.min(255, (src & 0xFF) + dr)\n const bg = Math.min(255, ((src >> 8) & 0xFF) + dg)\n const bb = Math.min(255, ((src >> 16) & 0xFF) + db)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Multiply: Darkens the destination based on the source color.\n * Result = (Src * Dst) / 255\n */\nexport const multiplyColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = ((src & 0xFF) * dr) >> 8\n const bg = (((src >> 8) & 0xFF) * dg) >> 8\n const bb = (((src >> 16) & 0xFF) * db) >> 8\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Difference: Subtracts the darker color from the lighter color.\n * Result = |Src - Dst|\n */\nexport const differenceColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = Math.abs((src & 0xFF) - dr)\n const bg = Math.abs(((src >> 8) & 0xFF) - dg)\n const bb = Math.abs(((src >> 16) & 0xFF) - db)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Hard Light: Decides Multiply vs Screen based on SOURCE brightness.\n * Acts like a harsh spotlight.\n */\nexport const hardLightColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = sr < 128 ? (2 * sr * dr) >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8)\n const bg = sg < 128 ? (2 * sg * dg) >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8)\n const bb = sb < 128 ? (2 * sb * db) >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Color Burn: Darkens the destination to reflect the source color.\n * Intense saturation in the darks.\n */\nexport const colorBurnColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math (Avoid division by zero)\n const br = dr === 255 ? 255 : Math.max(0, 255 - ((255 - dr) << 8) / (sr || 1))\n const bg = dg === 255 ? 255 : Math.max(0, 255 - ((255 - dg) << 8) / (sg || 1))\n const bb = db === 255 ? 255 : Math.max(0, 255 - ((255 - db) << 8) / (sb || 1))\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n/**\n * Overlay: The classic \"Contrast\" mode.\n * Decides Multiply vs Screen based on DESTINATION brightness.\n */\nexport const overlayColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = dr < 128 ? (2 * sr * dr) >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8)\n const bg = dg < 128 ? (2 * sg * dg) >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8)\n const bb = db < 128 ? (2 * sb * db) >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\nexport const COLOR_32_BLEND_MODES = {\n sourceOver: sourceOverColor32,\n screen: screenColor32,\n linearDodge: linearDodgeColor32,\n multiply: multiplyColor32,\n difference: differenceColor32,\n overlay: overlayColor32,\n hardLight: hardLightColor32,\n colorBurn: colorBurnColor32,\n}\n","import type { BlendColor32, Color32 } from '../_types'\nimport { sourceOverColor32 } from './blend-modes'\n\nexport type BlendImageDataOptions = {\n dx?: number\n dy?: number\n sx?: number\n sy?: number\n sw?: number\n sh?: number\n opacity?: number\n alpha?: number\n mask?: Uint8Array | null\n maskMode?: 'binary' | 'alpha'\n blendFn?: BlendColor32\n}\n\n/**\n * Blits source ImageData into a destination ImageData using 32-bit integer bitwise blending.\n * * This function bypasses standard Canvas API limitations by operating directly on\n * Uint32Array views. It supports various blend modes, binary/alpha masking, and\n * automatic clipping of both source and destination bounds.\n * * @param dst - The destination ImageData to write into.\n * @param src - The source ImageData to read from.\n * @param dst - The destination ImageData to write to.\n * @param opts - Configuration for the blit operation.\n * @param opts.dx - Destination X offset. Defaults to 0.\n * @param opts.dy - Destination Y offset. Defaults to 0.\n * @param opts.sx - Source X offset. Defaults to 0.\n * @param opts.sy - Source Y offset. Defaults to 0.\n * @param opts.sw - Width of the source area to blit. Defaults to src.width.\n * @param opts.sh - Height of the source area to blit. Defaults to src.height.\n * @param opts.opacity - Global strength of the blit (0.0 to 1.0). Defaults to 1.0.\n * @param opts.alpha - Global strength of the blit (0 to 255). Overrides 'opacity' if provided.\n * @param opts.mask - An optional Uint8Array acting as a stencil or alpha mask.\n * Must match source dimensions.\n * @param opts.maskMode - 'binary' ignores pixels where mask is 0.\n * 'alpha' scales source alpha by mask value (0-255).\n * @param opts.blendFn - The math logic used to combine pixels.\n * Defaults to `sourceOverColor32`.\n * * @example\n * blendImageData32(ctx.getImageData(0,0,100,100), sprite, {\n * blendFn: COLOR_32_BLEND_MODES.multiply,\n * mask: brushMask,\n * maskMode: 'alpha'\n * });\n */\nexport function blendImageData(\n dst: ImageData,\n src: ImageData,\n opts: BlendImageDataOptions,\n) {\n let {\n dx = 0,\n dy = 0,\n sx = 0,\n sy = 0,\n sw = src.width,\n sh = src.height,\n maskMode = 'alpha',\n opacity = 1,\n alpha,\n blendFn = sourceOverColor32,\n mask,\n } = opts\n\n // 1. Clip Source Area\n if (sx < 0) {\n dx -= sx\n sw += sx\n sx = 0\n }\n if (sy < 0) {\n dy -= sy\n sh += sy\n sy = 0\n }\n sw = Math.min(sw, src.width - sx)\n sh = Math.min(sh, src.height - sy)\n\n // 2. Clip Destination Area\n if (dx < 0) {\n sx -= dx\n sw += dx\n dx = 0\n }\n if (dy < 0) {\n sy -= dy\n sh += dy\n dy = 0\n }\n const actualW = Math.min(sw, dst.width - dx)\n const actualH = Math.min(sh, dst.height - dy)\n\n if (actualW <= 0 || actualH <= 0) return\n\n // 32-bit views of the same memory\n const dst32 = new Uint32Array(dst.data.buffer)\n const src32 = new Uint32Array(src.data.buffer)\n\n const dw = dst.width\n const sw_orig = src.width\n\n const gAlpha = alpha !== undefined\n ? (alpha | 0)\n : Math.round(opacity * 255)\n\n const maskIsAlpha = maskMode === 'alpha'\n\n for (let iy = 0; iy < actualH; iy++) {\n const dRow = (iy + dy) * dw\n const sRow = (iy + sy) * sw_orig\n\n for (let ix = 0; ix < actualW; ix++) {\n const di = dRow + (ix + dx)\n const si = sRow + (ix + sx)\n\n let s = src32[si] as Color32\n let sa = (s >>> 24) & 0xFF\n\n // skip fully transparent pixel\n if (sa === 0) continue\n\n let activeWeight = gAlpha\n\n if (mask) {\n const m = mask[si]\n if (m === 0) continue\n activeWeight = maskIsAlpha ? (m * activeWeight + 128) >> 8 : activeWeight\n }\n\n if (activeWeight < 255) {\n sa = (sa * activeWeight + 128) >> 8\n }\n\n // If combined alpha is 0 after masking/opacity, skip the blend math\n if (sa === 0) continue\n\n // Re-pack source with final calculated alpha\n s = ((s & 0x00FFFFFF) | (sa << 24)) >>> 0 as Color32\n\n dst32[di] = blendFn(s, dst32[di] as Color32)\n }\n }\n}\n","import type { Color32, ImageDataLike } from '../_types'\n\nexport function makeImageDataColor32Adapter(imageData: ImageDataLike) {\n const data32 = new Uint32Array(imageData.data.buffer)\n\n function inBounds(x: number, y: number) {\n return x < 0 || x >= imageData.width || y < 0 || y >= imageData.height\n }\n\n function setPixel(\n x: number,\n y: number,\n color: Color32,\n ): void {\n if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) return\n data32[y * imageData.width + x] = color\n }\n\n function getPixel(\n x: number,\n y: number,\n ): Color32 | undefined {\n if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) return\n\n return data32[y * imageData.width + x] as Color32\n }\n\n return {\n inBounds,\n imageData,\n data32,\n setPixel,\n getPixel,\n }\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/ImageData/blend-modes.ts","../src/_types.ts","../src/ImageData/blit.ts","../src/ImageData/mask.ts","../src/ImageData/read-write-pixels.ts","../src/ImageData/serialization.ts","../src/color.ts"],"sourcesContent":["import type { BlendColor32, Color32 } from '../_types'\n\nexport const sourceOverColor32: BlendColor32 = (src, dst) => {\n const a = (src >>> 24) & 0xFF\n if (a === 255) return src\n if (a === 0) return dst\n\n // Pattern: (src * a + dst * (255 - a)) >> 8\n // We process RB and G separately so they don't overflow into each other\n const rbMask = 0xFF00FF\n const gMask = 0x00FF00\n\n const sRB = src & rbMask\n const sG = src & gMask\n const dRB = dst & rbMask\n const dG = dst & gMask\n\n const invA = 255 - a\n\n const outRB = ((sRB * a + dRB * invA) >> 8) & rbMask\n const outG = ((sG * a + dG * invA) >> 8) & gMask\n\n // Re-pack with opaque alpha (or calculate combined alpha if needed)\n const outA = (a + (((dst >>> 24) & 0xFF) * invA >> 8))\n return ((outA << 24) | outRB | outG) >>> 0 as Color32\n}\n\n/**\n * Screen: Lightens the destination (inverse of Multiply).\n * Result = 1 - ((1 - Src) * (1 - Dst))\n */\nexport const screenColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = 255 - (((255 - (src & 0xFF)) * (255 - dr)) >> 8)\n const bg = 255 - (((255 - ((src >> 8) & 0xFF)) * (255 - dg)) >> 8)\n const bb = 255 - (((255 - ((src >> 16) & 0xFF)) * (255 - db)) >> 8)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Linear Dodge (Additive): Simply adds the source to the destination.\n * Clamps at 255.\n */\nexport const linearDodgeColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math (Additive with clamping)\n const br = Math.min(255, (src & 0xFF) + dr)\n const bg = Math.min(255, ((src >> 8) & 0xFF) + dg)\n const bb = Math.min(255, ((src >> 16) & 0xFF) + db)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Multiply: Darkens the destination based on the source color.\n * Result = (Src * Dst) / 255\n */\nexport const multiplyColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = ((src & 0xFF) * dr) >> 8\n const bg = (((src >> 8) & 0xFF) * dg) >> 8\n const bb = (((src >> 16) & 0xFF) * db) >> 8\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Difference: Subtracts the darker color from the lighter color.\n * Result = |Src - Dst|\n */\nexport const differenceColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = Math.abs((src & 0xFF) - dr)\n const bg = Math.abs(((src >> 8) & 0xFF) - dg)\n const bb = Math.abs(((src >> 16) & 0xFF) - db)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Hard Light: Decides Multiply vs Screen based on SOURCE brightness.\n * Acts like a harsh spotlight.\n */\nexport const hardLightColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = sr < 128 ? (2 * sr * dr) >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8)\n const bg = sg < 128 ? (2 * sg * dg) >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8)\n const bb = sb < 128 ? (2 * sb * db) >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Color Burn: Darkens the destination to reflect the source color.\n * Intense saturation in the darks.\n */\nexport const colorBurnColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math (Avoid division by zero)\n const br = dr === 255 ? 255 : Math.max(0, 255 - ((255 - dr) << 8) / (sr || 1))\n const bg = dg === 255 ? 255 : Math.max(0, 255 - ((255 - dg) << 8) / (sg || 1))\n const bb = db === 255 ? 255 : Math.max(0, 255 - ((255 - db) << 8) / (sb || 1))\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n/**\n * Overlay: The classic \"Contrast\" mode.\n * Decides Multiply vs Screen based on DESTINATION brightness.\n */\nexport const overlayColor32: BlendColor32 = (src, dst) => {\n const sa = (src >>> 24) & 0xFF\n if (sa === 0) return dst\n\n const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF\n const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF\n\n // 1. Core Math\n const br = dr < 128 ? (2 * sr * dr) >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8)\n const bg = dg < 128 ? (2 * sg * dg) >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8)\n const bb = db < 128 ? (2 * sb * db) >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8)\n\n if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32\n\n // 2. Alpha Lerp inlined\n const invA = 255 - sa\n const r = (br * sa + dr * invA) >> 8\n const g = (bg * sa + dg * invA) >> 8\n const b = (bb * sa + db * invA) >> 8\n const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8\n\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\nexport const COLOR_32_BLEND_MODES = {\n sourceOver: sourceOverColor32,\n screen: screenColor32,\n linearDodge: linearDodgeColor32,\n multiply: multiplyColor32,\n difference: differenceColor32,\n overlay: overlayColor32,\n hardLight: hardLightColor32,\n colorBurn: colorBurnColor32,\n}\n","// ALL values are 0-255 (including alpha which in CSS is 0-1)\nexport type RGBA = { r: number, g: number, b: number, a: number }\n\n// A 32-bit integer containing r,g,b,a data\nexport type Color32 = number & { readonly __brandColor32: unique symbol }\n\nexport type BlendColor32 = (src: Color32, dst: Color32) => Color32\n\nexport type ImageDataLike = {\n width: number\n height: number\n data: Uint8ClampedArray<ArrayBuffer>\n}\n\nexport type SerializedImageData = {\n width: number\n height: number\n data: string\n}\n\nexport type Base64EncodedUInt8Array = string & { readonly __brandBase64UInt8Array: unique symbol }\n\nexport type Rect = { x: number; y: number; w: number; h: number }\n\nexport enum MaskType {\n ALPHA,\n BINARY\n}\n\ninterface BaseMaskData {\n readonly width: number\n readonly height: number\n readonly data: Uint8Array\n}\n\nexport interface AlphaMask extends BaseMaskData {\n readonly type: MaskType.ALPHA\n}\n\nexport interface BinaryMask extends BaseMaskData {\n readonly type: MaskType.BINARY\n}\n\nexport type AnyMask = AlphaMask | BinaryMask\n","import { type AnyMask, type BlendColor32, type Color32, type ImageDataLike, MaskType } from '../_types'\nimport { sourceOverColor32 } from './blend-modes'\n\nexport type BlendImageDataOptions = {\n /**\n * The x-coordinate in the destination image where the blend begins.\n * @default 0\n */\n dx?: number\n\n /**\n * The y-coordinate in the destination image where the blend begins.\n * @default 0\n */\n dy?: number\n\n /**\n * The x-coordinate of the top-left corner of the sub-rectangle\n * of the source image to extract.\n * @default 0\n */\n sx?: number\n\n /**\n * The y-coordinate of the top-left corner of the sub-rectangle\n * of the source image to extract.\n * @default 0\n */\n sy?: number\n\n /**\n * The width of the sub-rectangle of the source image to extract.\n * Defaults to the full remaining width of the source.\n */\n sw?: number\n\n /**\n * The height of the sub-rectangle of the source image to extract.\n * Defaults to the full remaining height of the source.\n */\n sh?: number\n\n /**\n * Overall layer opacity, typically ranging from 0.0 (transparent) to 1.0 (opaque).\n * @default 1.0\n */\n opacity?: number\n\n /**\n * Same as opacity but is 0-255 and faster when processing. If Present opacity is ignored.\n * @default undefined\n */\n alpha?: number\n\n /**\n * An optional alpha mask buffer.\n * The values in this array (0-255) determine the intensity of the blend\n * at each corresponding pixel.\n */\n mask?: AnyMask | null\n\n /**\n * The specific blending function/algorithm to use for pixel math\n * (e.g., Multiply, Screen, Overlay).\n */\n blendFn?: BlendColor32\n};\n\n/**\n * Blits source ImageData into a destination ImageData using 32-bit integer bitwise blending.\n * This function bypasses standard Canvas API limitations by operating directly on\n * Uint32Array views. It supports various blend modes, binary/alpha masking, and\n * automatic clipping of both source and destination bounds.\n * @example\n * blendImageData32(ctx.getImageData(0,0,100,100), sprite, {\n * blendFn: COLOR_32_BLEND_MODES.multiply,\n * mask: brushMask,\n * maskMode: MaskMode.ALPHA\n * });\n */\nexport function blendImageData(\n dst: ImageDataLike,\n src: ImageDataLike,\n opts: BlendImageDataOptions,\n) {\n let {\n dx = 0,\n dy = 0,\n sx = 0,\n sy = 0,\n sw = src.width,\n sh = src.height,\n opacity = 1,\n alpha,\n blendFn = sourceOverColor32,\n mask,\n } = opts\n\n // 1. Clip Source Area\n if (sx < 0) {\n dx -= sx\n sw += sx\n sx = 0\n }\n if (sy < 0) {\n dy -= sy\n sh += sy\n sy = 0\n }\n sw = Math.min(sw, src.width - sx)\n sh = Math.min(sh, src.height - sy)\n\n // 2. Clip Destination Area\n if (dx < 0) {\n sx -= dx\n sw += dx\n dx = 0\n }\n if (dy < 0) {\n sy -= dy\n sh += dy\n dy = 0\n }\n const actualW = Math.min(sw, dst.width - dx)\n const actualH = Math.min(sh, dst.height - dy)\n\n if (actualW <= 0 || actualH <= 0) return\n\n // 32-bit views of the same memory\n const dst32 = new Uint32Array(dst.data.buffer)\n const src32 = new Uint32Array(src.data.buffer)\n\n const dw = dst.width\n const sw_orig = src.width\n\n const gAlpha = alpha !== undefined\n ? (alpha | 0)\n : Math.round(opacity * 255)\n\n const maskIsAlpha = mask?.type === MaskType.ALPHA\n\n for (let iy = 0; iy < actualH; iy++) {\n const dRow = (iy + dy) * dw\n const sRow = (iy + sy) * sw_orig\n\n for (let ix = 0; ix < actualW; ix++) {\n const di = dRow + (ix + dx)\n const si = sRow + (ix + sx)\n\n let s = src32[si] as Color32\n let sa = (s >>> 24) & 0xFF\n\n // skip fully transparent pixel\n if (sa === 0) continue\n\n let activeWeight = gAlpha\n\n if (mask) {\n const m = mask.data[si]\n if (m === 0) continue\n activeWeight = maskIsAlpha ? (m * activeWeight + 128) >> 8 : activeWeight\n }\n\n if (activeWeight < 255) {\n sa = (sa * activeWeight + 128) >> 8\n }\n\n // If combined alpha is 0 after masking/opacity, skip the blend math\n if (sa === 0) continue\n\n // Re-pack source with final calculated alpha\n s = ((s & 0x00FFFFFF) | (sa << 24)) >>> 0 as Color32\n\n dst32[di] = blendFn(s, dst32[di] as Color32)\n }\n }\n}\n","import type { AlphaMask, BinaryMask, ImageDataLike } from '../_types'\n\nexport type ApplyMaskOptions = {\n /**\n * The x-coordinate in the destination image where the mask begins.\n * @default 0\n */\n dx?: number\n\n /**\n * The y-coordinate in the destination image where the mask begins.\n * @default 0\n */\n dy?: number\n\n /**\n * The x-coordinate of the top-left corner of the sub-rectangle\n * of the source image to extract.\n * @default 0\n */\n sx?: number\n\n /**\n * The y-coordinate of the top-left corner of the sub-rectangle\n * of the source image to extract.\n * @default 0\n */\n sy?: number\n\n /**\n * The width of the sub-rectangle of the source image to extract.\n * Defaults to the full remaining width of the source.\n */\n sw?: number\n\n /**\n * The height of the sub-rectangle of the source image to extract.\n * Defaults to the full remaining height of the source.\n */\n sh?: number;\n}\n\n/**\n * Applies a binary (on/off) mask to an RGBA buffer.\n * If mask value is 0, pixel becomes transparent.\n */\nexport function applyBinaryMask(\n dst: ImageDataLike,\n mask: BinaryMask,\n opts: ApplyMaskOptions = {},\n) {\n const { width: maskWidth, height: maskHeight } = mask\n\n const { dx = 0, dy = 0, sx = 0, sy = 0, sw = maskWidth, sh = maskHeight } = opts\n\n // 1. Calculate intersection boundaries\n const x0 = Math.max(0, dx, dx + (0 - sx))\n const y0 = Math.max(0, dy, dy + (0 - sy))\n const x1 = Math.min(dst.width, dx + sw, dx + (maskWidth - sx))\n const y1 = Math.min(dst.height, dy + sh, dy + (maskHeight - sy))\n\n if (x1 <= x0 || y1 <= y0) return\n\n const { data: dstData, width: dstW } = dst\n\n for (let y = y0; y < y1; y++) {\n const maskY = y - dy + sy\n const dstRowOffset = y * dstW * 4\n const maskRowOffset = maskY * maskWidth\n\n for (let x = x0; x < x1; x++) {\n const maskX = x - dx + sx\n const mIdx = maskRowOffset + maskX\n\n // Binary check: If mask is 0, kill the alpha\n if (mask.data[mIdx] === 0) {\n const aIdx = dstRowOffset + (x * 4) + 3\n dstData[aIdx] = 0\n }\n }\n }\n\n return dst\n}\n\n/**\n * Applies a smooth alpha mask to an RGBA buffer.\n * Multiplies existing Alpha by (maskValue / 255).\n */\nexport function applyAlphaMask(\n dst: ImageData,\n mask: AlphaMask,\n opts: ApplyMaskOptions = {},\n): void {\n let { dx = 0, dy = 0, sx = 0, sy = 0, sw = mask.width, sh = mask.height } = opts\n\n // 1. Clipping Logic\n if (dx < 0) {\n sx -= dx\n sw += dx\n dx = 0\n }\n if (dy < 0) {\n sy -= dy\n sh += dy\n dy = 0\n }\n if (sx < 0) {\n dx -= sx\n sw += sx\n sx = 0\n }\n if (sy < 0) {\n dy -= sy\n sh += sy\n sy = 0\n }\n const actualW = Math.min(sw, dst.width - dx, mask.width - sx)\n const actualH = Math.min(sh, dst.height - dy, mask.height - sy)\n\n if (actualW <= 0 || actualH <= 0) return\n\n const dData = dst.data\n const mData = mask.data\n const dW = dst.width\n const mW = mask.width\n\n for (let y = 0; y < actualH; y++) {\n const dOffset = ((dy + y) * dW + dx) << 2\n const mOffset = (sy + y) * mW + sx\n\n for (let x = 0; x < actualW; x++) {\n const mVal = mData[mOffset + x]\n\n if (mVal === 255) continue\n\n const aIdx = dOffset + (x << 2) + 3\n\n // --- BRANCH: Zero Alpha ---\n if (mVal === 0) {\n dData[aIdx] = 0\n continue\n }\n\n // To get 101 from 200 * 128, we use the bias (a * m + 257) >> 8\n // 25600 + 257 = 25857. 25857 >> 8 = 101.\n dData[aIdx] = (dData[aIdx] * mVal + 257) >> 8\n }\n }\n}\n","import type { Color32, ImageDataLike, Rect } from '../_types'\n\nexport function makeImageDataColor32Adapter(imageData: ImageDataLike) {\n const data32 = new Uint32Array(imageData.data.buffer)\n\n function inBounds(x: number, y: number) {\n return x < 0 || x >= imageData.width || y < 0 || y >= imageData.height\n }\n\n function setPixel(\n x: number,\n y: number,\n color: Color32,\n ): void {\n if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) return\n data32[y * imageData.width + x] = color\n }\n\n function getPixel(\n x: number,\n y: number,\n ): Color32 | undefined {\n if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) return\n\n return data32[y * imageData.width + x] as Color32\n }\n\n return {\n inBounds,\n imageData,\n data32,\n setPixel,\n getPixel,\n }\n}\n\nexport function extractPixelData(\n imageData: ImageDataLike,\n rect: Rect,\n): Uint8ClampedArray\n\nexport function extractPixelData(\n imageData: ImageDataLike,\n x: number,\n y: number,\n w: number,\n h: number,\n): Uint8ClampedArray\nexport function extractPixelData(\n imageData: ImageDataLike,\n _x: Rect | number,\n _y?: number,\n _w?: number,\n _h?: number,\n): Uint8ClampedArray {\n const { x, y, w, h } = typeof _x === 'object'\n ? _x\n : { x: _x, y: _y!, w: _w!, h: _h! }\n\n const { width: srcW, height: srcH, data: src } = imageData\n // Safety check for invalid dimensions\n if (w <= 0 || h <= 0) return new Uint8ClampedArray(0)\n const out = new Uint8ClampedArray(w * h * 4)\n\n const x0 = Math.max(0, x)\n const y0 = Math.max(0, y)\n const x1 = Math.min(srcW, x + w)\n const y1 = Math.min(srcH, y + h)\n\n // If no intersection, return the empty\n if (x1 <= x0 || y1 <= y0) return out\n\n for (let row = 0; row < (y1 - y0); row++) {\n // Where to read from the source canvas\n const srcRow = y0 + row\n const srcStart = (srcRow * srcW + x0) * 4\n const rowLen = (x1 - x0) * 4\n\n // Where to write into the 'out' patch\n const dstRow = (y0 - y) + row\n const dstCol = (x0 - x)\n const dstStart = (dstRow * w + dstCol) * 4\n\n // Perform the high-speed bulk copy\n out.set(src.subarray(srcStart, srcStart + rowLen), dstStart)\n }\n\n return out\n}\n\nexport function copyImageData({ data, width, height }: ImageDataLike): ImageData {\n return new ImageData(data.slice(), width, height)\n}\n\nexport function copyImageDataLike({ data, width, height }: ImageDataLike): ImageDataLike {\n return {\n data: data.slice(),\n width,\n height,\n }\n}\n","import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'\n\nexport function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {\n const binary = String.fromCharCode(...new Uint8Array(buffer))\n return btoa(binary) as Base64EncodedUInt8Array\n}\n\nexport function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray<ArrayBuffer> {\n const binary = atob(encoded)\n const bytes = new Uint8ClampedArray(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n/**\n * Serialize for use in JSON. Pixel data is stored as base64 encoded string.\n */\nexport function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData {\n return {\n width: imageData.width,\n height: imageData.height,\n data: base64EncodeArrayBuffer(imageData.data.buffer),\n }\n}\n\nexport function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData {\n if (!imageData) return null as any\n\n return serializeImageData(imageData) as any\n}\n\nexport function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike {\n return {\n width: serialized.width,\n height: serialized.height,\n data: base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array),\n }\n}\n\nexport function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData {\n const data = base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array)\n\n return new ImageData(data as ImageDataArray, serialized.width, serialized.height) as any\n}\n\nexport function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData {\n if (!serialized) return null as any\n return deserializeImageData(serialized) as any\n}\n","import type { Color32, RGBA } from './_types'\n\n/**\n * Packs RGBA into a 32-bit integer compatible with\n * Little-Endian Uint32Array views on ImageData.\n */\nexport function packColor(r: number, g: number, b: number, a: number): Color32 {\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\nexport function packRGBA({ r, g, b, a }: RGBA): Color32 {\n return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\nexport const unpackRed = (packed: Color32): number => (packed >>> 0) & 0xFF\nexport const unpackGreen = (packed: Color32): number => (packed >>> 8) & 0xFF\nexport const unpackBlue = (packed: Color32): number => (packed >>> 16) & 0xFF\nexport const unpackAlpha = (packed: Color32): number => (packed >>> 24) & 0xFF\n\nexport function unpackColor(packed: Color32): RGBA {\n return {\n r: (packed >>> 0) & 0xFF,\n g: (packed >>> 8) & 0xFF,\n b: (packed >>> 16) & 0xFF,\n a: (packed >>> 24) & 0xFF,\n }\n}\n\nconst SCRATCH_RGBA: RGBA = { r: 0, g: 0, b: 0, a: 0 }\n\n// uses a scratch arg for memory perf. Be careful about re-use.\nexport function unpackColorTo(packed: Color32, scratch = SCRATCH_RGBA): RGBA {\n scratch.r = (packed >>> 0) & 0xFF\n scratch.g = (packed >>> 8) & 0xFF\n scratch.b = (packed >>> 16) & 0xFF\n scratch.a = (packed >>> 24) & 0xFF\n return scratch\n}\n\nexport function colorDistance(a: Color32, b: Color32): number {\n const dr = (a & 0xFF) - (b & 0xFF)\n const dg = ((a >>> 8) & 0xFF) - ((b >>> 8) & 0xFF)\n const db = ((a >>> 16) & 0xFF) - ((b >>> 16) & 0xFF)\n const da = ((a >>> 24) & 0xFF) - ((b >>> 24) & 0xFF)\n return dr * dr + dg * dg + db * db + da * da\n}\n\n/**\n * Linearly interpolates between two 32-bit colors using a floating-point weight.\n * * This is the preferred method for UI animations or scenarios where high\n * precision is required. It uses the standard `a + t * (b - a)` formula\n * for each channel.\n * @param a - The starting color as a 32-bit integer (AABBGGRR).\n * @param b - The target color as a 32-bit integer (AABBGGRR).\n * @param t - The interpolation factor between 0.0 and 1.0.\n * @returns The interpolated 32-bit color.\n */\nexport function lerpColor32(a: Color32, b: Color32, t: number): Color32 {\n const r = (a & 0xFF) + t * ((b & 0xFF) - (a & 0xFF))\n const g = ((a >>> 8) & 0xFF) + t * (((b >>> 8) & 0xFF) - ((a >>> 8) & 0xFF))\n const b_ = ((a >>> 16) & 0xFF) + t * (((b >>> 16) & 0xFF) - ((a >>> 16) & 0xFF))\n const a_ = ((a >>> 24) & 0xFF) + t * (((b >>> 24) & 0xFF) - ((a >>> 24) & 0xFF))\n\n return ((a_ << 24) | (b_ << 16) | (g << 8) | r) >>> 0 as Color32\n}\n\n/**\n * Linearly interpolates between two 32-bit colors using integer fixed-point math.\n * Highly optimized for image processing and real-time blitting. It processes\n * channels in parallel using bitmasks (RB and GA pairs).\n * @note Subject to a 1-bit drift (rounding down) due to fast bit-shift division.\n * @param src - The source (foreground) color as a 32-bit integer.\n * @param dst - The destination (background) color as a 32-bit integer.\n * @param w - The blend weight as a byte value from 0 to 255. Where 0 is 100% dst and 255 is 100% src\n * @returns The blended 32-bit color.\n */export function lerpColor32Fast(src: Color32, dst: Color32, w: number): Color32 {\n const invA = 255 - w;\n\n // Masking Red and Blue: 0x00FF00FF\n // We process R and B in one go, then shift back down\n const rb = (((src & 0x00FF00FF) * w + (dst & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF;\n\n // Masking Green and Alpha: 0xFF00FF00\n // We shift down first to avoid overflow, then shift back up\n const ga = ((((src >>> 8) & 0x00FF00FF) * w + ((dst >>> 8) & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF;\n\n return (rb | (ga << 8)) >>> 0 as Color32;\n}\n\n// Convert 0xAABBGGRR to #RRGGBBAA\nexport function color32ToHex(color: Color32): string {\n const r = (color & 0xFF).toString(16).padStart(2, '0')\n const g = ((color >>> 8) & 0xFF).toString(16).padStart(2, '0')\n const b = ((color >>> 16) & 0xFF).toString(16).padStart(2, '0')\n const a = ((color >>> 24) & 0xFF).toString(16).padStart(2, '0')\n return `#${r}${g}${b}${a}`\n}\n\n/**\n * Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.\n * Example: 0xFF0000FF -> \"rgba(255,0,0,1)\"\n */\nexport function color32ToCssRGBA(color: Color32): string {\n const r = color & 0xFF\n const g = (color >>> 8) & 0xFF\n const b = (color >>> 16) & 0xFF\n const a = (color >>> 24) & 0xFF\n\n const alpha = Number((a / 255).toFixed(3))\n\n return `rgba(${r},${g},${b},${alpha})`\n}\n"],"mappings":"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","names":["MaskType"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pixel-data-js",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "packageManager": "pnpm@10.30.0",
6
6
  "description": "JS Pixel and ImageData operations",
7
7
  "author": {
@@ -1,53 +1,86 @@
1
- import type { BlendColor32, Color32 } from '../_types'
1
+ import { type AnyMask, type BlendColor32, type Color32, type ImageDataLike, MaskType } from '../_types'
2
2
  import { sourceOverColor32 } from './blend-modes'
3
3
 
4
4
  export type BlendImageDataOptions = {
5
+ /**
6
+ * The x-coordinate in the destination image where the blend begins.
7
+ * @default 0
8
+ */
5
9
  dx?: number
10
+
11
+ /**
12
+ * The y-coordinate in the destination image where the blend begins.
13
+ * @default 0
14
+ */
6
15
  dy?: number
16
+
17
+ /**
18
+ * The x-coordinate of the top-left corner of the sub-rectangle
19
+ * of the source image to extract.
20
+ * @default 0
21
+ */
7
22
  sx?: number
23
+
24
+ /**
25
+ * The y-coordinate of the top-left corner of the sub-rectangle
26
+ * of the source image to extract.
27
+ * @default 0
28
+ */
8
29
  sy?: number
30
+
31
+ /**
32
+ * The width of the sub-rectangle of the source image to extract.
33
+ * Defaults to the full remaining width of the source.
34
+ */
9
35
  sw?: number
36
+
37
+ /**
38
+ * The height of the sub-rectangle of the source image to extract.
39
+ * Defaults to the full remaining height of the source.
40
+ */
10
41
  sh?: number
42
+
43
+ /**
44
+ * Overall layer opacity, typically ranging from 0.0 (transparent) to 1.0 (opaque).
45
+ * @default 1.0
46
+ */
11
47
  opacity?: number
48
+
49
+ /**
50
+ * Same as opacity but is 0-255 and faster when processing. If Present opacity is ignored.
51
+ * @default undefined
52
+ */
12
53
  alpha?: number
13
- mask?: Uint8Array | null
14
- maskMode?: 'binary' | 'alpha'
54
+
55
+ /**
56
+ * An optional alpha mask buffer.
57
+ * The values in this array (0-255) determine the intensity of the blend
58
+ * at each corresponding pixel.
59
+ */
60
+ mask?: AnyMask | null
61
+
62
+ /**
63
+ * The specific blending function/algorithm to use for pixel math
64
+ * (e.g., Multiply, Screen, Overlay).
65
+ */
15
66
  blendFn?: BlendColor32
16
- }
67
+ };
17
68
 
18
69
  /**
19
70
  * Blits source ImageData into a destination ImageData using 32-bit integer bitwise blending.
20
- * * This function bypasses standard Canvas API limitations by operating directly on
71
+ * This function bypasses standard Canvas API limitations by operating directly on
21
72
  * Uint32Array views. It supports various blend modes, binary/alpha masking, and
22
73
  * automatic clipping of both source and destination bounds.
23
- * * @param dst - The destination ImageData to write into.
24
- * @param src - The source ImageData to read from.
25
- * @param dst - The destination ImageData to write to.
26
- * @param opts - Configuration for the blit operation.
27
- * @param opts.dx - Destination X offset. Defaults to 0.
28
- * @param opts.dy - Destination Y offset. Defaults to 0.
29
- * @param opts.sx - Source X offset. Defaults to 0.
30
- * @param opts.sy - Source Y offset. Defaults to 0.
31
- * @param opts.sw - Width of the source area to blit. Defaults to src.width.
32
- * @param opts.sh - Height of the source area to blit. Defaults to src.height.
33
- * @param opts.opacity - Global strength of the blit (0.0 to 1.0). Defaults to 1.0.
34
- * @param opts.alpha - Global strength of the blit (0 to 255). Overrides 'opacity' if provided.
35
- * @param opts.mask - An optional Uint8Array acting as a stencil or alpha mask.
36
- * Must match source dimensions.
37
- * @param opts.maskMode - 'binary' ignores pixels where mask is 0.
38
- * 'alpha' scales source alpha by mask value (0-255).
39
- * @param opts.blendFn - The math logic used to combine pixels.
40
- * Defaults to `sourceOverColor32`.
41
- * * @example
74
+ * @example
42
75
  * blendImageData32(ctx.getImageData(0,0,100,100), sprite, {
43
- * blendFn: COLOR_32_BLEND_MODES.multiply,
44
- * mask: brushMask,
45
- * maskMode: 'alpha'
76
+ * blendFn: COLOR_32_BLEND_MODES.multiply,
77
+ * mask: brushMask,
78
+ * maskMode: MaskMode.ALPHA
46
79
  * });
47
80
  */
48
81
  export function blendImageData(
49
- dst: ImageData,
50
- src: ImageData,
82
+ dst: ImageDataLike,
83
+ src: ImageDataLike,
51
84
  opts: BlendImageDataOptions,
52
85
  ) {
53
86
  let {
@@ -57,7 +90,6 @@ export function blendImageData(
57
90
  sy = 0,
58
91
  sw = src.width,
59
92
  sh = src.height,
60
- maskMode = 'alpha',
61
93
  opacity = 1,
62
94
  alpha,
63
95
  blendFn = sourceOverColor32,
@@ -105,7 +137,7 @@ export function blendImageData(
105
137
  ? (alpha | 0)
106
138
  : Math.round(opacity * 255)
107
139
 
108
- const maskIsAlpha = maskMode === 'alpha'
140
+ const maskIsAlpha = mask?.type === MaskType.ALPHA
109
141
 
110
142
  for (let iy = 0; iy < actualH; iy++) {
111
143
  const dRow = (iy + dy) * dw
@@ -124,7 +156,7 @@ export function blendImageData(
124
156
  let activeWeight = gAlpha
125
157
 
126
158
  if (mask) {
127
- const m = mask[si]
159
+ const m = mask.data[si]
128
160
  if (m === 0) continue
129
161
  activeWeight = maskIsAlpha ? (m * activeWeight + 128) >> 8 : activeWeight
130
162
  }
@@ -0,0 +1,150 @@
1
+ import type { AlphaMask, BinaryMask, ImageDataLike } from '../_types'
2
+
3
+ export type ApplyMaskOptions = {
4
+ /**
5
+ * The x-coordinate in the destination image where the mask begins.
6
+ * @default 0
7
+ */
8
+ dx?: number
9
+
10
+ /**
11
+ * The y-coordinate in the destination image where the mask begins.
12
+ * @default 0
13
+ */
14
+ dy?: number
15
+
16
+ /**
17
+ * The x-coordinate of the top-left corner of the sub-rectangle
18
+ * of the source image to extract.
19
+ * @default 0
20
+ */
21
+ sx?: number
22
+
23
+ /**
24
+ * The y-coordinate of the top-left corner of the sub-rectangle
25
+ * of the source image to extract.
26
+ * @default 0
27
+ */
28
+ sy?: number
29
+
30
+ /**
31
+ * The width of the sub-rectangle of the source image to extract.
32
+ * Defaults to the full remaining width of the source.
33
+ */
34
+ sw?: number
35
+
36
+ /**
37
+ * The height of the sub-rectangle of the source image to extract.
38
+ * Defaults to the full remaining height of the source.
39
+ */
40
+ sh?: number;
41
+ }
42
+
43
+ /**
44
+ * Applies a binary (on/off) mask to an RGBA buffer.
45
+ * If mask value is 0, pixel becomes transparent.
46
+ */
47
+ export function applyBinaryMask(
48
+ dst: ImageDataLike,
49
+ mask: BinaryMask,
50
+ opts: ApplyMaskOptions = {},
51
+ ) {
52
+ const { width: maskWidth, height: maskHeight } = mask
53
+
54
+ const { dx = 0, dy = 0, sx = 0, sy = 0, sw = maskWidth, sh = maskHeight } = opts
55
+
56
+ // 1. Calculate intersection boundaries
57
+ const x0 = Math.max(0, dx, dx + (0 - sx))
58
+ const y0 = Math.max(0, dy, dy + (0 - sy))
59
+ const x1 = Math.min(dst.width, dx + sw, dx + (maskWidth - sx))
60
+ const y1 = Math.min(dst.height, dy + sh, dy + (maskHeight - sy))
61
+
62
+ if (x1 <= x0 || y1 <= y0) return
63
+
64
+ const { data: dstData, width: dstW } = dst
65
+
66
+ for (let y = y0; y < y1; y++) {
67
+ const maskY = y - dy + sy
68
+ const dstRowOffset = y * dstW * 4
69
+ const maskRowOffset = maskY * maskWidth
70
+
71
+ for (let x = x0; x < x1; x++) {
72
+ const maskX = x - dx + sx
73
+ const mIdx = maskRowOffset + maskX
74
+
75
+ // Binary check: If mask is 0, kill the alpha
76
+ if (mask.data[mIdx] === 0) {
77
+ const aIdx = dstRowOffset + (x * 4) + 3
78
+ dstData[aIdx] = 0
79
+ }
80
+ }
81
+ }
82
+
83
+ return dst
84
+ }
85
+
86
+ /**
87
+ * Applies a smooth alpha mask to an RGBA buffer.
88
+ * Multiplies existing Alpha by (maskValue / 255).
89
+ */
90
+ export function applyAlphaMask(
91
+ dst: ImageData,
92
+ mask: AlphaMask,
93
+ opts: ApplyMaskOptions = {},
94
+ ): void {
95
+ let { dx = 0, dy = 0, sx = 0, sy = 0, sw = mask.width, sh = mask.height } = opts
96
+
97
+ // 1. Clipping Logic
98
+ if (dx < 0) {
99
+ sx -= dx
100
+ sw += dx
101
+ dx = 0
102
+ }
103
+ if (dy < 0) {
104
+ sy -= dy
105
+ sh += dy
106
+ dy = 0
107
+ }
108
+ if (sx < 0) {
109
+ dx -= sx
110
+ sw += sx
111
+ sx = 0
112
+ }
113
+ if (sy < 0) {
114
+ dy -= sy
115
+ sh += sy
116
+ sy = 0
117
+ }
118
+ const actualW = Math.min(sw, dst.width - dx, mask.width - sx)
119
+ const actualH = Math.min(sh, dst.height - dy, mask.height - sy)
120
+
121
+ if (actualW <= 0 || actualH <= 0) return
122
+
123
+ const dData = dst.data
124
+ const mData = mask.data
125
+ const dW = dst.width
126
+ const mW = mask.width
127
+
128
+ for (let y = 0; y < actualH; y++) {
129
+ const dOffset = ((dy + y) * dW + dx) << 2
130
+ const mOffset = (sy + y) * mW + sx
131
+
132
+ for (let x = 0; x < actualW; x++) {
133
+ const mVal = mData[mOffset + x]
134
+
135
+ if (mVal === 255) continue
136
+
137
+ const aIdx = dOffset + (x << 2) + 3
138
+
139
+ // --- BRANCH: Zero Alpha ---
140
+ if (mVal === 0) {
141
+ dData[aIdx] = 0
142
+ continue
143
+ }
144
+
145
+ // To get 101 from 200 * 128, we use the bias (a * m + 257) >> 8
146
+ // 25600 + 257 = 25857. 25857 >> 8 = 101.
147
+ dData[aIdx] = (dData[aIdx] * mVal + 257) >> 8
148
+ }
149
+ }
150
+ }
@@ -1,4 +1,4 @@
1
- import type { Color32, ImageDataLike } from '../_types'
1
+ import type { Color32, ImageDataLike, Rect } from '../_types'
2
2
 
3
3
  export function makeImageDataColor32Adapter(imageData: ImageDataLike) {
4
4
  const data32 = new Uint32Array(imageData.data.buffer)
@@ -34,3 +34,68 @@ export function makeImageDataColor32Adapter(imageData: ImageDataLike) {
34
34
  }
35
35
  }
36
36
 
37
+ export function extractPixelData(
38
+ imageData: ImageDataLike,
39
+ rect: Rect,
40
+ ): Uint8ClampedArray
41
+
42
+ export function extractPixelData(
43
+ imageData: ImageDataLike,
44
+ x: number,
45
+ y: number,
46
+ w: number,
47
+ h: number,
48
+ ): Uint8ClampedArray
49
+ export function extractPixelData(
50
+ imageData: ImageDataLike,
51
+ _x: Rect | number,
52
+ _y?: number,
53
+ _w?: number,
54
+ _h?: number,
55
+ ): Uint8ClampedArray {
56
+ const { x, y, w, h } = typeof _x === 'object'
57
+ ? _x
58
+ : { x: _x, y: _y!, w: _w!, h: _h! }
59
+
60
+ const { width: srcW, height: srcH, data: src } = imageData
61
+ // Safety check for invalid dimensions
62
+ if (w <= 0 || h <= 0) return new Uint8ClampedArray(0)
63
+ const out = new Uint8ClampedArray(w * h * 4)
64
+
65
+ const x0 = Math.max(0, x)
66
+ const y0 = Math.max(0, y)
67
+ const x1 = Math.min(srcW, x + w)
68
+ const y1 = Math.min(srcH, y + h)
69
+
70
+ // If no intersection, return the empty
71
+ if (x1 <= x0 || y1 <= y0) return out
72
+
73
+ for (let row = 0; row < (y1 - y0); row++) {
74
+ // Where to read from the source canvas
75
+ const srcRow = y0 + row
76
+ const srcStart = (srcRow * srcW + x0) * 4
77
+ const rowLen = (x1 - x0) * 4
78
+
79
+ // Where to write into the 'out' patch
80
+ const dstRow = (y0 - y) + row
81
+ const dstCol = (x0 - x)
82
+ const dstStart = (dstRow * w + dstCol) * 4
83
+
84
+ // Perform the high-speed bulk copy
85
+ out.set(src.subarray(srcStart, srcStart + rowLen), dstStart)
86
+ }
87
+
88
+ return out
89
+ }
90
+
91
+ export function copyImageData({ data, width, height }: ImageDataLike): ImageData {
92
+ return new ImageData(data.slice(), width, height)
93
+ }
94
+
95
+ export function copyImageDataLike({ data, width, height }: ImageDataLike): ImageDataLike {
96
+ return {
97
+ data: data.slice(),
98
+ width,
99
+ height,
100
+ }
101
+ }
@@ -5,7 +5,7 @@ export function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedU
5
5
  return btoa(binary) as Base64EncodedUInt8Array
6
6
  }
7
7
 
8
- export function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray {
8
+ export function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray<ArrayBuffer> {
9
9
  const binary = atob(encoded)
10
10
  const bytes = new Uint8ClampedArray(binary.length)
11
11
  for (let i = 0; i < binary.length; i++) {
package/src/_types.ts CHANGED
@@ -2,23 +2,43 @@
2
2
  export type RGBA = { r: number, g: number, b: number, a: number }
3
3
 
4
4
  // A 32-bit integer containing r,g,b,a data
5
- export type Color32 = number & { readonly __brandColor32: unique symbol };
5
+ export type Color32 = number & { readonly __brandColor32: unique symbol }
6
6
 
7
- // ALL values are floats from 0-1
8
- export type RGBAFloat = RGBA & { readonly __brandRGBAFloat: unique symbol }
9
-
10
- export type BlendColor32 = (src: Color32, dst: Color32) => Color32;
7
+ export type BlendColor32 = (src: Color32, dst: Color32) => Color32
11
8
 
12
9
  export type ImageDataLike = {
13
10
  width: number
14
11
  height: number
15
- data: Uint8ClampedArray
12
+ data: Uint8ClampedArray<ArrayBuffer>
16
13
  }
17
14
 
18
15
  export type SerializedImageData = {
19
- width: number,
20
- height: number,
21
- data: string,
16
+ width: number
17
+ height: number
18
+ data: string
22
19
  }
23
20
 
24
21
  export type Base64EncodedUInt8Array = string & { readonly __brandBase64UInt8Array: unique symbol }
22
+
23
+ export type Rect = { x: number; y: number; w: number; h: number }
24
+
25
+ export enum MaskType {
26
+ ALPHA,
27
+ BINARY
28
+ }
29
+
30
+ interface BaseMaskData {
31
+ readonly width: number
32
+ readonly height: number
33
+ readonly data: Uint8Array
34
+ }
35
+
36
+ export interface AlphaMask extends BaseMaskData {
37
+ readonly type: MaskType.ALPHA
38
+ }
39
+
40
+ export interface BinaryMask extends BaseMaskData {
41
+ readonly type: MaskType.BINARY
42
+ }
43
+
44
+ export type AnyMask = AlphaMask | BinaryMask
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
- export * from './color'
2
- export * from './ImageData/serialization'
3
- export * from './ImageData/blit'
4
1
  export * from './ImageData/blend-modes'
2
+ export * from './ImageData/blit'
3
+ export * from './ImageData/mask'
5
4
  export * from './ImageData/read-write-pixels'
5
+ export * from './ImageData/serialization'
6
+ export * from './_types'
7
+ export * from './color'