@forgeax/engine-image 0.1.27 → 0.1.29
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.
- package/README.md +12 -2
- package/dist/__tests__/cube-parser.unit.test.d.ts +2 -0
- package/dist/__tests__/cube-parser.unit.test.d.ts.map +1 -0
- package/dist/__tests__/cube-producer.integration.test.d.ts +2 -0
- package/dist/__tests__/cube-producer.integration.test.d.ts.map +1 -0
- package/dist/__tests__/cube-recovery.integration.test.d.ts +2 -0
- package/dist/__tests__/cube-recovery.integration.test.d.ts.map +1 -0
- package/dist/decode-image-from-file.d.ts +1 -1
- package/dist/decode-image-from-file.mjs.map +1 -1
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/hdr-decoder.mjs.map +1 -1
- package/dist/image-importer.d.ts +2 -0
- package/dist/image-importer.d.ts.map +1 -1
- package/dist/image-importer.mjs +286 -2
- package/dist/image-importer.mjs.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/lut/cube-parser.d.ts +18 -0
- package/dist/lut/cube-parser.d.ts.map +1 -0
- package/dist/lut/cube-producer.d.ts +17 -0
- package/dist/lut/cube-producer.d.ts.map +1 -0
- package/dist/parse-image.mjs.map +1 -1
- package/dist/to-asset-pack.d.ts +1 -1
- package/package.json +6 -6
- package/src/__tests__/cube-parser.unit.test.ts +84 -0
- package/src/__tests__/cube-producer.integration.test.ts +94 -0
- package/src/__tests__/cube-recovery.integration.test.ts +52 -0
- package/src/__tests__/errors.test-d.ts +2 -2
- package/src/__tests__/image-importer-topology.unit.test.ts +27 -0
- package/src/__tests__/image.unit.test.ts +1 -1
- package/src/decode-image-from-file.ts +1 -1
- package/src/errors.ts +35 -0
- package/src/image-importer.ts +13 -1
- package/src/lut/cube-parser.ts +185 -0
- package/src/lut/cube-producer.ts +135 -0
- package/src/to-asset-pack.ts +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/hdr-decoder.ts","../src/image-decoder-browser.ts","../src/image-decoder-node.ts","../src/to-asset-pack.ts","../src/pixel-surface.ts","../src/source-key.ts","../src/sub-asset-key.ts","../src/reimport-reuse-meta.ts","../src/runtime/asset-decoders.ts"],"names":["invalid","err","ok"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ImageErrorCode, ImageErrorDetailFor, ImageErrorFor } from '@forgeax/engine-types';\nimport { IMAGE_ERROR_HINTS } from '@forgeax/engine-types';\n\n// Per-code .expected string literals SSOT.\n// Mirrors ASSET_ERROR_HINTS / IMAGE_ERROR_HINTS shape (charter P5 consistent\n// abstraction). AI users surface .expected when a structured error needs to\n// describe the precondition that was violated.\nconst IMAGE_ERROR_EXPECTED: Readonly<Record<ImageErrorCode, string>> = {\n 'image-decode-failed': 'PNG / JPG byte stream decodes successfully',\n 'image-format-unsupported':\n \"mime is one of ['image/png', 'image/jpeg']; uploadTexture format <-> colorSpace family agrees\",\n 'image-dimension-out-of-bounds':\n 'width and height fall under device caps maxTextureDimension2D (or 16384 hard cap)',\n 'image-meta-missing':\n \"<source>.meta.json sidecar (importer: 'image') exists in the same directory\",\n 'image-hdr-decode-failed': 'Radiance RGBE header is valid and pixel data decodes successfully',\n // feat-20260521-sprite-atlas-animation M1 T-03 — vite-plugin-image atlas\n // hook .expected literals (plan-strategy section 2 D-2 + AC-10 a/b/c).\n // ImageErrorImpl construction path is unchanged: the new atlas-* errors\n // flow through `new ImageErrorImpl({ code: 'atlas-...', ...detail })` and\n // pick the .expected string up from this Record at construction time\n // (charter P3 explicit failure SSOT — AI users surface .expected next to\n // .hint after switch (err.code) without parsing the message).\n 'atlas-empty-input': 'images.length >= 1',\n 'atlas-size-exceeded':\n 'image width x height <= maxAtlasSize^2 and each image fits in the atlas footprint',\n 'atlas-region-mismatch': 'sum(regions[i].w x regions[i].h) <= atlasWidth x atlasHeight',\n 'image-surface-invalid':\n 'PixelSurface dimensions and authoring inputs satisfy the RGBA8 contract',\n};\n\n/** Runtime implementation of the correlated `ImageErrorFor<C>` envelope. */\nexport class ImageErrorImpl<C extends ImageErrorCode = ImageErrorCode>\n extends Error\n implements ImageErrorFor<C>\n{\n readonly code: C;\n readonly expected: string;\n readonly hint: string;\n readonly detail: ImageErrorDetailFor<C>;\n\n constructor(detail: ImageErrorDetailFor<C>) {\n const code = detail.code;\n const expected = IMAGE_ERROR_EXPECTED[code];\n const hint = IMAGE_ERROR_HINTS[code];\n super(`[ImageError ${code}] expected: ${expected}; hint: ${hint}`);\n this.name = 'ImageError';\n this.code = code;\n this.expected = expected;\n this.hint = hint;\n this.detail = detail;\n }\n}\n\n/** Construct a structured image error while preserving its code/detail pair. */\nexport function imageError<C extends ImageErrorCode>(\n detail: ImageErrorDetailFor<C>,\n): ImageErrorFor<C> {\n return new ImageErrorImpl<C>(detail);\n}\n\nexport { IMAGE_ERROR_EXPECTED };\n","import type { ImageError } from '@forgeax/engine-types';\nimport { imageError } from './errors.js';\nimport type { Result } from './result.js';\nimport { err, ok } from './result.js';\n\n/**\n * Decoded HDR image POD.\n *\n * `data` is an interleaved RGBA Float32Array (4 floats per pixel, row-major).\n * Color space is always linear (RGBE is radiance).\n */\nexport interface HdrDecoded {\n readonly width: number;\n readonly height: number;\n readonly data: Float32Array;\n}\n\n// ASCII code points\nconst LF = 0x0a;\n\n/**\n * Decode a Radiance RGBE (.hdr) byte stream into linear RGBA float pixels.\n *\n * Steps per research F-6:\n * 1. Parse ASCII header: magic (#?RADIANCE), FORMAT=32-bit_rle_rgbe,\n * resolution line (-Y height +X width).\n * 2. Decode per-scanline new-RLE pixel data: 4 independent channel runs.\n * 3. Convert RGBE to float: if E==0 -> 0; else f=2^(E-136), R=(Rm+0.5)*f.\n * 4. Output as interleaved RGBA Float32Array (alpha=1.0).\n *\n * Old-RLE scanlines (prefix not matching 0x02,0x02,hi,lo with correct width)\n * return image-hdr-decode-failed.\n */\nexport function decodeHdr(bytes: Uint8Array): Result<HdrDecoded, ImageError> {\n let pos = 0;\n\n // Step 1a: magic -- must start with \"#?RADIANCE\" or \"#?RGBE\"\n if (bytes.length < 11) {\n return err(hdrDecodeError('file too short for Radiance HDR header'));\n }\n const magic = String.fromCharCode(...bytes.subarray(0, 11));\n if (magic !== '#?RADIANCE\\n' && magic !== '#?RGBE\\n') {\n return err(\n hdrDecodeError('missing or invalid Radiance HDR magic; expected #?RADIANCE or #?RGBE'),\n );\n }\n pos = 11;\n\n // Step 1b: key=value lines until empty line\n let formatFound = false;\n while (pos < bytes.length) {\n const lineEnd = findByte(bytes, LF, pos);\n if (lineEnd === -1) return err(hdrDecodeError('header truncated before empty line'));\n const line = asciiSubstring(bytes, pos, lineEnd);\n pos = lineEnd + 1;\n if (line === '') break;\n if (line.startsWith('FORMAT=')) {\n const val = line.slice(7);\n if (val === '32-bit_rle_rgbe') {\n formatFound = true;\n }\n }\n }\n if (!formatFound) {\n return err(hdrDecodeError('missing or unsupported FORMAT; expected FORMAT=32-bit_rle_rgbe'));\n }\n\n // Step 1c: resolution line (-Y height +X width)\n if (pos >= bytes.length) return err(hdrDecodeError('missing resolution line'));\n const resLineEnd = findByte(bytes, LF, pos);\n if (resLineEnd === -1) return err(hdrDecodeError('resolution line truncated'));\n const resLine = asciiSubstring(bytes, pos, resLineEnd);\n pos = resLineEnd + 1;\n const resMatch = /^-Y\\s+(\\d+)\\s+\\+X\\s+(\\d+)\\s*$/.exec(resLine);\n if (resMatch === null) {\n return err(\n hdrDecodeError(`unexpected resolution line format: \"${resLine}\"; expected \"-Y H +X W\"`),\n );\n }\n const height = Number(resMatch[1]);\n const width = Number(resMatch[2]);\n if (height === undefined || width === undefined || height <= 0 || width <= 0) {\n return err(hdrDecodeError(`invalid dimensions: ${width}x${height}`));\n }\n\n // Step 2: decode per-scanline new-RLE pixel data\n const pixelCount = width * height;\n const rgbe = new Uint8Array(pixelCount * 4);\n const scanlineBytes = width * 4;\n\n for (let y = 0; y < height; y++) {\n if (pos + 4 > bytes.length) {\n return err(hdrDecodeError(`truncated at scanline ${y}: expected 4-byte prefix`));\n }\n const p0 = bytes[pos];\n const p1 = bytes[pos + 1];\n const pHi = bytes[pos + 2];\n const pLo = bytes[pos + 3];\n if (p0 === undefined || p1 === undefined || pHi === undefined || pLo === undefined) {\n return err(hdrDecodeError(`truncated at scanline ${y}`));\n }\n // biome-ignore lint/style/noNonNullAssertion: checked above\n const prefixWidth = (pHi! << 8) | pLo!;\n\n if (p0 === 0x02 && p1 === 0x02 && prefixWidth === width && width >= 8 && width <= 32767) {\n // New-RLE: 4 channels independently RLE-encoded, then interleaved\n const decoded = decodeNewRleScanline(bytes, pos + 4, width);\n if (decoded === null) {\n return err(hdrDecodeError(`RLE decode failed at scanline ${y}`));\n }\n // Copy into rgbe interleaved buffer\n const base = y * scanlineBytes;\n const channels = [decoded[0], decoded[1], decoded[2], decoded[3]];\n for (let x = 0; x < width; x++) {\n for (let ch = 0; ch < 4; ch++) {\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n rgbe[base + x * 4 + ch] = channels[ch]![x]!;\n }\n }\n // Advance pos past the 4-byte prefix + the total RLE bytes consumed\n pos += 4 + decoded.totalBytes;\n } else {\n // Old-RLE: not supported\n return err(\n hdrDecodeError(\n 'old-RLE format is not supported; only new-RLE (32-bit_rle_rgbe) is accepted',\n ),\n );\n }\n }\n\n // Step 3: RGBE to float\n const out = new Float32Array(pixelCount * 4);\n for (let i = 0; i < pixelCount; i++) {\n const base = i * 4;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const r = rgbe[base]!;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const g = rgbe[base + 1]!;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const b = rgbe[base + 2]!;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const e = rgbe[base + 3]!;\n out[base + 3] = 1.0;\n\n if (e === 0) {\n out[base] = 0;\n out[base + 1] = 0;\n out[base + 2] = 0;\n } else {\n // f = 2^(E - 136) = ldexp(1.0, E - 128 - 8)\n // Equivalent: ldexp(1.0 / 256.0, E - 128)\n // But simpler: const f = (1.0 / 256.0) * Math.pow(2, e - 128);\n // Using ldexp: Math.pow(2, e - 136)\n const f = 2 ** (e - 136);\n out[base] = (r + 0.5) * f;\n out[base + 1] = (g + 0.5) * f;\n out[base + 2] = (b + 0.5) * f;\n }\n }\n\n return ok({ width, height, data: out });\n}\n\ninterface DecodedScanline {\n /** 4 channels, each width bytes */\n readonly 0: Uint8Array;\n readonly 1: Uint8Array;\n readonly 2: Uint8Array;\n readonly 3: Uint8Array;\n readonly totalBytes: number;\n}\n\n/**\n * Decode one scanline of new-RLE data into 4 channel arrays.\n * Returns null on failure (data underrun).\n */\nfunction decodeNewRleScanline(\n bytes: Uint8Array,\n start: number,\n width: number,\n): DecodedScanline | null {\n const channels: Uint8Array[] = [];\n let cursor = start;\n\n for (let ch = 0; ch < 4; ch++) {\n const data = new Uint8Array(width);\n let x = 0;\n while (x < width) {\n if (cursor >= bytes.length) return null;\n // biome-ignore lint/style/noNonNullAssertion: checked above\n const code = bytes[cursor]!;\n cursor++;\n if (code > 128) {\n // RLE run: next byte repeated (code - 128) times\n const count = code - 128;\n if (cursor >= bytes.length || x + count > width) return null;\n // biome-ignore lint/style/noNonNullAssertion: checked above\n const val = bytes[cursor]!;\n cursor++;\n data.fill(val, x, x + count);\n x += count;\n } else {\n // Literal run: next `code` bytes copied verbatim\n if (code === 0) continue;\n if (cursor + code > bytes.length || x + code > width) return null;\n for (let k = 0; k < code; k++) {\n // biome-ignore lint/style/noNonNullAssertion: checked above\n data[x + k] = bytes[cursor + k]!;\n }\n cursor += code;\n x += code;\n }\n }\n channels.push(data);\n }\n\n const ch0 = channels[0];\n const ch1 = channels[1];\n const ch2 = channels[2];\n const ch3 = channels[3];\n if (ch0 === undefined || ch1 === undefined || ch2 === undefined || ch3 === undefined) {\n return null; // channels should always have 4 entries\n }\n return {\n 0: ch0,\n 1: ch1,\n 2: ch2,\n 3: ch3,\n totalBytes: cursor - start,\n };\n}\n\nfunction findByte(bytes: Uint8Array, target: number, start: number): number {\n for (let i = start; i < bytes.length; i++) {\n if (bytes[i] === target) return i;\n }\n return -1;\n}\n\nfunction asciiSubstring(bytes: Uint8Array, start: number, end: number): string {\n let s = '';\n for (let i = start; i < end; i++) {\n // biome-ignore lint/style/noNonNullAssertion: in bounds\n s += String.fromCharCode(bytes[i]!);\n }\n return s;\n}\n\nfunction hdrDecodeError(reason: string): ImageError {\n return imageError({\n code: 'image-hdr-decode-failed',\n reason,\n });\n}\n","import type { DecodedImage, ImageColorSpace, ImageError } from '@forgeax/engine-types';\nimport { imageError } from './errors.js';\nimport type { Result } from './result.js';\nimport { err, ok } from './result.js';\n\n/**\n * Browser image decoder (plan-strategy section 3.3 + section 2.4 D Open Q-3).\n *\n * Uses `createImageBitmap(blob, { colorSpaceConversion: 'none' })` to keep\n * the bytes in their authored color space (sRGB / linear); the runtime\n * uploadTexture entry asserts `format <-> colorSpace` consistency at GPU\n * upload time (plan-strategy section 2.5 D Open Q-4 (c)).\n *\n * Pixel readback uses an offscreen 2D canvas + `getImageData`; AI users\n * never call this directly -- it is the browser-mode counterpart of\n * `parseImage` (Node-mode upng / jpeg-js path) consumed by future M2b\n * vite-plugin overlay paths and direct browser-mode loaders. The Node\n * decoder remains the SSOT for parseImage; this file is the browser\n * fallback when running inside vitest browser mode or the Vite dev server.\n */\nexport async function decodeImageInBrowser(\n bytes: Uint8Array,\n mime: 'image/png' | 'image/jpeg',\n opts: { colorSpace?: ImageColorSpace; mipmap?: boolean } = {},\n): Promise<Result<DecodedImage, ImageError>> {\n if (typeof createImageBitmap !== 'function') {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: 'createImageBitmap is not available in this environment',\n }),\n );\n }\n\n let bitmap: ImageBitmap;\n try {\n const blob = new Blob([bytes as BlobPart], { type: mime });\n bitmap = await createImageBitmap(blob, {\n colorSpaceConversion: 'none',\n premultiplyAlpha: 'none',\n });\n } catch (e) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: e instanceof Error ? e.message : String(e),\n }),\n );\n }\n\n // Read pixels back via OffscreenCanvas (browser context) -- 2D context is\n // ubiquitous and avoids the WebGPU readback round-trip during disk-side\n // decode (charter P5 producer / consumer split: GPU upload happens inside\n // @forgeax/engine-runtime AssetRegistry.uploadTexture, not here).\n const canvas =\n typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(bitmap.width, bitmap.height)\n : (() => {\n const c = document.createElement('canvas');\n c.width = bitmap.width;\n c.height = bitmap.height;\n return c;\n })();\n const ctx = canvas.getContext('2d') as\n | OffscreenCanvasRenderingContext2D\n | CanvasRenderingContext2D\n | null;\n if (ctx === null) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: 'failed to acquire 2d canvas context for pixel readback',\n }),\n );\n }\n ctx.drawImage(bitmap, 0, 0);\n const data = ctx.getImageData(0, 0, bitmap.width, bitmap.height);\n bitmap.close();\n\n return ok({\n bytes: new Uint8Array(data.data.buffer.slice(0)),\n width: data.width,\n height: data.height,\n mime,\n colorSpace: opts.colorSpace ?? 'srgb',\n mipmap: opts.mipmap ?? true,\n });\n}\n","// Node-only image decoder shell for @forgeax/engine-image.\n//\n// Lazy-loads `upng-js` (PNG) and `jpeg-js` (JPEG) via dynamic imports so the\n// browser bundle tree-shakes both decoders out of the ESM output (the\n// browser path uses createImageBitmap instead). Migrated from the original\n// packages/runtime/src/ home in feat-20260515-learn-render-getting-started\n// M2 T-M2-02 (Open Q-3 option a; plan-strategy section 2.4) -- runtime now\n// only consumes DecodedImage POD and never imports the decoders.\n//\n// The implementation surface is filled out by T-M2-05; this file is staged\n// here in T-M2-02 so the migration touches a single owner directory and the\n// runtime package never ships a half-migrated state.\n\n/**\n * Local widening of the upng-js dynamic-import surface (the package ships\n * without a usable @types entry; the d.ts in image-decoders.d.ts only adds\n * the module declaration). Lists the two functions consumed by the Node\n * decoder path; widening here keeps the import site free of `as any` casts\n * (charter P3 explicit failure -- structural typing > escape hatches).\n */\nexport interface UpngModule {\n decode: (bytes: Uint8Array | ArrayBuffer) => {\n width: number;\n height: number;\n depth: number;\n ctype: number;\n data: Uint8Array;\n frames?: unknown[];\n };\n toRGBA8: (img: { width: number; height: number; data: Uint8Array }) => ArrayBuffer[];\n}\n\n/**\n * Local widening of the jpeg-js dynamic-import surface. Mirrors the subset\n * used by the Node decoder; jpeg-js has @types/jpeg-js but we keep a local\n * shape so the d.ts ambient declaration stays the SSOT for runtime resolution\n * (charter P5 producer / consumer split).\n */\nexport interface JpegModule {\n decode: (\n bytes: Uint8Array | ArrayBuffer,\n opts?: { useTArray?: boolean; formatAsRGBA?: boolean },\n ) => {\n width: number;\n height: number;\n data: Uint8Array;\n };\n}\n\n/**\n * Lazy-load the upng-js Node decoder module. Browser bundlers tree-shake\n * this dynamic import out when only the createImageBitmap path is reached\n * at runtime; tsup external keeps the package name from being inlined.\n */\nexport async function loadUpng(): Promise<UpngModule> {\n const mod = (await import('upng-js')) as { default?: UpngModule } & UpngModule;\n return (mod.default ?? mod) as UpngModule;\n}\n\n/**\n * Lazy-load the jpeg-js Node decoder module. Same tree-shake guarantee as\n * loadUpng -- browser bundles never reach this path.\n */\nexport async function loadJpeg(): Promise<JpegModule> {\n const mod = (await import('jpeg-js')) as { default?: JpegModule } & JpegModule;\n return (mod.default ?? mod) as JpegModule;\n}\n","import type { AssetCompression, DecodedImage, ImageMeta } from '@forgeax/engine-types';\n\n/**\n * External-asset-package envelope shape mirroring the\n * `packages/pack/schema/meta.schema.json` $defs/ExternalAssetPackage.\n * Consumed by `forgeax-engine-remote-asset import` (CLI entry, M3b) +\n * the image importer in M2 build-time path. The internal-text-package\n * variant ($defs/InternalTextPackage) is owned by the gltf-loader feat.\n */\nexport interface ExternalAssetPackage {\n readonly schemaVersion: string;\n readonly kind: 'external-asset-package';\n readonly importer: 'image';\n readonly source: string;\n readonly importSettings: ImageImportSettings;\n readonly subAssets: readonly ExternalSubAsset[];\n}\n\n/**\n * Free-form image importer settings persisted into `*.meta.json` (importer: 'image')\n * `importSettings`. Matches `ImageMeta` POD field-for-field (charter P5\n * single SSOT) but the open Record shape allows future minor adds (cubemap\n * face / array layer / custom colorSpace) without breaking existing meta\n * files (plan-strategy R5).\n */\nexport interface ImageImportSettings extends Readonly<Record<string, unknown>> {\n readonly colorSpace: ImageMeta['colorSpace'];\n readonly mipmap: ImageMeta['mipmap'];\n readonly addressMode: ImageMeta['addressMode'];\n readonly filterMode: ImageMeta['filterMode'];\n readonly downscaleMaxDimension?: number;\n /**\n * Explicit per-asset compression override (AC-01: importSettings carries\n * compression intent). When omitted, the build-time compression strategy\n * table decides by artifact kind (mesh -> zstd, texture -> none). When set,\n * this value wins over the default table for this asset.\n */\n readonly compression?: AssetCompression;\n}\n\nexport interface ExternalSubAsset {\n readonly guid: string;\n readonly sourceIndex: number;\n readonly kind: string;\n readonly name?: string;\n}\n\n/**\n * Pure function that translates a DecodedImage POD + ImageMeta POD into\n * an external-asset-package envelope ready to be JSON-stringified into a\n * `*.meta.json` sidecar with importer: 'image' (plan-strategy section 3.2 sequence A; AC-13\n * disk schema reuse).\n *\n * The single sub-asset emitted carries:\n * - `guid` -- copied verbatim from `meta.guid` (the producer guarantees\n * the GUID is freshly minted or reused via `reimportReuseMeta`)\n * - `sourceIndex: 0` (single-sub-asset shape; cubemap / array reserved\n * for future feat)\n * - `kind: 'texture'` (closed literal for image-importer emit)\n *\n * Two consecutive calls with identical inputs produce JSON.stringify\n * byte-equal output (AC-16 idempotent reimport).\n */\nexport function toAssetPack(decoded: DecodedImage, meta: ImageMeta): ExternalAssetPackage {\n // decoded.bytes are not embedded in the meta envelope -- the source bytes\n // live next to the sidecar on disk (the runtime reads them via\n // decodeImageFromFile at app load time). We keep the parameter so future\n // cubemap / array-layer feats can derive multi-sub-asset emit from decoded\n // dimensions without breaking the call surface (charter P5 consistent\n // abstraction).\n void decoded;\n\n return {\n schemaVersion: '1.0.0',\n kind: 'external-asset-package',\n importer: 'image',\n source: '',\n importSettings: {\n colorSpace: meta.colorSpace,\n mipmap: meta.mipmap,\n addressMode: meta.addressMode,\n filterMode: meta.filterMode,\n ...(meta.downscaleMaxDimension !== undefined\n ? { downscaleMaxDimension: meta.downscaleMaxDimension }\n : {}),\n },\n subAssets: [\n {\n guid: meta.guid,\n sourceIndex: 0,\n kind: 'texture',\n },\n ],\n };\n}\n","import type {\n DecodedImage,\n ImageColorSpace,\n ImageError,\n ImageErrorDetailFor,\n ImageMeta,\n TextureAsset,\n} from '@forgeax/engine-types';\nimport { imageError } from './errors.js';\nimport { err, ok, type Result } from './result.js';\nimport { type ExternalAssetPackage, toAssetPack } from './to-asset-pack.js';\n\n/** Four finite RGBA8 channels in source order. */\nexport type PixelColor =\n | readonly [number, number, number, number]\n | Readonly<{ r: number; g: number; b: number; a: number }>;\n\n/** Optional source rectangle for a blit; coordinates are rounded and clipped. */\nexport interface PixelSurfaceRect {\n readonly x?: number;\n readonly y?: number;\n readonly width?: number;\n readonly height?: number;\n}\n\nexport interface PixelSurfaceNoiseOptions {\n /** Inclusive lower channel bound, default 0. */\n readonly min?: number;\n /** Inclusive upper channel bound, default 255. */\n readonly max?: number;\n /** Alpha written for every generated pixel, default 255. */\n readonly alpha?: number;\n}\n\nexport interface PixelSurfaceOptions {\n readonly width: number;\n readonly height: number;\n readonly colorSpace?: ImageColorSpace;\n readonly mipmap?: boolean;\n}\n\nexport interface PixelSurface {\n readonly width: number;\n readonly height: number;\n readonly colorSpace: ImageColorSpace;\n readonly mipmap: boolean;\n /** Tight RGBA8 level-zero bytes in row-major order. */\n readonly data: Uint8Array;\n\n setPixel(x: number, y: number, color: PixelColor): Result<void, ImageError>;\n fillRect(\n x: number,\n y: number,\n width: number,\n height: number,\n color: PixelColor,\n ): Result<void, ImageError>;\n fillCircle(cx: number, cy: number, radius: number, color: PixelColor): Result<void, ImageError>;\n blit(\n source: PixelSurface,\n destinationX: number,\n destinationY: number,\n sourceRect?: PixelSurfaceRect,\n ): Result<void, ImageError>;\n /** Fill RGB channels with deterministic seeded noise and set alpha uniformly. */\n fillNoise(seed: number, options?: PixelSurfaceNoiseOptions): Result<void, ImageError>;\n /** Alias kept on the same value for the concise authoring spelling. */\n noise(seed: number, options?: PixelSurfaceNoiseOptions): Result<void, ImageError>;\n\n toDecodedImage(): DecodedImage;\n toTextureAsset(): TextureAsset;\n toAssetPack(meta: ImageMeta): ExternalAssetPackage;\n}\n\ntype SurfaceOperation = ImageErrorDetailFor<'image-surface-invalid'>['operation'];\n\nfunction invalid(\n operation: SurfaceOperation,\n field: string,\n value: string | number,\n expected: string,\n): Result<never, ImageError> {\n return err(\n imageError({\n code: 'image-surface-invalid',\n operation,\n field,\n value,\n expected,\n }),\n );\n}\n\nfunction finiteNumber(\n operation: SurfaceOperation,\n field: string,\n value: number,\n): Result<number, ImageError> {\n if (!Number.isFinite(value)) {\n return invalid(operation, field, String(value), 'a finite number');\n }\n return ok(value);\n}\n\nfunction positiveDimension(\n operation: SurfaceOperation,\n field: string,\n value: number,\n): Result<number, ImageError> {\n if (!Number.isInteger(value) || value <= 0) {\n return invalid(operation, field, value, 'a positive integer');\n }\n return ok(value);\n}\n\nfunction colorChannels(\n operation: SurfaceOperation,\n color: PixelColor,\n): Result<readonly [number, number, number, number], ImageError> {\n const channels = Array.isArray(color)\n ? color\n : color !== null && typeof color === 'object'\n ? [\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).r,\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).g,\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).b,\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).a,\n ]\n : undefined;\n if (channels === undefined || channels.length !== 4) {\n return invalid(operation, 'color', 'malformed', 'four finite RGBA8 channels');\n }\n const normalized: [number, number, number, number] = [0, 0, 0, 0];\n for (let index = 0; index < channels.length; index += 1) {\n const channel = channels[index];\n if (channel === undefined || !Number.isFinite(channel) || channel < 0 || channel > 255) {\n return invalid(\n operation,\n `color[${index}]`,\n channel ?? 'missing',\n 'a finite number in [0, 255]',\n );\n }\n normalized[index] = Math.round(channel);\n }\n return ok(normalized);\n}\n\nfunction rounded(\n operation: SurfaceOperation,\n field: string,\n value: number,\n): Result<number, ImageError> {\n const result = finiteNumber(operation, field, value);\n return result.ok ? ok(Math.round(result.value)) : result;\n}\n\nfunction rectangle(\n operation: 'fill-rect' | 'blit',\n x: number,\n y: number,\n width: number,\n height: number,\n): Result<readonly [number, number, number, number], ImageError> {\n const values: [number, number, number, number] = [x, y, width, height];\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n if (value === undefined || !Number.isFinite(value)) {\n return invalid(\n operation,\n ['x', 'y', 'width', 'height'][index] ?? 'rectangle',\n String(value),\n 'a finite number',\n );\n }\n }\n if (width <= 0 || height <= 0) {\n return invalid(\n operation,\n width <= 0 ? 'width' : 'height',\n width <= 0 ? width : height,\n 'a positive number',\n );\n }\n return ok([Math.round(x), Math.round(y), Math.round(width), Math.round(height)]);\n}\n\nfunction writePixel(\n data: Uint8Array,\n width: number,\n x: number,\n y: number,\n color: ArrayLike<number>,\n): void {\n if (x < 0 || y < 0 || x >= width) return;\n const offset = (y * width + x) * 4;\n data[offset] = color[0] ?? 0;\n data[offset + 1] = color[1] ?? 0;\n data[offset + 2] = color[2] ?? 0;\n data[offset + 3] = color[3] ?? 0;\n}\n\nfunction fillClippedRect(\n data: Uint8Array,\n width: number,\n height: number,\n x: number,\n y: number,\n rectWidth: number,\n rectHeight: number,\n color: readonly number[],\n): void {\n const left = Math.max(0, x);\n const top = Math.max(0, y);\n const right = Math.min(width, x + rectWidth);\n const bottom = Math.min(height, y + rectHeight);\n for (let row = top; row < bottom; row += 1) {\n for (let column = left; column < right; column += 1) {\n writePixel(data, width, column, row, color);\n }\n }\n}\n\nfunction nextRandom(state: number): number {\n // xorshift32 is small, deterministic across runtimes, and has no ambient\n // state. The seed is normalized once by fillNoise, so zero remains valid.\n let value = state >>> 0;\n value ^= value << 13;\n value ^= value >>> 17;\n value ^= value << 5;\n return value >>> 0;\n}\n\nfunction makeSurface(options: PixelSurfaceOptions): Result<PixelSurface, ImageError> {\n const width = positiveDimension('create', 'width', options.width);\n if (!width.ok) return width;\n const height = positiveDimension('create', 'height', options.height);\n if (!height.ok) return height;\n if (\n options.colorSpace !== undefined &&\n options.colorSpace !== 'srgb' &&\n options.colorSpace !== 'linear'\n ) {\n return invalid('create', 'colorSpace', String(options.colorSpace), \"'srgb' or 'linear'\");\n }\n if (options.mipmap !== undefined && typeof options.mipmap !== 'boolean') {\n return invalid('create', 'mipmap', String(options.mipmap), 'a boolean');\n }\n const data = new Uint8Array(width.value * height.value * 4);\n const colorSpace = options.colorSpace ?? 'srgb';\n const mipmap = options.mipmap ?? false;\n\n const surface: PixelSurface = {\n width: width.value,\n height: height.value,\n colorSpace,\n mipmap,\n data,\n setPixel(x, y, color) {\n const px = rounded('set-pixel', 'x', x);\n if (!px.ok) return px;\n const py = rounded('set-pixel', 'y', y);\n if (!py.ok) return py;\n const normalized = colorChannels('set-pixel', color);\n if (!normalized.ok) return normalized;\n writePixel(data, width.value, px.value, py.value, normalized.value);\n return ok(undefined);\n },\n fillRect(x, y, rectWidth, rectHeight, color) {\n const rect = rectangle('fill-rect', x, y, rectWidth, rectHeight);\n if (!rect.ok) return rect;\n const normalized = colorChannels('fill-rect', color);\n if (!normalized.ok) return normalized;\n fillClippedRect(\n data,\n width.value,\n height.value,\n rect.value[0] ?? 0,\n rect.value[1] ?? 0,\n rect.value[2] ?? 0,\n rect.value[3] ?? 0,\n normalized.value,\n );\n return ok(undefined);\n },\n fillCircle(cx, cy, radius, color) {\n const centerX = rounded('fill-circle', 'cx', cx);\n if (!centerX.ok) return centerX;\n const centerY = rounded('fill-circle', 'cy', cy);\n if (!centerY.ok) return centerY;\n const circleRadius = rounded('fill-circle', 'radius', radius);\n if (!circleRadius.ok) return circleRadius;\n if (circleRadius.value <= 0) {\n return invalid('fill-circle', 'radius', circleRadius.value, 'a positive number');\n }\n const normalized = colorChannels('fill-circle', color);\n if (!normalized.ok) return normalized;\n const radiusSquared = circleRadius.value * circleRadius.value;\n const left = Math.max(0, centerX.value - circleRadius.value);\n const right = Math.min(width.value - 1, centerX.value + circleRadius.value);\n const top = Math.max(0, centerY.value - circleRadius.value);\n const bottom = Math.min(height.value - 1, centerY.value + circleRadius.value);\n for (let row = top; row <= bottom; row += 1) {\n for (let column = left; column <= right; column += 1) {\n const dx = column - centerX.value;\n const dy = row - centerY.value;\n if (dx * dx + dy * dy <= radiusSquared)\n writePixel(data, width.value, column, row, normalized.value);\n }\n }\n return ok(undefined);\n },\n blit(source, destinationX, destinationY, sourceRect) {\n const destination = rounded('blit', 'destinationX', destinationX);\n if (!destination.ok) return destination;\n const destinationYResult = rounded('blit', 'destinationY', destinationY);\n if (!destinationYResult.ok) return destinationYResult;\n if (\n source === null ||\n typeof source !== 'object' ||\n !Number.isInteger(source.width) ||\n !Number.isInteger(source.height) ||\n !(source.data instanceof Uint8Array) ||\n source.data.length !== source.width * source.height * 4\n ) {\n return invalid('blit', 'source', 'malformed', 'a valid PixelSurface');\n }\n const rect = rectangle(\n 'blit',\n sourceRect?.x ?? 0,\n sourceRect?.y ?? 0,\n sourceRect?.width ?? source.width,\n sourceRect?.height ?? source.height,\n );\n if (!rect.ok) return rect;\n const sourceX = rect.value[0] ?? 0;\n const sourceY = rect.value[1] ?? 0;\n const sourceWidth = rect.value[2] ?? 0;\n const sourceHeight = rect.value[3] ?? 0;\n const left = Math.max(0, sourceX);\n const top = Math.max(0, sourceY);\n const right = Math.min(source.width, sourceX + sourceWidth);\n const bottom = Math.min(source.height, sourceY + sourceHeight);\n if (right <= left || bottom <= top) return ok(undefined);\n const snapshot = source.data.slice();\n for (let row = top; row < bottom; row += 1) {\n for (let column = left; column < right; column += 1) {\n const targetX = destination.value + column - sourceX;\n const targetY = destinationYResult.value + row - sourceY;\n if (targetX < 0 || targetY < 0 || targetX >= width.value || targetY >= height.value)\n continue;\n const sourceOffset = (row * source.width + column) * 4;\n writePixel(\n data,\n width.value,\n targetX,\n targetY,\n snapshot.subarray(sourceOffset, sourceOffset + 4),\n );\n }\n }\n return ok(undefined);\n },\n fillNoise(seed, noiseOptions) {\n if (!Number.isFinite(seed)) return invalid('noise', 'seed', String(seed), 'a finite number');\n const min = noiseOptions?.min ?? 0;\n const max = noiseOptions?.max ?? 255;\n const alpha = noiseOptions?.alpha ?? 255;\n for (const [field, value] of [\n ['min', min],\n ['max', max],\n ['alpha', alpha],\n ] as const) {\n if (!Number.isFinite(value) || value < 0 || value > 255) {\n return invalid('noise', field, value, 'a finite number in [0, 255]');\n }\n }\n if (min > max) return invalid('noise', 'min', min, 'a value no greater than max');\n let state = Math.trunc(seed) >>> 0 || 0x6d2b79f5;\n const span = max - min;\n for (let index = 0; index < data.length; index += 4) {\n state = nextRandom(state);\n const value = min + (state / 0x100000000) * span;\n const channel = Math.round(value);\n data[index] = channel;\n data[index + 1] = channel;\n data[index + 2] = channel;\n data[index + 3] = Math.round(alpha);\n }\n return ok(undefined);\n },\n noise(seed, noiseOptions) {\n return surface.fillNoise(seed, noiseOptions);\n },\n toDecodedImage() {\n return {\n bytes: data.slice(),\n width: width.value,\n height: height.value,\n mime: 'image/png',\n colorSpace,\n mipmap,\n };\n },\n toTextureAsset() {\n return {\n kind: 'texture',\n shape: {\n viewDimension: '2d',\n extent: { width: width.value, height: height.value },\n },\n format: colorSpace === 'srgb' ? 'rgba8unorm-srgb' : 'rgba8unorm',\n data: data.slice(),\n colorSpace,\n mips: mipmap ? { kind: 'generate' } : { kind: 'none' },\n };\n },\n toAssetPack(meta) {\n return toAssetPack(surface.toDecodedImage(), meta);\n },\n };\n return ok(surface);\n}\n\n/** Create a deterministic, image-owned RGBA8 authoring surface. */\nexport function createPixelSurface(options: PixelSurfaceOptions): Result<PixelSurface, ImageError> {\n return makeSurface(options);\n}\n","/**\n * Derive the semantic identity for an image producer output.\n *\n * The role is the producer-owned identity. Source path and sourceIndex are\n * locators only and must not be folded into this key.\n */\nexport interface ImageSourceKeyLocator {\n readonly sourcePath?: string;\n readonly sourceIndex?: number;\n}\n\nexport type ImageSourceKeyResult =\n | { readonly ok: true; readonly keys: readonly string[] }\n | {\n readonly ok: false;\n readonly code: 'missing-source-key' | 'duplicate-source-key';\n readonly roles: readonly string[];\n };\n\nexport function deriveImageSourceKey(\n role: string,\n _locator?: ImageSourceKeyLocator,\n): string | undefined {\n const normalizedRole = role.trim();\n if (normalizedRole.length === 0) return undefined;\n return `image:${normalizedRole}`;\n}\n\nexport function deriveImageSourceKeys(roles: readonly string[]): ImageSourceKeyResult {\n const keys = roles.map((role) => deriveImageSourceKey(role));\n const missing = roles.filter((_role, index) => keys[index] === undefined);\n if (missing.length > 0) return { ok: false, code: 'missing-source-key', roles: missing };\n\n const seen = new Set<string>();\n for (const key of keys) {\n if (key === undefined) continue;\n if (seen.has(key)) return { ok: false, code: 'duplicate-source-key', roles };\n seen.add(key);\n }\n return { ok: true, keys: keys as string[] };\n}\n","/**\n * Sub-asset-key shape mirroring the in-flight gltf-loader feat (plan-strategy\n * section 2.2 D-4 same-shape). Each sub-asset emitted into a `*.meta.json`\n * sidecar (uniformly `<source>.meta.json` across image / gltf arms,\n * dispatched on top-level `importer` field per feat-20260521) carries a\n * `{kind, name?, indexFallback}` triple so the importer can deterministically\n * match an existing sub-asset across reimports (charter P5 producer/consumer\n * split + AC-14 cross-feat alignment).\n *\n * The image disk schema is currently single-sub-asset; this file collapses\n * to `kind='texture'` + `indexFallback='textures/0'` so the same matching\n * algorithm is reusable when the future cubemap / array-layer feat lands\n * (plan-strategy R5 free-form schema).\n */\n\nexport interface SubAssetKey {\n /** Discriminator literal -- 'texture' for engine-image; 'mesh' / 'material' / 'scene' / 'image' for gltf-loader. */\n readonly kind: string;\n /** Optional human-readable name field; absent when the source has no symbolic identifier. */\n readonly name?: string;\n /** Path-style fallback identifier (e.g. 'images/0' / 'meshes/0'); always non-empty. */\n readonly indexFallback: string;\n}\n\nexport interface SubAssetKeyInput {\n readonly kind: string;\n readonly sourceIndex: number;\n readonly name?: string;\n}\n\n/**\n * Construct a sub-asset key from importer input. The `indexFallback` is\n * synthesised from the kind literal + sourceIndex so two importers\n * (image / gltf-loader) emit identical fallback strings for the same\n * `(kind, sourceIndex)` pair (AC-14).\n */\nexport function subAssetKey(input: SubAssetKeyInput): SubAssetKey {\n const indexFallback = `${input.kind}s/${input.sourceIndex}`;\n if (input.name !== undefined) {\n return { kind: input.kind, name: input.name, indexFallback };\n }\n return { kind: input.kind, indexFallback };\n}\n\n/**\n * Two-phase equality predicate used by reimportReuseMeta for GUID\n * preservation across reimports:\n *\n * Phase 1 -- (kind + name + idx) full match: identical in all three fields\n * Phase 2 -- (kind + idx) match: same kind + indexFallback, name absent or\n * equal (collapses when both sides omit name)\n * Phase 3 -- otherwise: false (importer mints fresh UUIDv7)\n *\n * The two phases are folded into a single boolean predicate here; the\n * reimportReuseMeta caller iterates the existing subAssets list and picks\n * the first hit (deterministic order; matches AC-16 byte-identical\n * reimport).\n */\nexport function subAssetKeyEqual(a: SubAssetKey, b: SubAssetKey): boolean {\n if (a.kind !== b.kind) return false;\n if (a.indexFallback !== b.indexFallback) return false;\n // name comparison: undefined === undefined is fine; if one side has a name\n // and the other does not, the keys are NOT equal (charter P4 explicit\n // failure -- name carries a meaningful identifier when present)\n if (a.name !== b.name) return false;\n return true;\n}\n","import { AssetGuid } from '@forgeax/engine-pack/guid';\nimport type { DecodedImage, ImageColorSpace } from '@forgeax/engine-types';\n\nimport { deriveImageSourceKey } from './source-key.js';\nimport type { SubAssetKey } from './sub-asset-key.js';\nimport { subAssetKey, subAssetKeyEqual } from './sub-asset-key.js';\n\n/**\n * Existing `*.meta.json` (importer: 'image') sub-asset record (free-form sub-shape; the\n * meta.schema.json $defs/subAsset locks `guid` + `sourceIndex` + `kind` only).\n * Image importer treats the open shape as a key-bearing tuple; the gltf-loader\n * feat works on the same shape.\n */\nexport interface ExistingSubAsset {\n readonly guid: string;\n readonly sourceIndex: number;\n readonly kind: string;\n readonly name?: string;\n readonly sourceKey?: string;\n}\n\n/**\n * Existing `*.meta.json` (importer: 'image') package record. Mirrors the\n * meta.schema.json $defs/ExternalAssetPackage shape (research F-9). Only\n * the fields consumed by the reimport reuse algorithm are typed here;\n * importSettings stays a free-form open shape.\n */\nexport interface ExistingExternalAssetPackage {\n readonly schemaVersion: string;\n readonly kind: 'external-asset-package';\n readonly importer: 'image';\n readonly source: string;\n readonly importSettings: Readonly<Record<string, unknown>>;\n readonly subAssets: readonly ExistingSubAsset[];\n}\n\n/**\n * Sub-asset record emitted into the regenerated `*.meta.json` (importer: 'image')\n * `subAssets[]` slot. Shape matches `ExistingSubAsset` so two consecutive\n * reimports of byte-identical bytes produce byte-identical JSON\n * (AC-16 byte-stable reimport guarantee).\n */\nexport interface EmittedSubAsset {\n readonly guid: string;\n readonly sourceIndex: number;\n readonly kind: string;\n readonly name?: string;\n readonly sourceKey?: string;\n}\n\n/**\n * Two-phase reimport reuse algorithm (plan-strategy section 2.2 D-4\n * mirroring the in-flight gltf-loader algorithm).\n *\n * For each fresh sub-asset key the importer would emit, this function:\n * 1. Searches the `existing.subAssets[]` for a `subAssetKeyEqual` hit.\n * 2. On hit -- reuses the existing GUID byte-for-byte.\n * 3. On miss -- mints a fresh UUIDv7 via `AssetGuid.random()`.\n *\n * Image disk schema currently emits a single `kind='texture'` sub-asset per\n * source (cubemap face / array layer reserved for future feat); the\n * algorithm scales to multi-sub-asset sources without modification (charter\n * P5 consistent abstraction).\n *\n * Returns the regenerated `subAssets[]` array. The caller (toAssetPack /\n * runAssetImport) splices it into the `*.meta.json` (importer: 'image') envelope.\n */\nexport function reimportReuseMeta(\n decoded: DecodedImage,\n existing: ExistingExternalAssetPackage | undefined,\n): readonly EmittedSubAsset[] {\n // Image disk schema is single-sub-asset; the future multi-asset path\n // iterates a producer-side list of sub-asset keys instead of a single one.\n const freshKeys: readonly SubAssetKey[] = [subAssetKey({ kind: 'texture', sourceIndex: 0 })];\n\n const out: EmittedSubAsset[] = [];\n const sourceKey = deriveImageSourceKey('texture');\n\n for (let i = 0; i < freshKeys.length; i++) {\n const fresh = freshKeys[i];\n if (fresh === undefined) continue;\n\n let reuseGuid: string | undefined;\n if (existing !== undefined) {\n for (const candidate of existing.subAssets) {\n if (sourceKey !== undefined && candidate.sourceKey === sourceKey) {\n reuseGuid = candidate.guid;\n break;\n }\n const candidateKey = subAssetKey({\n kind: candidate.kind,\n sourceIndex: candidate.sourceIndex,\n ...(candidate.name !== undefined ? { name: candidate.name } : {}),\n });\n if (subAssetKeyEqual(fresh, candidateKey)) {\n reuseGuid = candidate.guid;\n break;\n }\n }\n }\n\n const guid = reuseGuid ?? AssetGuid.format(AssetGuid.random());\n const emit: EmittedSubAsset =\n fresh.name !== undefined\n ? {\n guid,\n sourceIndex: i,\n kind: fresh.kind,\n name: fresh.name,\n ...(sourceKey === undefined ? {} : { sourceKey }),\n }\n : {\n guid,\n sourceIndex: i,\n kind: fresh.kind,\n ...(sourceKey === undefined ? {} : { sourceKey }),\n };\n out.push(emit);\n }\n\n // Ensure decoded ref is consumed (decoded.bytes is the producer signal that\n // a sub-asset exists; without bytes there is nothing to emit). The\n // signature keeps `decoded` in the public surface so the future cubemap /\n // array-layer feat can derive its multi-sub-asset list from `decoded`\n // metadata without a breaking change (charter P5 consistent abstraction).\n void decoded;\n\n return out;\n}\n\n/**\n * HDR colorSpace validator for *.image.meta.json sidecar importSettings\n * (plan-strategy D-8 -- HDR files must use colorSpace=linear).\n *\n * Extension is matched case-insensitively against the HDR set\n * (`.hdr`, `.exr`). When the extension belongs to the HDR set:\n * - `colorSpace='linear'` -> ok, returns `'linear'`\n * - any other colorSpace -> structured rejection with expected/actual\n *\n * Non-HDR extensions pass through the colorSpace unchanged. Downstream\n * callers (decodeImageFromFile, parseImage) invoke this at the\n * sidecar-read entry to fail-fast when an .hdr meta carries srgb.\n */\nexport function validateColorSpaceForHdr(\n ext: string,\n colorSpace: ImageColorSpace,\n): { ok: true; value: ImageColorSpace } | { ok: false; expected: string; actual: string } {\n const lower = ext.toLowerCase();\n if (lower === '.hdr' || lower === '.exr') {\n if (colorSpace !== 'linear') {\n return {\n ok: false,\n expected: 'colorSpace=linear for HDR source files',\n actual: `colorSpace=${colorSpace}`,\n };\n }\n return { ok: true, value: 'linear' };\n }\n return { ok: true, value: colorSpace };\n}\n","import { parseKtx2, transcodeBasis, transcodeKtx2 } from '@forgeax/engine-codec';\nimport {\n type AssetDecoderContribution,\n type AssetDecoderInput,\n type AssetDecoderResult,\n type AssetKind,\n type AssetLoadError,\n type EquirectAsset,\n err,\n ok,\n type TextureAsset,\n} from '@forgeax/engine-types';\n\nfunction invalid(guid: string, expected: string, reason: string) {\n return err<AssetLoadError>({\n code: 'asset-package-invalid',\n expected,\n hint: 'recook the image asset and publish its complete device-neutral payload',\n detail: { guid, reason },\n });\n}\n\nfunction validDimensions(width: number, height: number): boolean {\n return Number.isSafeInteger(width) && width > 0 && Number.isSafeInteger(height) && height > 0;\n}\n\nfunction imageBytes(value: unknown): Uint8Array | Uint8ClampedArray | undefined {\n if (value instanceof Uint8Array || value instanceof Uint8ClampedArray) return value;\n if (!Array.isArray(value)) return undefined;\n if (!value.every((item) => Number.isInteger(item) && item >= 0 && item <= 255)) {\n return undefined;\n }\n return Uint8Array.from(value);\n}\n\nfunction compressedImageTarget(colorSpace: TextureAsset['colorSpace']): TextureAsset['format'] {\n return colorSpace === 'srgb' ? 'rgba8unorm-srgb' : 'rgba8unorm';\n}\n\nfunction validTextureSurface(\n value: unknown,\n): value is Pick<TextureAsset, 'shape' | 'format' | 'data' | 'colorSpace' | 'mips'> {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as Partial<TextureAsset>;\n const shape = candidate.shape;\n const extent = shape?.viewDimension === '2d' ? shape.extent : undefined;\n const mips = candidate.mips;\n return (\n extent !== undefined &&\n validDimensions(extent.width, extent.height) &&\n mips !== undefined &&\n (mips.kind === 'none' ||\n mips.kind === 'generate' ||\n (mips.kind === 'packed' && Number.isSafeInteger(mips.levelCount) && mips.levelCount > 0)) &&\n typeof candidate.format === 'string' &&\n imageBytes(candidate.data) !== undefined &&\n (candidate.colorSpace === 'srgb' || candidate.colorSpace === 'linear')\n );\n}\n\nfunction validEquirectSurface(\n value: unknown,\n): value is Pick<EquirectAsset, 'width' | 'height' | 'format' | 'data' | 'colorSpace'> {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as Partial<EquirectAsset>;\n return (\n validDimensions(candidate.width ?? 0, candidate.height ?? 0) &&\n typeof candidate.format === 'string' &&\n imageBytes(candidate.data) !== undefined &&\n (candidate.colorSpace === 'srgb' || candidate.colorSpace === 'linear')\n );\n}\n\nasync function readImageSurface<P extends TextureAsset | EquirectAsset>(\n input: AssetDecoderInput<P>,\n kind: P['kind'],\n expected: string,\n): Promise<AssetDecoderResult<P>> {\n const { envelope, artifacts } = input;\n const payload = envelope.payload as unknown;\n if (\n payload === null ||\n typeof payload !== 'object' ||\n (payload as { kind?: unknown }).kind !== kind\n ) {\n return invalid(envelope.guid, expected, `${kind} owner validation failed`);\n }\n\n const body = envelope.artifacts.body ?? envelope.artifacts.atlas;\n let data = imageBytes((payload as { data?: unknown }).data);\n if (body !== undefined) {\n const read = await artifacts.read(body);\n if (!read.ok) return err(read.error);\n const bytes = read.value;\n data = bytes;\n\n // Pack v2 keeps the authored TextureAsset format device-neutral while the\n // artifact may carry a Basis KTX2/raw-Basis delivery codec. The new\n // AssetDecoder seam has no device-capability input, so its safe baseline is\n // an uncompressed RGBA target; the renderer can upload that POD on every\n // backend without interpreting container bytes as pixels.\n if (body.assetCodec?.name === 'basis' && body.assetCodec.container !== undefined) {\n const candidate = payload as Partial<TextureAsset>;\n if (candidate.colorSpace !== 'srgb' && candidate.colorSpace !== 'linear') {\n return invalid(envelope.guid, expected, `${kind} color space is invalid`);\n }\n const target = compressedImageTarget(candidate.colorSpace);\n const transcoded =\n body.assetCodec.container === 'ktx2'\n ? await parseKtx2(bytes).then((parsed) =>\n parsed.ok ? transcodeKtx2(parsed.value, target) : parsed,\n )\n : await transcodeBasis(bytes, target);\n if (!transcoded.ok) {\n return invalid(envelope.guid, expected, `codec:${transcoded.error.code}`);\n }\n const mip = transcoded.value.mips[0];\n if (mip === undefined) return invalid(envelope.guid, expected, 'codec:base-mip-missing');\n data = mip.data;\n return readDecodedSurface(\n envelope.guid,\n expected,\n kind,\n kind === 'texture'\n ? {\n ...payload,\n shape: { viewDimension: '2d', extent: { width: mip.width, height: mip.height } },\n format: target,\n data,\n mips: { kind: 'none' },\n }\n : { ...payload, width: mip.width, height: mip.height, format: target, data },\n );\n }\n }\n\n return readDecodedSurface(envelope.guid, expected, kind, {\n ...payload,\n ...(data === undefined ? {} : { data }),\n });\n}\n\nfunction readDecodedSurface<P extends TextureAsset | EquirectAsset>(\n guid: string,\n expected: string,\n kind: P['kind'],\n candidate: unknown,\n): ReturnType<typeof ok<P>> | ReturnType<typeof err<AssetLoadError>> {\n if (kind === 'texture' ? !validTextureSurface(candidate) : !validEquirectSurface(candidate)) {\n return invalid(guid, expected, 'image owner validation failed');\n }\n return ok(candidate as P);\n}\n\nexport const textureContribution: AssetDecoderContribution<TextureAsset, 'texture'> = {\n kind: { kind: 'texture' } as AssetKind<TextureAsset, 'texture'>,\n consumer: 'Image/Render DeviceScope',\n decoder: {\n async decode(input) {\n return readImageSurface(\n input,\n 'texture',\n 'a texture payload with dimensions, format, color space, and bytes',\n );\n },\n },\n};\n\nexport const equirectContribution: AssetDecoderContribution<EquirectAsset, 'equirect'> = {\n kind: { kind: 'equirect' } as AssetKind<EquirectAsset, 'equirect'>,\n consumer: 'Image/Render DeviceScope',\n decoder: {\n async decode(input) {\n return readImageSurface(\n input,\n 'equirect',\n 'an equirect payload with dimensions, format, color space, and bytes',\n );\n },\n },\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/hdr-decoder.ts","../src/image-decoder-browser.ts","../src/image-decoder-node.ts","../src/to-asset-pack.ts","../src/pixel-surface.ts","../src/source-key.ts","../src/sub-asset-key.ts","../src/reimport-reuse-meta.ts","../src/runtime/asset-decoders.ts"],"names":["invalid","err","ok"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ImageErrorCode, ImageErrorDetailFor, ImageErrorFor } from '@forgeax/engine-types';\nimport { IMAGE_ERROR_HINTS } from '@forgeax/engine-types';\n\n// Per-code .expected string literals SSOT.\n// Mirrors ASSET_ERROR_HINTS / IMAGE_ERROR_HINTS shape (charter P5 consistent\n// abstraction). AI users surface .expected when a structured error needs to\n// describe the precondition that was violated.\nconst IMAGE_ERROR_EXPECTED: Readonly<Record<ImageErrorCode, string>> = {\n 'image-decode-failed': 'PNG / JPG byte stream decodes successfully',\n 'image-format-unsupported':\n \"mime is one of ['image/png', 'image/jpeg']; uploadTexture format <-> colorSpace family agrees\",\n 'image-dimension-out-of-bounds':\n 'width and height fall under device caps maxTextureDimension2D (or 16384 hard cap)',\n 'image-meta-missing':\n \"<source>.meta.json sidecar (importer: 'image') exists in the same directory\",\n 'image-hdr-decode-failed': 'Radiance RGBE header is valid and pixel data decodes successfully',\n // feat-20260521-sprite-atlas-animation M1 T-03 — vite-plugin-image atlas\n // hook .expected literals (plan-strategy section 2 D-2 + AC-10 a/b/c).\n // ImageErrorImpl construction path is unchanged: the new atlas-* errors\n // flow through `new ImageErrorImpl({ code: 'atlas-...', ...detail })` and\n // pick the .expected string up from this Record at construction time\n // (charter P3 explicit failure SSOT — AI users surface .expected next to\n // .hint after switch (err.code) without parsing the message).\n 'atlas-empty-input': 'images.length >= 1',\n 'atlas-size-exceeded':\n 'image width x height <= maxAtlasSize^2 and each image fits in the atlas footprint',\n 'atlas-region-mismatch': 'sum(regions[i].w x regions[i].h) <= atlasWidth x atlasHeight',\n 'image-surface-invalid':\n 'PixelSurface dimensions and authoring inputs satisfy the RGBA8 contract',\n};\n\n/** Runtime implementation of the correlated `ImageErrorFor<C>` envelope. */\nexport class ImageErrorImpl<C extends ImageErrorCode = ImageErrorCode>\n extends Error\n implements ImageErrorFor<C>\n{\n readonly code: C;\n readonly expected: string;\n readonly hint: string;\n readonly detail: ImageErrorDetailFor<C>;\n\n constructor(detail: ImageErrorDetailFor<C>) {\n const code = detail.code;\n const expected = IMAGE_ERROR_EXPECTED[code];\n const hint = IMAGE_ERROR_HINTS[code];\n super(`[ImageError ${code}] expected: ${expected}; hint: ${hint}`);\n this.name = 'ImageError';\n this.code = code;\n this.expected = expected;\n this.hint = hint;\n this.detail = detail;\n }\n}\n\n/** Construct a structured image error while preserving its code/detail pair. */\nexport function imageError<C extends ImageErrorCode>(\n detail: ImageErrorDetailFor<C>,\n): ImageErrorFor<C> {\n return new ImageErrorImpl<C>(detail);\n}\n\nexport { IMAGE_ERROR_EXPECTED };\n\nexport type CubeParserErrorCode =\n | 'cube-header-invalid'\n | 'cube-size-invalid'\n | 'cube-domain-invalid'\n | 'cube-row-invalid'\n | 'cube-data-count-invalid';\n\nexport interface CubeParserError {\n readonly code: CubeParserErrorCode;\n readonly expected: string;\n readonly hint: string;\n readonly detail: {\n readonly sourceKey: string;\n readonly line?: number;\n readonly field: string;\n readonly actual: unknown;\n };\n}\n\nexport function cubeParserError(\n code: CubeParserErrorCode,\n sourceKey: string,\n field: string,\n actual: unknown,\n line?: number,\n): CubeParserError {\n const lineDetail = line === undefined ? '' : ` at line ${line}`;\n return {\n code,\n expected: `valid .cube source with LUT_3D_SIZE, DOMAIN_MIN/MAX, and exactly N^3 RGB rows${lineDetail}`,\n hint: `repair field ${field} in sourceKey ${sourceKey} and re-run the Node image producer`,\n detail: { sourceKey, field, actual, ...(line === undefined ? {} : { line }) },\n };\n}\n","import type { ImageError } from '@forgeax/engine-types';\nimport { imageError } from './errors.js';\nimport type { Result } from './result.js';\nimport { err, ok } from './result.js';\n\n/**\n * Decoded HDR image POD.\n *\n * `data` is an interleaved RGBA Float32Array (4 floats per pixel, row-major).\n * Color space is always linear (RGBE is radiance).\n */\nexport interface HdrDecoded {\n readonly width: number;\n readonly height: number;\n readonly data: Float32Array;\n}\n\n// ASCII code points\nconst LF = 0x0a;\n\n/**\n * Decode a Radiance RGBE (.hdr) byte stream into linear RGBA float pixels.\n *\n * Steps per research F-6:\n * 1. Parse ASCII header: magic (#?RADIANCE), FORMAT=32-bit_rle_rgbe,\n * resolution line (-Y height +X width).\n * 2. Decode per-scanline new-RLE pixel data: 4 independent channel runs.\n * 3. Convert RGBE to float: if E==0 -> 0; else f=2^(E-136), R=(Rm+0.5)*f.\n * 4. Output as interleaved RGBA Float32Array (alpha=1.0).\n *\n * Old-RLE scanlines (prefix not matching 0x02,0x02,hi,lo with correct width)\n * return image-hdr-decode-failed.\n */\nexport function decodeHdr(bytes: Uint8Array): Result<HdrDecoded, ImageError> {\n let pos = 0;\n\n // Step 1a: magic -- must start with \"#?RADIANCE\" or \"#?RGBE\"\n if (bytes.length < 11) {\n return err(hdrDecodeError('file too short for Radiance HDR header'));\n }\n const magic = String.fromCharCode(...bytes.subarray(0, 11));\n if (magic !== '#?RADIANCE\\n' && magic !== '#?RGBE\\n') {\n return err(\n hdrDecodeError('missing or invalid Radiance HDR magic; expected #?RADIANCE or #?RGBE'),\n );\n }\n pos = 11;\n\n // Step 1b: key=value lines until empty line\n let formatFound = false;\n while (pos < bytes.length) {\n const lineEnd = findByte(bytes, LF, pos);\n if (lineEnd === -1) return err(hdrDecodeError('header truncated before empty line'));\n const line = asciiSubstring(bytes, pos, lineEnd);\n pos = lineEnd + 1;\n if (line === '') break;\n if (line.startsWith('FORMAT=')) {\n const val = line.slice(7);\n if (val === '32-bit_rle_rgbe') {\n formatFound = true;\n }\n }\n }\n if (!formatFound) {\n return err(hdrDecodeError('missing or unsupported FORMAT; expected FORMAT=32-bit_rle_rgbe'));\n }\n\n // Step 1c: resolution line (-Y height +X width)\n if (pos >= bytes.length) return err(hdrDecodeError('missing resolution line'));\n const resLineEnd = findByte(bytes, LF, pos);\n if (resLineEnd === -1) return err(hdrDecodeError('resolution line truncated'));\n const resLine = asciiSubstring(bytes, pos, resLineEnd);\n pos = resLineEnd + 1;\n const resMatch = /^-Y\\s+(\\d+)\\s+\\+X\\s+(\\d+)\\s*$/.exec(resLine);\n if (resMatch === null) {\n return err(\n hdrDecodeError(`unexpected resolution line format: \"${resLine}\"; expected \"-Y H +X W\"`),\n );\n }\n const height = Number(resMatch[1]);\n const width = Number(resMatch[2]);\n if (height === undefined || width === undefined || height <= 0 || width <= 0) {\n return err(hdrDecodeError(`invalid dimensions: ${width}x${height}`));\n }\n\n // Step 2: decode per-scanline new-RLE pixel data\n const pixelCount = width * height;\n const rgbe = new Uint8Array(pixelCount * 4);\n const scanlineBytes = width * 4;\n\n for (let y = 0; y < height; y++) {\n if (pos + 4 > bytes.length) {\n return err(hdrDecodeError(`truncated at scanline ${y}: expected 4-byte prefix`));\n }\n const p0 = bytes[pos];\n const p1 = bytes[pos + 1];\n const pHi = bytes[pos + 2];\n const pLo = bytes[pos + 3];\n if (p0 === undefined || p1 === undefined || pHi === undefined || pLo === undefined) {\n return err(hdrDecodeError(`truncated at scanline ${y}`));\n }\n // biome-ignore lint/style/noNonNullAssertion: checked above\n const prefixWidth = (pHi! << 8) | pLo!;\n\n if (p0 === 0x02 && p1 === 0x02 && prefixWidth === width && width >= 8 && width <= 32767) {\n // New-RLE: 4 channels independently RLE-encoded, then interleaved\n const decoded = decodeNewRleScanline(bytes, pos + 4, width);\n if (decoded === null) {\n return err(hdrDecodeError(`RLE decode failed at scanline ${y}`));\n }\n // Copy into rgbe interleaved buffer\n const base = y * scanlineBytes;\n const channels = [decoded[0], decoded[1], decoded[2], decoded[3]];\n for (let x = 0; x < width; x++) {\n for (let ch = 0; ch < 4; ch++) {\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n rgbe[base + x * 4 + ch] = channels[ch]![x]!;\n }\n }\n // Advance pos past the 4-byte prefix + the total RLE bytes consumed\n pos += 4 + decoded.totalBytes;\n } else {\n // Old-RLE: not supported\n return err(\n hdrDecodeError(\n 'old-RLE format is not supported; only new-RLE (32-bit_rle_rgbe) is accepted',\n ),\n );\n }\n }\n\n // Step 3: RGBE to float\n const out = new Float32Array(pixelCount * 4);\n for (let i = 0; i < pixelCount; i++) {\n const base = i * 4;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const r = rgbe[base]!;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const g = rgbe[base + 1]!;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const b = rgbe[base + 2]!;\n // biome-ignore lint/style/noNonNullAssertion: checked at allocation\n const e = rgbe[base + 3]!;\n out[base + 3] = 1.0;\n\n if (e === 0) {\n out[base] = 0;\n out[base + 1] = 0;\n out[base + 2] = 0;\n } else {\n // f = 2^(E - 136) = ldexp(1.0, E - 128 - 8)\n // Equivalent: ldexp(1.0 / 256.0, E - 128)\n // But simpler: const f = (1.0 / 256.0) * Math.pow(2, e - 128);\n // Using ldexp: Math.pow(2, e - 136)\n const f = 2 ** (e - 136);\n out[base] = (r + 0.5) * f;\n out[base + 1] = (g + 0.5) * f;\n out[base + 2] = (b + 0.5) * f;\n }\n }\n\n return ok({ width, height, data: out });\n}\n\ninterface DecodedScanline {\n /** 4 channels, each width bytes */\n readonly 0: Uint8Array;\n readonly 1: Uint8Array;\n readonly 2: Uint8Array;\n readonly 3: Uint8Array;\n readonly totalBytes: number;\n}\n\n/**\n * Decode one scanline of new-RLE data into 4 channel arrays.\n * Returns null on failure (data underrun).\n */\nfunction decodeNewRleScanline(\n bytes: Uint8Array,\n start: number,\n width: number,\n): DecodedScanline | null {\n const channels: Uint8Array[] = [];\n let cursor = start;\n\n for (let ch = 0; ch < 4; ch++) {\n const data = new Uint8Array(width);\n let x = 0;\n while (x < width) {\n if (cursor >= bytes.length) return null;\n // biome-ignore lint/style/noNonNullAssertion: checked above\n const code = bytes[cursor]!;\n cursor++;\n if (code > 128) {\n // RLE run: next byte repeated (code - 128) times\n const count = code - 128;\n if (cursor >= bytes.length || x + count > width) return null;\n // biome-ignore lint/style/noNonNullAssertion: checked above\n const val = bytes[cursor]!;\n cursor++;\n data.fill(val, x, x + count);\n x += count;\n } else {\n // Literal run: next `code` bytes copied verbatim\n if (code === 0) continue;\n if (cursor + code > bytes.length || x + code > width) return null;\n for (let k = 0; k < code; k++) {\n // biome-ignore lint/style/noNonNullAssertion: checked above\n data[x + k] = bytes[cursor + k]!;\n }\n cursor += code;\n x += code;\n }\n }\n channels.push(data);\n }\n\n const ch0 = channels[0];\n const ch1 = channels[1];\n const ch2 = channels[2];\n const ch3 = channels[3];\n if (ch0 === undefined || ch1 === undefined || ch2 === undefined || ch3 === undefined) {\n return null; // channels should always have 4 entries\n }\n return {\n 0: ch0,\n 1: ch1,\n 2: ch2,\n 3: ch3,\n totalBytes: cursor - start,\n };\n}\n\nfunction findByte(bytes: Uint8Array, target: number, start: number): number {\n for (let i = start; i < bytes.length; i++) {\n if (bytes[i] === target) return i;\n }\n return -1;\n}\n\nfunction asciiSubstring(bytes: Uint8Array, start: number, end: number): string {\n let s = '';\n for (let i = start; i < end; i++) {\n // biome-ignore lint/style/noNonNullAssertion: in bounds\n s += String.fromCharCode(bytes[i]!);\n }\n return s;\n}\n\nfunction hdrDecodeError(reason: string): ImageError {\n return imageError({\n code: 'image-hdr-decode-failed',\n reason,\n });\n}\n","import type { DecodedImage, ImageColorSpace, ImageError } from '@forgeax/engine-types';\nimport { imageError } from './errors.js';\nimport type { Result } from './result.js';\nimport { err, ok } from './result.js';\n\n/**\n * Browser image decoder (plan-strategy section 3.3 + section 2.4 D Open Q-3).\n *\n * Uses `createImageBitmap(blob, { colorSpaceConversion: 'none' })` to keep\n * the bytes in their authored color space (sRGB / linear); the runtime\n * uploadTexture entry asserts `format <-> colorSpace` consistency at GPU\n * upload time (plan-strategy section 2.5 D Open Q-4 (c)).\n *\n * Pixel readback uses an offscreen 2D canvas + `getImageData`; AI users\n * never call this directly -- it is the browser-mode counterpart of\n * `parseImage` (Node-mode upng / jpeg-js path) consumed by future M2b\n * vite-plugin overlay paths and direct browser-mode loaders. The Node\n * decoder remains the SSOT for parseImage; this file is the browser\n * fallback when running inside vitest browser mode or the Vite dev server.\n */\nexport async function decodeImageInBrowser(\n bytes: Uint8Array,\n mime: 'image/png' | 'image/jpeg',\n opts: { colorSpace?: ImageColorSpace; mipmap?: boolean } = {},\n): Promise<Result<DecodedImage, ImageError>> {\n if (typeof createImageBitmap !== 'function') {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: 'createImageBitmap is not available in this environment',\n }),\n );\n }\n\n let bitmap: ImageBitmap;\n try {\n const blob = new Blob([bytes as BlobPart], { type: mime });\n bitmap = await createImageBitmap(blob, {\n colorSpaceConversion: 'none',\n premultiplyAlpha: 'none',\n });\n } catch (e) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: e instanceof Error ? e.message : String(e),\n }),\n );\n }\n\n // Read pixels back via OffscreenCanvas (browser context) -- 2D context is\n // ubiquitous and avoids the WebGPU readback round-trip during disk-side\n // decode (charter P5 producer / consumer split: GPU upload happens inside\n // @forgeax/engine-runtime AssetRegistry.uploadTexture, not here).\n const canvas =\n typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(bitmap.width, bitmap.height)\n : (() => {\n const c = document.createElement('canvas');\n c.width = bitmap.width;\n c.height = bitmap.height;\n return c;\n })();\n const ctx = canvas.getContext('2d') as\n | OffscreenCanvasRenderingContext2D\n | CanvasRenderingContext2D\n | null;\n if (ctx === null) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: 'failed to acquire 2d canvas context for pixel readback',\n }),\n );\n }\n ctx.drawImage(bitmap, 0, 0);\n const data = ctx.getImageData(0, 0, bitmap.width, bitmap.height);\n bitmap.close();\n\n return ok({\n bytes: new Uint8Array(data.data.buffer.slice(0)),\n width: data.width,\n height: data.height,\n mime,\n colorSpace: opts.colorSpace ?? 'srgb',\n mipmap: opts.mipmap ?? true,\n });\n}\n","// Node-only image decoder shell for @forgeax/engine-image.\n//\n// Lazy-loads `upng-js` (PNG) and `jpeg-js` (JPEG) via dynamic imports so the\n// browser bundle tree-shakes both decoders out of the ESM output (the\n// browser path uses createImageBitmap instead). Migrated from the original\n// packages/runtime/src/ home in feat-20260515-learn-render-getting-started\n// M2 T-M2-02 (Open Q-3 option a; plan-strategy section 2.4) -- runtime now\n// only consumes DecodedImage POD and never imports the decoders.\n//\n// The implementation surface is filled out by T-M2-05; this file is staged\n// here in T-M2-02 so the migration touches a single owner directory and the\n// runtime package never ships a half-migrated state.\n\n/**\n * Local widening of the upng-js dynamic-import surface (the package ships\n * without a usable @types entry; the d.ts in image-decoders.d.ts only adds\n * the module declaration). Lists the two functions consumed by the Node\n * decoder path; widening here keeps the import site free of `as any` casts\n * (charter P3 explicit failure -- structural typing > escape hatches).\n */\nexport interface UpngModule {\n decode: (bytes: Uint8Array | ArrayBuffer) => {\n width: number;\n height: number;\n depth: number;\n ctype: number;\n data: Uint8Array;\n frames?: unknown[];\n };\n toRGBA8: (img: { width: number; height: number; data: Uint8Array }) => ArrayBuffer[];\n}\n\n/**\n * Local widening of the jpeg-js dynamic-import surface. Mirrors the subset\n * used by the Node decoder; jpeg-js has @types/jpeg-js but we keep a local\n * shape so the d.ts ambient declaration stays the SSOT for runtime resolution\n * (charter P5 producer / consumer split).\n */\nexport interface JpegModule {\n decode: (\n bytes: Uint8Array | ArrayBuffer,\n opts?: { useTArray?: boolean; formatAsRGBA?: boolean },\n ) => {\n width: number;\n height: number;\n data: Uint8Array;\n };\n}\n\n/**\n * Lazy-load the upng-js Node decoder module. Browser bundlers tree-shake\n * this dynamic import out when only the createImageBitmap path is reached\n * at runtime; tsup external keeps the package name from being inlined.\n */\nexport async function loadUpng(): Promise<UpngModule> {\n const mod = (await import('upng-js')) as { default?: UpngModule } & UpngModule;\n return (mod.default ?? mod) as UpngModule;\n}\n\n/**\n * Lazy-load the jpeg-js Node decoder module. Same tree-shake guarantee as\n * loadUpng -- browser bundles never reach this path.\n */\nexport async function loadJpeg(): Promise<JpegModule> {\n const mod = (await import('jpeg-js')) as { default?: JpegModule } & JpegModule;\n return (mod.default ?? mod) as JpegModule;\n}\n","import type { AssetCompression, DecodedImage, ImageMeta } from '@forgeax/engine-types';\n\n/**\n * External-asset-package envelope shape mirroring the\n * `packages/pack/schema/meta.schema.json` $defs/ExternalAssetPackage.\n * Consumed by `forgeax asset import` (unified CLI entry) +\n * the image importer in M2 build-time path. The internal-text-package\n * variant ($defs/InternalTextPackage) is owned by the gltf-loader feat.\n */\nexport interface ExternalAssetPackage {\n readonly schemaVersion: string;\n readonly kind: 'external-asset-package';\n readonly importer: 'image';\n readonly source: string;\n readonly importSettings: ImageImportSettings;\n readonly subAssets: readonly ExternalSubAsset[];\n}\n\n/**\n * Free-form image importer settings persisted into `*.meta.json` (importer: 'image')\n * `importSettings`. Matches `ImageMeta` POD field-for-field (charter P5\n * single SSOT) but the open Record shape allows future minor adds (cubemap\n * face / array layer / custom colorSpace) without breaking existing meta\n * files (plan-strategy R5).\n */\nexport interface ImageImportSettings extends Readonly<Record<string, unknown>> {\n readonly colorSpace: ImageMeta['colorSpace'];\n readonly mipmap: ImageMeta['mipmap'];\n readonly addressMode: ImageMeta['addressMode'];\n readonly filterMode: ImageMeta['filterMode'];\n readonly downscaleMaxDimension?: number;\n /**\n * Explicit per-asset compression override (AC-01: importSettings carries\n * compression intent). When omitted, the build-time compression strategy\n * table decides by artifact kind (mesh -> zstd, texture -> none). When set,\n * this value wins over the default table for this asset.\n */\n readonly compression?: AssetCompression;\n}\n\nexport interface ExternalSubAsset {\n readonly guid: string;\n readonly sourceIndex: number;\n readonly kind: string;\n readonly name?: string;\n}\n\n/**\n * Pure function that translates a DecodedImage POD + ImageMeta POD into\n * an external-asset-package envelope ready to be JSON-stringified into a\n * `*.meta.json` sidecar with importer: 'image' (plan-strategy section 3.2 sequence A; AC-13\n * disk schema reuse).\n *\n * The single sub-asset emitted carries:\n * - `guid` -- copied verbatim from `meta.guid` (the producer guarantees\n * the GUID is freshly minted or reused via `reimportReuseMeta`)\n * - `sourceIndex: 0` (single-sub-asset shape; cubemap / array reserved\n * for future feat)\n * - `kind: 'texture'` (closed literal for image-importer emit)\n *\n * Two consecutive calls with identical inputs produce JSON.stringify\n * byte-equal output (AC-16 idempotent reimport).\n */\nexport function toAssetPack(decoded: DecodedImage, meta: ImageMeta): ExternalAssetPackage {\n // decoded.bytes are not embedded in the meta envelope -- the source bytes\n // live next to the sidecar on disk (the runtime reads them via\n // decodeImageFromFile at app load time). We keep the parameter so future\n // cubemap / array-layer feats can derive multi-sub-asset emit from decoded\n // dimensions without breaking the call surface (charter P5 consistent\n // abstraction).\n void decoded;\n\n return {\n schemaVersion: '1.0.0',\n kind: 'external-asset-package',\n importer: 'image',\n source: '',\n importSettings: {\n colorSpace: meta.colorSpace,\n mipmap: meta.mipmap,\n addressMode: meta.addressMode,\n filterMode: meta.filterMode,\n ...(meta.downscaleMaxDimension !== undefined\n ? { downscaleMaxDimension: meta.downscaleMaxDimension }\n : {}),\n },\n subAssets: [\n {\n guid: meta.guid,\n sourceIndex: 0,\n kind: 'texture',\n },\n ],\n };\n}\n","import type {\n DecodedImage,\n ImageColorSpace,\n ImageError,\n ImageErrorDetailFor,\n ImageMeta,\n TextureAsset,\n} from '@forgeax/engine-types';\nimport { imageError } from './errors.js';\nimport { err, ok, type Result } from './result.js';\nimport { type ExternalAssetPackage, toAssetPack } from './to-asset-pack.js';\n\n/** Four finite RGBA8 channels in source order. */\nexport type PixelColor =\n | readonly [number, number, number, number]\n | Readonly<{ r: number; g: number; b: number; a: number }>;\n\n/** Optional source rectangle for a blit; coordinates are rounded and clipped. */\nexport interface PixelSurfaceRect {\n readonly x?: number;\n readonly y?: number;\n readonly width?: number;\n readonly height?: number;\n}\n\nexport interface PixelSurfaceNoiseOptions {\n /** Inclusive lower channel bound, default 0. */\n readonly min?: number;\n /** Inclusive upper channel bound, default 255. */\n readonly max?: number;\n /** Alpha written for every generated pixel, default 255. */\n readonly alpha?: number;\n}\n\nexport interface PixelSurfaceOptions {\n readonly width: number;\n readonly height: number;\n readonly colorSpace?: ImageColorSpace;\n readonly mipmap?: boolean;\n}\n\nexport interface PixelSurface {\n readonly width: number;\n readonly height: number;\n readonly colorSpace: ImageColorSpace;\n readonly mipmap: boolean;\n /** Tight RGBA8 level-zero bytes in row-major order. */\n readonly data: Uint8Array;\n\n setPixel(x: number, y: number, color: PixelColor): Result<void, ImageError>;\n fillRect(\n x: number,\n y: number,\n width: number,\n height: number,\n color: PixelColor,\n ): Result<void, ImageError>;\n fillCircle(cx: number, cy: number, radius: number, color: PixelColor): Result<void, ImageError>;\n blit(\n source: PixelSurface,\n destinationX: number,\n destinationY: number,\n sourceRect?: PixelSurfaceRect,\n ): Result<void, ImageError>;\n /** Fill RGB channels with deterministic seeded noise and set alpha uniformly. */\n fillNoise(seed: number, options?: PixelSurfaceNoiseOptions): Result<void, ImageError>;\n /** Alias kept on the same value for the concise authoring spelling. */\n noise(seed: number, options?: PixelSurfaceNoiseOptions): Result<void, ImageError>;\n\n toDecodedImage(): DecodedImage;\n toTextureAsset(): TextureAsset;\n toAssetPack(meta: ImageMeta): ExternalAssetPackage;\n}\n\ntype SurfaceOperation = ImageErrorDetailFor<'image-surface-invalid'>['operation'];\n\nfunction invalid(\n operation: SurfaceOperation,\n field: string,\n value: string | number,\n expected: string,\n): Result<never, ImageError> {\n return err(\n imageError({\n code: 'image-surface-invalid',\n operation,\n field,\n value,\n expected,\n }),\n );\n}\n\nfunction finiteNumber(\n operation: SurfaceOperation,\n field: string,\n value: number,\n): Result<number, ImageError> {\n if (!Number.isFinite(value)) {\n return invalid(operation, field, String(value), 'a finite number');\n }\n return ok(value);\n}\n\nfunction positiveDimension(\n operation: SurfaceOperation,\n field: string,\n value: number,\n): Result<number, ImageError> {\n if (!Number.isInteger(value) || value <= 0) {\n return invalid(operation, field, value, 'a positive integer');\n }\n return ok(value);\n}\n\nfunction colorChannels(\n operation: SurfaceOperation,\n color: PixelColor,\n): Result<readonly [number, number, number, number], ImageError> {\n const channels = Array.isArray(color)\n ? color\n : color !== null && typeof color === 'object'\n ? [\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).r,\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).g,\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).b,\n (color as Readonly<{ r: number; g: number; b: number; a: number }>).a,\n ]\n : undefined;\n if (channels === undefined || channels.length !== 4) {\n return invalid(operation, 'color', 'malformed', 'four finite RGBA8 channels');\n }\n const normalized: [number, number, number, number] = [0, 0, 0, 0];\n for (let index = 0; index < channels.length; index += 1) {\n const channel = channels[index];\n if (channel === undefined || !Number.isFinite(channel) || channel < 0 || channel > 255) {\n return invalid(\n operation,\n `color[${index}]`,\n channel ?? 'missing',\n 'a finite number in [0, 255]',\n );\n }\n normalized[index] = Math.round(channel);\n }\n return ok(normalized);\n}\n\nfunction rounded(\n operation: SurfaceOperation,\n field: string,\n value: number,\n): Result<number, ImageError> {\n const result = finiteNumber(operation, field, value);\n return result.ok ? ok(Math.round(result.value)) : result;\n}\n\nfunction rectangle(\n operation: 'fill-rect' | 'blit',\n x: number,\n y: number,\n width: number,\n height: number,\n): Result<readonly [number, number, number, number], ImageError> {\n const values: [number, number, number, number] = [x, y, width, height];\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n if (value === undefined || !Number.isFinite(value)) {\n return invalid(\n operation,\n ['x', 'y', 'width', 'height'][index] ?? 'rectangle',\n String(value),\n 'a finite number',\n );\n }\n }\n if (width <= 0 || height <= 0) {\n return invalid(\n operation,\n width <= 0 ? 'width' : 'height',\n width <= 0 ? width : height,\n 'a positive number',\n );\n }\n return ok([Math.round(x), Math.round(y), Math.round(width), Math.round(height)]);\n}\n\nfunction writePixel(\n data: Uint8Array,\n width: number,\n x: number,\n y: number,\n color: ArrayLike<number>,\n): void {\n if (x < 0 || y < 0 || x >= width) return;\n const offset = (y * width + x) * 4;\n data[offset] = color[0] ?? 0;\n data[offset + 1] = color[1] ?? 0;\n data[offset + 2] = color[2] ?? 0;\n data[offset + 3] = color[3] ?? 0;\n}\n\nfunction fillClippedRect(\n data: Uint8Array,\n width: number,\n height: number,\n x: number,\n y: number,\n rectWidth: number,\n rectHeight: number,\n color: readonly number[],\n): void {\n const left = Math.max(0, x);\n const top = Math.max(0, y);\n const right = Math.min(width, x + rectWidth);\n const bottom = Math.min(height, y + rectHeight);\n for (let row = top; row < bottom; row += 1) {\n for (let column = left; column < right; column += 1) {\n writePixel(data, width, column, row, color);\n }\n }\n}\n\nfunction nextRandom(state: number): number {\n // xorshift32 is small, deterministic across runtimes, and has no ambient\n // state. The seed is normalized once by fillNoise, so zero remains valid.\n let value = state >>> 0;\n value ^= value << 13;\n value ^= value >>> 17;\n value ^= value << 5;\n return value >>> 0;\n}\n\nfunction makeSurface(options: PixelSurfaceOptions): Result<PixelSurface, ImageError> {\n const width = positiveDimension('create', 'width', options.width);\n if (!width.ok) return width;\n const height = positiveDimension('create', 'height', options.height);\n if (!height.ok) return height;\n if (\n options.colorSpace !== undefined &&\n options.colorSpace !== 'srgb' &&\n options.colorSpace !== 'linear'\n ) {\n return invalid('create', 'colorSpace', String(options.colorSpace), \"'srgb' or 'linear'\");\n }\n if (options.mipmap !== undefined && typeof options.mipmap !== 'boolean') {\n return invalid('create', 'mipmap', String(options.mipmap), 'a boolean');\n }\n const data = new Uint8Array(width.value * height.value * 4);\n const colorSpace = options.colorSpace ?? 'srgb';\n const mipmap = options.mipmap ?? false;\n\n const surface: PixelSurface = {\n width: width.value,\n height: height.value,\n colorSpace,\n mipmap,\n data,\n setPixel(x, y, color) {\n const px = rounded('set-pixel', 'x', x);\n if (!px.ok) return px;\n const py = rounded('set-pixel', 'y', y);\n if (!py.ok) return py;\n const normalized = colorChannels('set-pixel', color);\n if (!normalized.ok) return normalized;\n writePixel(data, width.value, px.value, py.value, normalized.value);\n return ok(undefined);\n },\n fillRect(x, y, rectWidth, rectHeight, color) {\n const rect = rectangle('fill-rect', x, y, rectWidth, rectHeight);\n if (!rect.ok) return rect;\n const normalized = colorChannels('fill-rect', color);\n if (!normalized.ok) return normalized;\n fillClippedRect(\n data,\n width.value,\n height.value,\n rect.value[0] ?? 0,\n rect.value[1] ?? 0,\n rect.value[2] ?? 0,\n rect.value[3] ?? 0,\n normalized.value,\n );\n return ok(undefined);\n },\n fillCircle(cx, cy, radius, color) {\n const centerX = rounded('fill-circle', 'cx', cx);\n if (!centerX.ok) return centerX;\n const centerY = rounded('fill-circle', 'cy', cy);\n if (!centerY.ok) return centerY;\n const circleRadius = rounded('fill-circle', 'radius', radius);\n if (!circleRadius.ok) return circleRadius;\n if (circleRadius.value <= 0) {\n return invalid('fill-circle', 'radius', circleRadius.value, 'a positive number');\n }\n const normalized = colorChannels('fill-circle', color);\n if (!normalized.ok) return normalized;\n const radiusSquared = circleRadius.value * circleRadius.value;\n const left = Math.max(0, centerX.value - circleRadius.value);\n const right = Math.min(width.value - 1, centerX.value + circleRadius.value);\n const top = Math.max(0, centerY.value - circleRadius.value);\n const bottom = Math.min(height.value - 1, centerY.value + circleRadius.value);\n for (let row = top; row <= bottom; row += 1) {\n for (let column = left; column <= right; column += 1) {\n const dx = column - centerX.value;\n const dy = row - centerY.value;\n if (dx * dx + dy * dy <= radiusSquared)\n writePixel(data, width.value, column, row, normalized.value);\n }\n }\n return ok(undefined);\n },\n blit(source, destinationX, destinationY, sourceRect) {\n const destination = rounded('blit', 'destinationX', destinationX);\n if (!destination.ok) return destination;\n const destinationYResult = rounded('blit', 'destinationY', destinationY);\n if (!destinationYResult.ok) return destinationYResult;\n if (\n source === null ||\n typeof source !== 'object' ||\n !Number.isInteger(source.width) ||\n !Number.isInteger(source.height) ||\n !(source.data instanceof Uint8Array) ||\n source.data.length !== source.width * source.height * 4\n ) {\n return invalid('blit', 'source', 'malformed', 'a valid PixelSurface');\n }\n const rect = rectangle(\n 'blit',\n sourceRect?.x ?? 0,\n sourceRect?.y ?? 0,\n sourceRect?.width ?? source.width,\n sourceRect?.height ?? source.height,\n );\n if (!rect.ok) return rect;\n const sourceX = rect.value[0] ?? 0;\n const sourceY = rect.value[1] ?? 0;\n const sourceWidth = rect.value[2] ?? 0;\n const sourceHeight = rect.value[3] ?? 0;\n const left = Math.max(0, sourceX);\n const top = Math.max(0, sourceY);\n const right = Math.min(source.width, sourceX + sourceWidth);\n const bottom = Math.min(source.height, sourceY + sourceHeight);\n if (right <= left || bottom <= top) return ok(undefined);\n const snapshot = source.data.slice();\n for (let row = top; row < bottom; row += 1) {\n for (let column = left; column < right; column += 1) {\n const targetX = destination.value + column - sourceX;\n const targetY = destinationYResult.value + row - sourceY;\n if (targetX < 0 || targetY < 0 || targetX >= width.value || targetY >= height.value)\n continue;\n const sourceOffset = (row * source.width + column) * 4;\n writePixel(\n data,\n width.value,\n targetX,\n targetY,\n snapshot.subarray(sourceOffset, sourceOffset + 4),\n );\n }\n }\n return ok(undefined);\n },\n fillNoise(seed, noiseOptions) {\n if (!Number.isFinite(seed)) return invalid('noise', 'seed', String(seed), 'a finite number');\n const min = noiseOptions?.min ?? 0;\n const max = noiseOptions?.max ?? 255;\n const alpha = noiseOptions?.alpha ?? 255;\n for (const [field, value] of [\n ['min', min],\n ['max', max],\n ['alpha', alpha],\n ] as const) {\n if (!Number.isFinite(value) || value < 0 || value > 255) {\n return invalid('noise', field, value, 'a finite number in [0, 255]');\n }\n }\n if (min > max) return invalid('noise', 'min', min, 'a value no greater than max');\n let state = Math.trunc(seed) >>> 0 || 0x6d2b79f5;\n const span = max - min;\n for (let index = 0; index < data.length; index += 4) {\n state = nextRandom(state);\n const value = min + (state / 0x100000000) * span;\n const channel = Math.round(value);\n data[index] = channel;\n data[index + 1] = channel;\n data[index + 2] = channel;\n data[index + 3] = Math.round(alpha);\n }\n return ok(undefined);\n },\n noise(seed, noiseOptions) {\n return surface.fillNoise(seed, noiseOptions);\n },\n toDecodedImage() {\n return {\n bytes: data.slice(),\n width: width.value,\n height: height.value,\n mime: 'image/png',\n colorSpace,\n mipmap,\n };\n },\n toTextureAsset() {\n return {\n kind: 'texture',\n shape: {\n viewDimension: '2d',\n extent: { width: width.value, height: height.value },\n },\n format: colorSpace === 'srgb' ? 'rgba8unorm-srgb' : 'rgba8unorm',\n data: data.slice(),\n colorSpace,\n mips: mipmap ? { kind: 'generate' } : { kind: 'none' },\n };\n },\n toAssetPack(meta) {\n return toAssetPack(surface.toDecodedImage(), meta);\n },\n };\n return ok(surface);\n}\n\n/** Create a deterministic, image-owned RGBA8 authoring surface. */\nexport function createPixelSurface(options: PixelSurfaceOptions): Result<PixelSurface, ImageError> {\n return makeSurface(options);\n}\n","/**\n * Derive the semantic identity for an image producer output.\n *\n * The role is the producer-owned identity. Source path and sourceIndex are\n * locators only and must not be folded into this key.\n */\nexport interface ImageSourceKeyLocator {\n readonly sourcePath?: string;\n readonly sourceIndex?: number;\n}\n\nexport type ImageSourceKeyResult =\n | { readonly ok: true; readonly keys: readonly string[] }\n | {\n readonly ok: false;\n readonly code: 'missing-source-key' | 'duplicate-source-key';\n readonly roles: readonly string[];\n };\n\nexport function deriveImageSourceKey(\n role: string,\n _locator?: ImageSourceKeyLocator,\n): string | undefined {\n const normalizedRole = role.trim();\n if (normalizedRole.length === 0) return undefined;\n return `image:${normalizedRole}`;\n}\n\nexport function deriveImageSourceKeys(roles: readonly string[]): ImageSourceKeyResult {\n const keys = roles.map((role) => deriveImageSourceKey(role));\n const missing = roles.filter((_role, index) => keys[index] === undefined);\n if (missing.length > 0) return { ok: false, code: 'missing-source-key', roles: missing };\n\n const seen = new Set<string>();\n for (const key of keys) {\n if (key === undefined) continue;\n if (seen.has(key)) return { ok: false, code: 'duplicate-source-key', roles };\n seen.add(key);\n }\n return { ok: true, keys: keys as string[] };\n}\n","/**\n * Sub-asset-key shape mirroring the in-flight gltf-loader feat (plan-strategy\n * section 2.2 D-4 same-shape). Each sub-asset emitted into a `*.meta.json`\n * sidecar (uniformly `<source>.meta.json` across image / gltf arms,\n * dispatched on top-level `importer` field per feat-20260521) carries a\n * `{kind, name?, indexFallback}` triple so the importer can deterministically\n * match an existing sub-asset across reimports (charter P5 producer/consumer\n * split + AC-14 cross-feat alignment).\n *\n * The image disk schema is currently single-sub-asset; this file collapses\n * to `kind='texture'` + `indexFallback='textures/0'` so the same matching\n * algorithm is reusable when the future cubemap / array-layer feat lands\n * (plan-strategy R5 free-form schema).\n */\n\nexport interface SubAssetKey {\n /** Discriminator literal -- 'texture' for engine-image; 'mesh' / 'material' / 'scene' / 'image' for gltf-loader. */\n readonly kind: string;\n /** Optional human-readable name field; absent when the source has no symbolic identifier. */\n readonly name?: string;\n /** Path-style fallback identifier (e.g. 'images/0' / 'meshes/0'); always non-empty. */\n readonly indexFallback: string;\n}\n\nexport interface SubAssetKeyInput {\n readonly kind: string;\n readonly sourceIndex: number;\n readonly name?: string;\n}\n\n/**\n * Construct a sub-asset key from importer input. The `indexFallback` is\n * synthesised from the kind literal + sourceIndex so two importers\n * (image / gltf-loader) emit identical fallback strings for the same\n * `(kind, sourceIndex)` pair (AC-14).\n */\nexport function subAssetKey(input: SubAssetKeyInput): SubAssetKey {\n const indexFallback = `${input.kind}s/${input.sourceIndex}`;\n if (input.name !== undefined) {\n return { kind: input.kind, name: input.name, indexFallback };\n }\n return { kind: input.kind, indexFallback };\n}\n\n/**\n * Two-phase equality predicate used by reimportReuseMeta for GUID\n * preservation across reimports:\n *\n * Phase 1 -- (kind + name + idx) full match: identical in all three fields\n * Phase 2 -- (kind + idx) match: same kind + indexFallback, name absent or\n * equal (collapses when both sides omit name)\n * Phase 3 -- otherwise: false (importer mints fresh UUIDv7)\n *\n * The two phases are folded into a single boolean predicate here; the\n * reimportReuseMeta caller iterates the existing subAssets list and picks\n * the first hit (deterministic order; matches AC-16 byte-identical\n * reimport).\n */\nexport function subAssetKeyEqual(a: SubAssetKey, b: SubAssetKey): boolean {\n if (a.kind !== b.kind) return false;\n if (a.indexFallback !== b.indexFallback) return false;\n // name comparison: undefined === undefined is fine; if one side has a name\n // and the other does not, the keys are NOT equal (charter P4 explicit\n // failure -- name carries a meaningful identifier when present)\n if (a.name !== b.name) return false;\n return true;\n}\n","import { AssetGuid } from '@forgeax/engine-pack/guid';\nimport type { DecodedImage, ImageColorSpace } from '@forgeax/engine-types';\n\nimport { deriveImageSourceKey } from './source-key.js';\nimport type { SubAssetKey } from './sub-asset-key.js';\nimport { subAssetKey, subAssetKeyEqual } from './sub-asset-key.js';\n\n/**\n * Existing `*.meta.json` (importer: 'image') sub-asset record (free-form sub-shape; the\n * meta.schema.json $defs/subAsset locks `guid` + `sourceIndex` + `kind` only).\n * Image importer treats the open shape as a key-bearing tuple; the gltf-loader\n * feat works on the same shape.\n */\nexport interface ExistingSubAsset {\n readonly guid: string;\n readonly sourceIndex: number;\n readonly kind: string;\n readonly name?: string;\n readonly sourceKey?: string;\n}\n\n/**\n * Existing `*.meta.json` (importer: 'image') package record. Mirrors the\n * meta.schema.json $defs/ExternalAssetPackage shape (research F-9). Only\n * the fields consumed by the reimport reuse algorithm are typed here;\n * importSettings stays a free-form open shape.\n */\nexport interface ExistingExternalAssetPackage {\n readonly schemaVersion: string;\n readonly kind: 'external-asset-package';\n readonly importer: 'image';\n readonly source: string;\n readonly importSettings: Readonly<Record<string, unknown>>;\n readonly subAssets: readonly ExistingSubAsset[];\n}\n\n/**\n * Sub-asset record emitted into the regenerated `*.meta.json` (importer: 'image')\n * `subAssets[]` slot. Shape matches `ExistingSubAsset` so two consecutive\n * reimports of byte-identical bytes produce byte-identical JSON\n * (AC-16 byte-stable reimport guarantee).\n */\nexport interface EmittedSubAsset {\n readonly guid: string;\n readonly sourceIndex: number;\n readonly kind: string;\n readonly name?: string;\n readonly sourceKey?: string;\n}\n\n/**\n * Two-phase reimport reuse algorithm (plan-strategy section 2.2 D-4\n * mirroring the in-flight gltf-loader algorithm).\n *\n * For each fresh sub-asset key the importer would emit, this function:\n * 1. Searches the `existing.subAssets[]` for a `subAssetKeyEqual` hit.\n * 2. On hit -- reuses the existing GUID byte-for-byte.\n * 3. On miss -- mints a fresh UUIDv7 via `AssetGuid.random()`.\n *\n * Image disk schema currently emits a single `kind='texture'` sub-asset per\n * source (cubemap face / array layer reserved for future feat); the\n * algorithm scales to multi-sub-asset sources without modification (charter\n * P5 consistent abstraction).\n *\n * Returns the regenerated `subAssets[]` array. The caller (toAssetPack /\n * runAssetImport) splices it into the `*.meta.json` (importer: 'image') envelope.\n */\nexport function reimportReuseMeta(\n decoded: DecodedImage,\n existing: ExistingExternalAssetPackage | undefined,\n): readonly EmittedSubAsset[] {\n // Image disk schema is single-sub-asset; the future multi-asset path\n // iterates a producer-side list of sub-asset keys instead of a single one.\n const freshKeys: readonly SubAssetKey[] = [subAssetKey({ kind: 'texture', sourceIndex: 0 })];\n\n const out: EmittedSubAsset[] = [];\n const sourceKey = deriveImageSourceKey('texture');\n\n for (let i = 0; i < freshKeys.length; i++) {\n const fresh = freshKeys[i];\n if (fresh === undefined) continue;\n\n let reuseGuid: string | undefined;\n if (existing !== undefined) {\n for (const candidate of existing.subAssets) {\n if (sourceKey !== undefined && candidate.sourceKey === sourceKey) {\n reuseGuid = candidate.guid;\n break;\n }\n const candidateKey = subAssetKey({\n kind: candidate.kind,\n sourceIndex: candidate.sourceIndex,\n ...(candidate.name !== undefined ? { name: candidate.name } : {}),\n });\n if (subAssetKeyEqual(fresh, candidateKey)) {\n reuseGuid = candidate.guid;\n break;\n }\n }\n }\n\n const guid = reuseGuid ?? AssetGuid.format(AssetGuid.random());\n const emit: EmittedSubAsset =\n fresh.name !== undefined\n ? {\n guid,\n sourceIndex: i,\n kind: fresh.kind,\n name: fresh.name,\n ...(sourceKey === undefined ? {} : { sourceKey }),\n }\n : {\n guid,\n sourceIndex: i,\n kind: fresh.kind,\n ...(sourceKey === undefined ? {} : { sourceKey }),\n };\n out.push(emit);\n }\n\n // Ensure decoded ref is consumed (decoded.bytes is the producer signal that\n // a sub-asset exists; without bytes there is nothing to emit). The\n // signature keeps `decoded` in the public surface so the future cubemap /\n // array-layer feat can derive its multi-sub-asset list from `decoded`\n // metadata without a breaking change (charter P5 consistent abstraction).\n void decoded;\n\n return out;\n}\n\n/**\n * HDR colorSpace validator for *.image.meta.json sidecar importSettings\n * (plan-strategy D-8 -- HDR files must use colorSpace=linear).\n *\n * Extension is matched case-insensitively against the HDR set\n * (`.hdr`, `.exr`). When the extension belongs to the HDR set:\n * - `colorSpace='linear'` -> ok, returns `'linear'`\n * - any other colorSpace -> structured rejection with expected/actual\n *\n * Non-HDR extensions pass through the colorSpace unchanged. Downstream\n * callers (decodeImageFromFile, parseImage) invoke this at the\n * sidecar-read entry to fail-fast when an .hdr meta carries srgb.\n */\nexport function validateColorSpaceForHdr(\n ext: string,\n colorSpace: ImageColorSpace,\n): { ok: true; value: ImageColorSpace } | { ok: false; expected: string; actual: string } {\n const lower = ext.toLowerCase();\n if (lower === '.hdr' || lower === '.exr') {\n if (colorSpace !== 'linear') {\n return {\n ok: false,\n expected: 'colorSpace=linear for HDR source files',\n actual: `colorSpace=${colorSpace}`,\n };\n }\n return { ok: true, value: 'linear' };\n }\n return { ok: true, value: colorSpace };\n}\n","import { parseKtx2, transcodeBasis, transcodeKtx2 } from '@forgeax/engine-codec';\nimport {\n type AssetDecoderContribution,\n type AssetDecoderInput,\n type AssetDecoderResult,\n type AssetKind,\n type AssetLoadError,\n type EquirectAsset,\n err,\n ok,\n type TextureAsset,\n} from '@forgeax/engine-types';\n\nfunction invalid(guid: string, expected: string, reason: string) {\n return err<AssetLoadError>({\n code: 'asset-package-invalid',\n expected,\n hint: 'recook the image asset and publish its complete device-neutral payload',\n detail: { guid, reason },\n });\n}\n\nfunction validDimensions(width: number, height: number): boolean {\n return Number.isSafeInteger(width) && width > 0 && Number.isSafeInteger(height) && height > 0;\n}\n\nfunction imageBytes(value: unknown): Uint8Array | Uint8ClampedArray | undefined {\n if (value instanceof Uint8Array || value instanceof Uint8ClampedArray) return value;\n if (!Array.isArray(value)) return undefined;\n if (!value.every((item) => Number.isInteger(item) && item >= 0 && item <= 255)) {\n return undefined;\n }\n return Uint8Array.from(value);\n}\n\nfunction compressedImageTarget(colorSpace: TextureAsset['colorSpace']): TextureAsset['format'] {\n return colorSpace === 'srgb' ? 'rgba8unorm-srgb' : 'rgba8unorm';\n}\n\nfunction validTextureSurface(\n value: unknown,\n): value is Pick<TextureAsset, 'shape' | 'format' | 'data' | 'colorSpace' | 'mips'> {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as Partial<TextureAsset>;\n const shape = candidate.shape;\n const extent = shape?.viewDimension === '2d' ? shape.extent : undefined;\n const mips = candidate.mips;\n return (\n extent !== undefined &&\n validDimensions(extent.width, extent.height) &&\n mips !== undefined &&\n (mips.kind === 'none' ||\n mips.kind === 'generate' ||\n (mips.kind === 'packed' && Number.isSafeInteger(mips.levelCount) && mips.levelCount > 0)) &&\n typeof candidate.format === 'string' &&\n imageBytes(candidate.data) !== undefined &&\n (candidate.colorSpace === 'srgb' || candidate.colorSpace === 'linear')\n );\n}\n\nfunction validEquirectSurface(\n value: unknown,\n): value is Pick<EquirectAsset, 'width' | 'height' | 'format' | 'data' | 'colorSpace'> {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as Partial<EquirectAsset>;\n return (\n validDimensions(candidate.width ?? 0, candidate.height ?? 0) &&\n typeof candidate.format === 'string' &&\n imageBytes(candidate.data) !== undefined &&\n (candidate.colorSpace === 'srgb' || candidate.colorSpace === 'linear')\n );\n}\n\nasync function readImageSurface<P extends TextureAsset | EquirectAsset>(\n input: AssetDecoderInput<P>,\n kind: P['kind'],\n expected: string,\n): Promise<AssetDecoderResult<P>> {\n const { envelope, artifacts } = input;\n const payload = envelope.payload as unknown;\n if (\n payload === null ||\n typeof payload !== 'object' ||\n (payload as { kind?: unknown }).kind !== kind\n ) {\n return invalid(envelope.guid, expected, `${kind} owner validation failed`);\n }\n\n const body = envelope.artifacts.body ?? envelope.artifacts.atlas;\n let data = imageBytes((payload as { data?: unknown }).data);\n if (body !== undefined) {\n const read = await artifacts.read(body);\n if (!read.ok) return err(read.error);\n const bytes = read.value;\n data = bytes;\n\n // Pack v2 keeps the authored TextureAsset format device-neutral while the\n // artifact may carry a Basis KTX2/raw-Basis delivery codec. The new\n // AssetDecoder seam has no device-capability input, so its safe baseline is\n // an uncompressed RGBA target; the renderer can upload that POD on every\n // backend without interpreting container bytes as pixels.\n if (body.assetCodec?.name === 'basis' && body.assetCodec.container !== undefined) {\n const candidate = payload as Partial<TextureAsset>;\n if (candidate.colorSpace !== 'srgb' && candidate.colorSpace !== 'linear') {\n return invalid(envelope.guid, expected, `${kind} color space is invalid`);\n }\n const target = compressedImageTarget(candidate.colorSpace);\n const transcoded =\n body.assetCodec.container === 'ktx2'\n ? await parseKtx2(bytes).then((parsed) =>\n parsed.ok ? transcodeKtx2(parsed.value, target) : parsed,\n )\n : await transcodeBasis(bytes, target);\n if (!transcoded.ok) {\n return invalid(envelope.guid, expected, `codec:${transcoded.error.code}`);\n }\n const mip = transcoded.value.mips[0];\n if (mip === undefined) return invalid(envelope.guid, expected, 'codec:base-mip-missing');\n data = mip.data;\n return readDecodedSurface(\n envelope.guid,\n expected,\n kind,\n kind === 'texture'\n ? {\n ...payload,\n shape: { viewDimension: '2d', extent: { width: mip.width, height: mip.height } },\n format: target,\n data,\n mips: { kind: 'none' },\n }\n : { ...payload, width: mip.width, height: mip.height, format: target, data },\n );\n }\n }\n\n return readDecodedSurface(envelope.guid, expected, kind, {\n ...payload,\n ...(data === undefined ? {} : { data }),\n });\n}\n\nfunction readDecodedSurface<P extends TextureAsset | EquirectAsset>(\n guid: string,\n expected: string,\n kind: P['kind'],\n candidate: unknown,\n): ReturnType<typeof ok<P>> | ReturnType<typeof err<AssetLoadError>> {\n if (kind === 'texture' ? !validTextureSurface(candidate) : !validEquirectSurface(candidate)) {\n return invalid(guid, expected, 'image owner validation failed');\n }\n return ok(candidate as P);\n}\n\nexport const textureContribution: AssetDecoderContribution<TextureAsset, 'texture'> = {\n kind: { kind: 'texture' } as AssetKind<TextureAsset, 'texture'>,\n consumer: 'Image/Render DeviceScope',\n decoder: {\n async decode(input) {\n return readImageSurface(\n input,\n 'texture',\n 'a texture payload with dimensions, format, color space, and bytes',\n );\n },\n },\n};\n\nexport const equirectContribution: AssetDecoderContribution<EquirectAsset, 'equirect'> = {\n kind: { kind: 'equirect' } as AssetKind<EquirectAsset, 'equirect'>,\n consumer: 'Image/Render DeviceScope',\n decoder: {\n async decode(input) {\n return readImageSurface(\n input,\n 'equirect',\n 'an equirect payload with dimensions, format, color space, and bytes',\n );\n },\n },\n};\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CubeParserError } from '../errors.js';
|
|
2
|
+
export interface CubeLut {
|
|
3
|
+
readonly size: 16 | 32 | 64;
|
|
4
|
+
readonly domainMin: readonly [number, number, number];
|
|
5
|
+
readonly domainMax: readonly [number, number, number];
|
|
6
|
+
readonly values: Float32Array;
|
|
7
|
+
readonly sourceKey: string;
|
|
8
|
+
}
|
|
9
|
+
export type CubeLutResult = {
|
|
10
|
+
readonly ok: true;
|
|
11
|
+
readonly value: CubeLut;
|
|
12
|
+
} | {
|
|
13
|
+
readonly ok: false;
|
|
14
|
+
readonly error: CubeParserError;
|
|
15
|
+
};
|
|
16
|
+
export declare function parseCubeLut(source: string, sourceKey?: string): CubeLutResult;
|
|
17
|
+
export declare function cubeLutBytes(lut: CubeLut): Uint8Array;
|
|
18
|
+
//# sourceMappingURL=cube-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cube-parser.d.ts","sourceRoot":"","sources":["../../src/lut/cube-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACtD,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACtD,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CAAC;AAkC5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,SAAmB,GAAG,aAAa,CA2HxF;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAYrD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ImportContext, ImportedAsset, ImportResult, TextureAsset } from '@forgeax/engine-types';
|
|
2
|
+
import type { CubeParserError } from '../errors.js';
|
|
3
|
+
export interface CubeTextureProducerInput {
|
|
4
|
+
readonly source: string;
|
|
5
|
+
readonly sourceKey: string;
|
|
6
|
+
readonly guid: string;
|
|
7
|
+
}
|
|
8
|
+
export type CubeTextureProducerResult = {
|
|
9
|
+
readonly ok: true;
|
|
10
|
+
readonly value: ImportedAsset<TextureAsset>;
|
|
11
|
+
} | {
|
|
12
|
+
readonly ok: false;
|
|
13
|
+
readonly error: CubeParserError;
|
|
14
|
+
};
|
|
15
|
+
export declare function produceCubeTexture(input: CubeTextureProducerInput, source: string): CubeTextureProducerResult;
|
|
16
|
+
export declare function importCubeSource(ctx: ImportContext): Promise<ImportResult>;
|
|
17
|
+
//# sourceMappingURL=cube-producer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cube-producer.d.ts","sourceRoot":"","sources":["../../src/lut/cube-producer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,yBAAyB,GACjC;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CAAC;AAE5D,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,wBAAwB,EAC/B,MAAM,EAAE,MAAM,GACb,yBAAyB,CA8B3B;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA+EhF"}
|
package/dist/parse-image.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/resize-image.ts","../src/parse-image.ts"],"names":[],"mappings":"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","file":"parse-image.mjs","sourcesContent":["import type { ImageErrorCode, ImageErrorDetailFor, ImageErrorFor } from '@forgeax/engine-types';\nimport { IMAGE_ERROR_HINTS } from '@forgeax/engine-types';\n\n// Per-code .expected string literals SSOT.\n// Mirrors ASSET_ERROR_HINTS / IMAGE_ERROR_HINTS shape (charter P5 consistent\n// abstraction). AI users surface .expected when a structured error needs to\n// describe the precondition that was violated.\nconst IMAGE_ERROR_EXPECTED: Readonly<Record<ImageErrorCode, string>> = {\n 'image-decode-failed': 'PNG / JPG byte stream decodes successfully',\n 'image-format-unsupported':\n \"mime is one of ['image/png', 'image/jpeg']; uploadTexture format <-> colorSpace family agrees\",\n 'image-dimension-out-of-bounds':\n 'width and height fall under device caps maxTextureDimension2D (or 16384 hard cap)',\n 'image-meta-missing':\n \"<source>.meta.json sidecar (importer: 'image') exists in the same directory\",\n 'image-hdr-decode-failed': 'Radiance RGBE header is valid and pixel data decodes successfully',\n // feat-20260521-sprite-atlas-animation M1 T-03 — vite-plugin-image atlas\n // hook .expected literals (plan-strategy section 2 D-2 + AC-10 a/b/c).\n // ImageErrorImpl construction path is unchanged: the new atlas-* errors\n // flow through `new ImageErrorImpl({ code: 'atlas-...', ...detail })` and\n // pick the .expected string up from this Record at construction time\n // (charter P3 explicit failure SSOT — AI users surface .expected next to\n // .hint after switch (err.code) without parsing the message).\n 'atlas-empty-input': 'images.length >= 1',\n 'atlas-size-exceeded':\n 'image width x height <= maxAtlasSize^2 and each image fits in the atlas footprint',\n 'atlas-region-mismatch': 'sum(regions[i].w x regions[i].h) <= atlasWidth x atlasHeight',\n 'image-surface-invalid':\n 'PixelSurface dimensions and authoring inputs satisfy the RGBA8 contract',\n};\n\n/** Runtime implementation of the correlated `ImageErrorFor<C>` envelope. */\nexport class ImageErrorImpl<C extends ImageErrorCode = ImageErrorCode>\n extends Error\n implements ImageErrorFor<C>\n{\n readonly code: C;\n readonly expected: string;\n readonly hint: string;\n readonly detail: ImageErrorDetailFor<C>;\n\n constructor(detail: ImageErrorDetailFor<C>) {\n const code = detail.code;\n const expected = IMAGE_ERROR_EXPECTED[code];\n const hint = IMAGE_ERROR_HINTS[code];\n super(`[ImageError ${code}] expected: ${expected}; hint: ${hint}`);\n this.name = 'ImageError';\n this.code = code;\n this.expected = expected;\n this.hint = hint;\n this.detail = detail;\n }\n}\n\n/** Construct a structured image error while preserving its code/detail pair. */\nexport function imageError<C extends ImageErrorCode>(\n detail: ImageErrorDetailFor<C>,\n): ImageErrorFor<C> {\n return new ImageErrorImpl<C>(detail);\n}\n\nexport { IMAGE_ERROR_EXPECTED };\n","/** Deterministically downscale tight-packed RGBA pixels with bilinear sampling. */\nexport function downscaleRgba(\n bytes: Uint8Array,\n width: number,\n height: number,\n maxDimension: number,\n): { readonly bytes: Uint8Array; readonly width: number; readonly height: number } {\n if (width <= maxDimension && height <= maxDimension) {\n return { bytes, width, height };\n }\n\n const scale = maxDimension / Math.max(width, height);\n const targetWidth = Math.max(1, Math.round(width * scale));\n const targetHeight = Math.max(1, Math.round(height * scale));\n const output = new Uint8Array(targetWidth * targetHeight * 4);\n const source = (x: number, y: number, channel: number): number =>\n bytes[(y * width + x) * 4 + channel] ?? 0;\n\n for (let y = 0; y < targetHeight; y += 1) {\n const sourceY = ((y + 0.5) * height) / targetHeight - 0.5;\n const y0 = Math.max(0, Math.floor(sourceY));\n const y1 = Math.min(height - 1, y0 + 1);\n const yWeight = Math.max(0, Math.min(1, sourceY - y0));\n for (let x = 0; x < targetWidth; x += 1) {\n const sourceX = ((x + 0.5) * width) / targetWidth - 0.5;\n const x0 = Math.max(0, Math.floor(sourceX));\n const x1 = Math.min(width - 1, x0 + 1);\n const xWeight = Math.max(0, Math.min(1, sourceX - x0));\n const outputOffset = (y * targetWidth + x) * 4;\n for (let channel = 0; channel < 4; channel += 1) {\n const top = source(x0, y0, channel) * (1 - xWeight) + source(x1, y0, channel) * xWeight;\n const bottom = source(x0, y1, channel) * (1 - xWeight) + source(x1, y1, channel) * xWeight;\n output[outputOffset + channel] = Math.round(top * (1 - yWeight) + bottom * yWeight);\n }\n }\n }\n\n return { bytes: output, width: targetWidth, height: targetHeight };\n}\n","import type { DecodedImage, ImageColorSpace, ImageError } from '@forgeax/engine-types';\nimport * as jpeg from 'jpeg-js';\n// Static imports of upng-js + jpeg-js. tsup external keeps both as runtime\n// require() calls so the browser bundle still tree-shakes them out when\n// only image-decoder-browser.ts (createImageBitmap) is reached at runtime.\n// Synchronous decode is required by the parseImage signature contract (see\n// plan-strategy section 3.3).\nimport * as UPNG from 'upng-js';\nimport { imageError } from './errors.js';\nimport { downscaleRgba } from './resize-image.js';\nimport type { Result } from './result.js';\nimport { err, ok } from './result.js';\n\nexport interface ParseImageOptions {\n /**\n * Maximum allowed dimension (width or height). Defaults to 16384 -- the\n * conservative WebGPU `maxTextureDimension2D` floor across desktop +\n * high-end mobile (research F-3 / spec). Test fixtures pass smaller\n * values to exercise the bounds-check code path with tiny fixtures.\n */\n readonly maxDimension?: number;\n\n /** Optional asset-owned cooked-payload target; source bytes remain unchanged. */\n readonly downscaleMaxDimension?: number;\n\n /**\n * Sidecar `colorSpace` carried over to the DecodedImage POD. Defaults to\n * 'srgb' (typical baseColor / albedo path; plan-strategy section 2.5).\n */\n readonly colorSpace?: ImageColorSpace;\n\n /**\n * Sidecar `mipmap` flag carried over to DecodedImage. Defaults to true\n * (auto-generate via runtime mipmap-generator; plan-strategy section 2.6).\n */\n readonly mipmap?: boolean;\n\n /**\n * Optional source path to embed in error.detail.path (decode-failed /\n * format-unsupported variants). decodeImageFromFile fills this in;\n * in-memory parseImage callers leave it empty.\n */\n readonly path?: string;\n}\n\nconst DEFAULT_MAX_DIMENSION = 16384;\n\nconst SUPPORTED_MIMES: readonly string[] = ['image/png', 'image/jpeg'];\n\ninterface UpngDecoded {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\ninterface JpegDecoded {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\n/**\n * Pure synchronous image decoder. Translates raw byte stream + mime hint\n * into a tight-packed RGBA `DecodedImage` POD. Surfaces all four\n * ImageErrorCode members through the structured `Result<DecodedImage,\n * ImageError>` return (charter P3 explicit failure; AGENTS.md \"Return\n * Result, never throw\").\n *\n * Decoder selection follows the mime hint:\n * - `image/png` -> upng-js `UPNG.decode` + `UPNG.toRGBA8`\n * - `image/jpeg` -> jpeg-js `decode` with `formatAsRGBA: true`\n * - other -> `image-format-unsupported`\n *\n * Decoded bytes are always tight-packed RGBA (`bytes.length === width *\n * height * 4`). `colorSpace` and `mipmap` are carried from the supplied\n * `ParseImageOptions` (decodeImageFromFile passes the sidecar settings).\n */\nexport function parseImage(\n bytes: Uint8Array,\n mime: string,\n opts: ParseImageOptions = {},\n): Result<DecodedImage, ImageError> {\n if (!SUPPORTED_MIMES.includes(mime)) {\n return err(\n imageError({\n code: 'image-format-unsupported',\n actualMime: mime,\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n }),\n );\n }\n\n let width = 0;\n let height = 0;\n let rgba: Uint8Array;\n\n try {\n if (mime === 'image/png') {\n const upngMod: {\n decode: (b: Uint8Array | ArrayBuffer) => UpngDecoded;\n toRGBA8: (i: UpngDecoded) => ArrayBuffer[];\n } = ((UPNG as unknown as { default?: typeof UPNG }).default ?? UPNG) as unknown as {\n decode: (b: Uint8Array | ArrayBuffer) => UpngDecoded;\n toRGBA8: (i: UpngDecoded) => ArrayBuffer[];\n };\n const decoded = upngMod.decode(bytes);\n width = decoded.width;\n height = decoded.height;\n const frames = upngMod.toRGBA8(decoded);\n const first = frames[0];\n if (first === undefined) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: 'UPNG.toRGBA8 returned no frames',\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n }),\n );\n }\n rgba = new Uint8Array(first);\n } else {\n // image/jpeg\n const jpegMod: {\n decode: (\n b: Uint8Array | ArrayBuffer,\n o?: { useTArray?: boolean; formatAsRGBA?: boolean },\n ) => JpegDecoded;\n } = ((jpeg as unknown as { default?: typeof jpeg }).default ?? jpeg) as unknown as {\n decode: (\n b: Uint8Array | ArrayBuffer,\n o?: { useTArray?: boolean; formatAsRGBA?: boolean },\n ) => JpegDecoded;\n };\n const decoded = jpegMod.decode(bytes, { useTArray: true, formatAsRGBA: true });\n width = decoded.width;\n height = decoded.height;\n rgba = decoded.data;\n }\n } catch (e) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: e instanceof Error ? e.message : String(e),\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n }),\n );\n }\n\n const downscaleLimit = opts.downscaleMaxDimension;\n if (downscaleLimit !== undefined && Number.isInteger(downscaleLimit) && downscaleLimit > 0) {\n const downscaled = downscaleRgba(rgba, width, height, downscaleLimit);\n rgba = downscaled.bytes;\n width = downscaled.width;\n height = downscaled.height;\n }\n\n const limit = opts.maxDimension ?? DEFAULT_MAX_DIMENSION;\n if (width > limit || height > limit) {\n return err(\n imageError({\n code: 'image-dimension-out-of-bounds',\n requested: { width, height },\n limit,\n }),\n );\n }\n\n return ok({\n bytes: rgba,\n width,\n height,\n mime: mime as 'image/png' | 'image/jpeg',\n colorSpace: opts.colorSpace ?? 'srgb',\n mipmap: opts.mipmap ?? true,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/resize-image.ts","../src/parse-image.ts"],"names":[],"mappings":"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","file":"parse-image.mjs","sourcesContent":["import type { ImageErrorCode, ImageErrorDetailFor, ImageErrorFor } from '@forgeax/engine-types';\nimport { IMAGE_ERROR_HINTS } from '@forgeax/engine-types';\n\n// Per-code .expected string literals SSOT.\n// Mirrors ASSET_ERROR_HINTS / IMAGE_ERROR_HINTS shape (charter P5 consistent\n// abstraction). AI users surface .expected when a structured error needs to\n// describe the precondition that was violated.\nconst IMAGE_ERROR_EXPECTED: Readonly<Record<ImageErrorCode, string>> = {\n 'image-decode-failed': 'PNG / JPG byte stream decodes successfully',\n 'image-format-unsupported':\n \"mime is one of ['image/png', 'image/jpeg']; uploadTexture format <-> colorSpace family agrees\",\n 'image-dimension-out-of-bounds':\n 'width and height fall under device caps maxTextureDimension2D (or 16384 hard cap)',\n 'image-meta-missing':\n \"<source>.meta.json sidecar (importer: 'image') exists in the same directory\",\n 'image-hdr-decode-failed': 'Radiance RGBE header is valid and pixel data decodes successfully',\n // feat-20260521-sprite-atlas-animation M1 T-03 — vite-plugin-image atlas\n // hook .expected literals (plan-strategy section 2 D-2 + AC-10 a/b/c).\n // ImageErrorImpl construction path is unchanged: the new atlas-* errors\n // flow through `new ImageErrorImpl({ code: 'atlas-...', ...detail })` and\n // pick the .expected string up from this Record at construction time\n // (charter P3 explicit failure SSOT — AI users surface .expected next to\n // .hint after switch (err.code) without parsing the message).\n 'atlas-empty-input': 'images.length >= 1',\n 'atlas-size-exceeded':\n 'image width x height <= maxAtlasSize^2 and each image fits in the atlas footprint',\n 'atlas-region-mismatch': 'sum(regions[i].w x regions[i].h) <= atlasWidth x atlasHeight',\n 'image-surface-invalid':\n 'PixelSurface dimensions and authoring inputs satisfy the RGBA8 contract',\n};\n\n/** Runtime implementation of the correlated `ImageErrorFor<C>` envelope. */\nexport class ImageErrorImpl<C extends ImageErrorCode = ImageErrorCode>\n extends Error\n implements ImageErrorFor<C>\n{\n readonly code: C;\n readonly expected: string;\n readonly hint: string;\n readonly detail: ImageErrorDetailFor<C>;\n\n constructor(detail: ImageErrorDetailFor<C>) {\n const code = detail.code;\n const expected = IMAGE_ERROR_EXPECTED[code];\n const hint = IMAGE_ERROR_HINTS[code];\n super(`[ImageError ${code}] expected: ${expected}; hint: ${hint}`);\n this.name = 'ImageError';\n this.code = code;\n this.expected = expected;\n this.hint = hint;\n this.detail = detail;\n }\n}\n\n/** Construct a structured image error while preserving its code/detail pair. */\nexport function imageError<C extends ImageErrorCode>(\n detail: ImageErrorDetailFor<C>,\n): ImageErrorFor<C> {\n return new ImageErrorImpl<C>(detail);\n}\n\nexport { IMAGE_ERROR_EXPECTED };\n\nexport type CubeParserErrorCode =\n | 'cube-header-invalid'\n | 'cube-size-invalid'\n | 'cube-domain-invalid'\n | 'cube-row-invalid'\n | 'cube-data-count-invalid';\n\nexport interface CubeParserError {\n readonly code: CubeParserErrorCode;\n readonly expected: string;\n readonly hint: string;\n readonly detail: {\n readonly sourceKey: string;\n readonly line?: number;\n readonly field: string;\n readonly actual: unknown;\n };\n}\n\nexport function cubeParserError(\n code: CubeParserErrorCode,\n sourceKey: string,\n field: string,\n actual: unknown,\n line?: number,\n): CubeParserError {\n const lineDetail = line === undefined ? '' : ` at line ${line}`;\n return {\n code,\n expected: `valid .cube source with LUT_3D_SIZE, DOMAIN_MIN/MAX, and exactly N^3 RGB rows${lineDetail}`,\n hint: `repair field ${field} in sourceKey ${sourceKey} and re-run the Node image producer`,\n detail: { sourceKey, field, actual, ...(line === undefined ? {} : { line }) },\n };\n}\n","/** Deterministically downscale tight-packed RGBA pixels with bilinear sampling. */\nexport function downscaleRgba(\n bytes: Uint8Array,\n width: number,\n height: number,\n maxDimension: number,\n): { readonly bytes: Uint8Array; readonly width: number; readonly height: number } {\n if (width <= maxDimension && height <= maxDimension) {\n return { bytes, width, height };\n }\n\n const scale = maxDimension / Math.max(width, height);\n const targetWidth = Math.max(1, Math.round(width * scale));\n const targetHeight = Math.max(1, Math.round(height * scale));\n const output = new Uint8Array(targetWidth * targetHeight * 4);\n const source = (x: number, y: number, channel: number): number =>\n bytes[(y * width + x) * 4 + channel] ?? 0;\n\n for (let y = 0; y < targetHeight; y += 1) {\n const sourceY = ((y + 0.5) * height) / targetHeight - 0.5;\n const y0 = Math.max(0, Math.floor(sourceY));\n const y1 = Math.min(height - 1, y0 + 1);\n const yWeight = Math.max(0, Math.min(1, sourceY - y0));\n for (let x = 0; x < targetWidth; x += 1) {\n const sourceX = ((x + 0.5) * width) / targetWidth - 0.5;\n const x0 = Math.max(0, Math.floor(sourceX));\n const x1 = Math.min(width - 1, x0 + 1);\n const xWeight = Math.max(0, Math.min(1, sourceX - x0));\n const outputOffset = (y * targetWidth + x) * 4;\n for (let channel = 0; channel < 4; channel += 1) {\n const top = source(x0, y0, channel) * (1 - xWeight) + source(x1, y0, channel) * xWeight;\n const bottom = source(x0, y1, channel) * (1 - xWeight) + source(x1, y1, channel) * xWeight;\n output[outputOffset + channel] = Math.round(top * (1 - yWeight) + bottom * yWeight);\n }\n }\n }\n\n return { bytes: output, width: targetWidth, height: targetHeight };\n}\n","import type { DecodedImage, ImageColorSpace, ImageError } from '@forgeax/engine-types';\nimport * as jpeg from 'jpeg-js';\n// Static imports of upng-js + jpeg-js. tsup external keeps both as runtime\n// require() calls so the browser bundle still tree-shakes them out when\n// only image-decoder-browser.ts (createImageBitmap) is reached at runtime.\n// Synchronous decode is required by the parseImage signature contract (see\n// plan-strategy section 3.3).\nimport * as UPNG from 'upng-js';\nimport { imageError } from './errors.js';\nimport { downscaleRgba } from './resize-image.js';\nimport type { Result } from './result.js';\nimport { err, ok } from './result.js';\n\nexport interface ParseImageOptions {\n /**\n * Maximum allowed dimension (width or height). Defaults to 16384 -- the\n * conservative WebGPU `maxTextureDimension2D` floor across desktop +\n * high-end mobile (research F-3 / spec). Test fixtures pass smaller\n * values to exercise the bounds-check code path with tiny fixtures.\n */\n readonly maxDimension?: number;\n\n /** Optional asset-owned cooked-payload target; source bytes remain unchanged. */\n readonly downscaleMaxDimension?: number;\n\n /**\n * Sidecar `colorSpace` carried over to the DecodedImage POD. Defaults to\n * 'srgb' (typical baseColor / albedo path; plan-strategy section 2.5).\n */\n readonly colorSpace?: ImageColorSpace;\n\n /**\n * Sidecar `mipmap` flag carried over to DecodedImage. Defaults to true\n * (auto-generate via runtime mipmap-generator; plan-strategy section 2.6).\n */\n readonly mipmap?: boolean;\n\n /**\n * Optional source path to embed in error.detail.path (decode-failed /\n * format-unsupported variants). decodeImageFromFile fills this in;\n * in-memory parseImage callers leave it empty.\n */\n readonly path?: string;\n}\n\nconst DEFAULT_MAX_DIMENSION = 16384;\n\nconst SUPPORTED_MIMES: readonly string[] = ['image/png', 'image/jpeg'];\n\ninterface UpngDecoded {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\ninterface JpegDecoded {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\n/**\n * Pure synchronous image decoder. Translates raw byte stream + mime hint\n * into a tight-packed RGBA `DecodedImage` POD. Surfaces all four\n * ImageErrorCode members through the structured `Result<DecodedImage,\n * ImageError>` return (charter P3 explicit failure; AGENTS.md \"Return\n * Result, never throw\").\n *\n * Decoder selection follows the mime hint:\n * - `image/png` -> upng-js `UPNG.decode` + `UPNG.toRGBA8`\n * - `image/jpeg` -> jpeg-js `decode` with `formatAsRGBA: true`\n * - other -> `image-format-unsupported`\n *\n * Decoded bytes are always tight-packed RGBA (`bytes.length === width *\n * height * 4`). `colorSpace` and `mipmap` are carried from the supplied\n * `ParseImageOptions` (decodeImageFromFile passes the sidecar settings).\n */\nexport function parseImage(\n bytes: Uint8Array,\n mime: string,\n opts: ParseImageOptions = {},\n): Result<DecodedImage, ImageError> {\n if (!SUPPORTED_MIMES.includes(mime)) {\n return err(\n imageError({\n code: 'image-format-unsupported',\n actualMime: mime,\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n }),\n );\n }\n\n let width = 0;\n let height = 0;\n let rgba: Uint8Array;\n\n try {\n if (mime === 'image/png') {\n const upngMod: {\n decode: (b: Uint8Array | ArrayBuffer) => UpngDecoded;\n toRGBA8: (i: UpngDecoded) => ArrayBuffer[];\n } = ((UPNG as unknown as { default?: typeof UPNG }).default ?? UPNG) as unknown as {\n decode: (b: Uint8Array | ArrayBuffer) => UpngDecoded;\n toRGBA8: (i: UpngDecoded) => ArrayBuffer[];\n };\n const decoded = upngMod.decode(bytes);\n width = decoded.width;\n height = decoded.height;\n const frames = upngMod.toRGBA8(decoded);\n const first = frames[0];\n if (first === undefined) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: 'UPNG.toRGBA8 returned no frames',\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n }),\n );\n }\n rgba = new Uint8Array(first);\n } else {\n // image/jpeg\n const jpegMod: {\n decode: (\n b: Uint8Array | ArrayBuffer,\n o?: { useTArray?: boolean; formatAsRGBA?: boolean },\n ) => JpegDecoded;\n } = ((jpeg as unknown as { default?: typeof jpeg }).default ?? jpeg) as unknown as {\n decode: (\n b: Uint8Array | ArrayBuffer,\n o?: { useTArray?: boolean; formatAsRGBA?: boolean },\n ) => JpegDecoded;\n };\n const decoded = jpegMod.decode(bytes, { useTArray: true, formatAsRGBA: true });\n width = decoded.width;\n height = decoded.height;\n rgba = decoded.data;\n }\n } catch (e) {\n return err(\n imageError({\n code: 'image-decode-failed',\n reason: e instanceof Error ? e.message : String(e),\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n }),\n );\n }\n\n const downscaleLimit = opts.downscaleMaxDimension;\n if (downscaleLimit !== undefined && Number.isInteger(downscaleLimit) && downscaleLimit > 0) {\n const downscaled = downscaleRgba(rgba, width, height, downscaleLimit);\n rgba = downscaled.bytes;\n width = downscaled.width;\n height = downscaled.height;\n }\n\n const limit = opts.maxDimension ?? DEFAULT_MAX_DIMENSION;\n if (width > limit || height > limit) {\n return err(\n imageError({\n code: 'image-dimension-out-of-bounds',\n requested: { width, height },\n limit,\n }),\n );\n }\n\n return ok({\n bytes: rgba,\n width,\n height,\n mime: mime as 'image/png' | 'image/jpeg',\n colorSpace: opts.colorSpace ?? 'srgb',\n mipmap: opts.mipmap ?? true,\n });\n}\n"]}
|
package/dist/to-asset-pack.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AssetCompression, DecodedImage, ImageMeta } from '@forgeax/engine-
|
|
|
2
2
|
/**
|
|
3
3
|
* External-asset-package envelope shape mirroring the
|
|
4
4
|
* `packages/pack/schema/meta.schema.json` $defs/ExternalAssetPackage.
|
|
5
|
-
* Consumed by `forgeax
|
|
5
|
+
* Consumed by `forgeax asset import` (unified CLI entry) +
|
|
6
6
|
* the image importer in M2 build-time path. The internal-text-package
|
|
7
7
|
* variant ($defs/InternalTextPackage) is owned by the gltf-loader feat.
|
|
8
8
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-image",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
"LICENSE"
|
|
55
55
|
],
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@forgeax/engine-codec": "0.1.
|
|
58
|
-
"@forgeax/engine-math": "0.1.
|
|
59
|
-
"@forgeax/engine-pack": "0.1.
|
|
60
|
-
"@forgeax/engine-types": "0.1.
|
|
57
|
+
"@forgeax/engine-codec": "0.1.29",
|
|
58
|
+
"@forgeax/engine-math": "0.1.29",
|
|
59
|
+
"@forgeax/engine-pack": "0.1.29",
|
|
60
|
+
"@forgeax/engine-types": "0.1.29",
|
|
61
61
|
"jpeg-js": "^0.4.4",
|
|
62
62
|
"pako": "^1.0.11",
|
|
63
63
|
"upng-js": "^2.1.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@forgeax/engine-import": "0.1.
|
|
66
|
+
"@forgeax/engine-import": "0.1.29",
|
|
67
67
|
"@types/node": "^20.14.0",
|
|
68
68
|
"@webgpu/types": "^0.1.71"
|
|
69
69
|
},
|