@su-engineering/heic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,445 @@
1
+ import { S as SourceColor, H as HeicWarning, a as SupportReport, D as DecodeOptions, b as DecodedImage, c as DecoderAdapter } from './types-Bv9KPnri.js';
2
+ export { A as AdapterRequest, d as AdapterResult, O as OutputColorSpace, e as Strategy, T as TransformsApplied } from './types-Bv9KPnri.js';
3
+
4
+ /**
5
+ * Typed error hierarchy. The parser consumes hostile input and the decode path
6
+ * fails in browser-specific ways, so every throw carries enough context to file
7
+ * a useful bug report without the reporter having to reproduce it.
8
+ */
9
+ interface HeicErrorContext {
10
+ /** ftyp major brand, when we got far enough to read it. */
11
+ brand?: string | undefined;
12
+ /** item_type of the item being worked on ('grid', 'hvc1', ...). */
13
+ itemType?: string | undefined;
14
+ /** Item ID being worked on. */
15
+ itemId?: number | undefined;
16
+ /** Strategy that produced the failure. */
17
+ strategy?: string | undefined;
18
+ /** Codec string handed to VideoDecoder. */
19
+ codec?: string | undefined;
20
+ /** Byte offset in the source buffer, for parse failures. */
21
+ offset?: number | undefined;
22
+ /** Four-character box type being read, for parse failures. */
23
+ box?: string | undefined;
24
+ }
25
+ declare class HeicError extends Error {
26
+ readonly context: HeicErrorContext;
27
+ constructor(message: string, context?: HeicErrorContext, options?: ErrorOptions);
28
+ }
29
+ /** The file is malformed, truncated, or uses a container feature we refuse to guess at. */
30
+ declare class HeicParseError extends HeicError {
31
+ constructor(message: string, context?: HeicErrorContext, options?: ErrorOptions);
32
+ }
33
+ /** The file is well-formed but this environment cannot decode it. */
34
+ declare class HeicUnsupportedError extends HeicError {
35
+ /** Strategies that were tried, and why each one was unavailable or failed. */
36
+ readonly attempts: ReadonlyArray<{
37
+ strategy: string;
38
+ reason: string;
39
+ }>;
40
+ constructor(message: string, attempts?: ReadonlyArray<{
41
+ strategy: string;
42
+ reason: string;
43
+ }>, context?: HeicErrorContext, options?: ErrorOptions);
44
+ }
45
+ /** A decoder was available and accepted the config, but decoding failed. */
46
+ declare class HeicDecodeError extends HeicError {
47
+ constructor(message: string, context?: HeicErrorContext, options?: ErrorOptions);
48
+ }
49
+ /** The caller's AbortSignal fired. */
50
+ declare class HeicAbortError extends HeicError {
51
+ constructor(message?: string, context?: HeicErrorContext, options?: ErrorOptions);
52
+ }
53
+
54
+ /**
55
+ * Bounds-checked big-endian byte reader.
56
+ *
57
+ * Every single read validates against the window before touching the buffer.
58
+ * This is the only place in the package that indexes raw bytes, so the security
59
+ * guarantee is enforceable by review: if it isn't going through Reader, it isn't
60
+ * reading the file.
61
+ *
62
+ * A Reader is a *window* onto a shared ArrayBuffer, not a copy. Sub-readers for
63
+ * nested boxes are free.
64
+ */
65
+ declare class Reader {
66
+ readonly bytes: Uint8Array;
67
+ private readonly view;
68
+ /** Absolute offset of this window's start within the underlying ArrayBuffer. */
69
+ readonly base: number;
70
+ /** Cursor, relative to the window start. */
71
+ private pos;
72
+ constructor(source: ArrayBuffer | Uint8Array, byteOffset?: number, byteLength?: number);
73
+ get length(): number;
74
+ get offset(): number;
75
+ /** Absolute offset of the cursor in the underlying buffer, for error reports. */
76
+ get absoluteOffset(): number;
77
+ get remaining(): number;
78
+ get eof(): boolean;
79
+ seek(to: number): void;
80
+ skip(count: number): void;
81
+ /**
82
+ * Throws unless `count` bytes are readable at `at` (default: the cursor).
83
+ * Callers that are about to allocate should call this first with the declared
84
+ * size, so a hostile length field fails here rather than in the allocator.
85
+ */
86
+ require(count: number, at?: number): void;
87
+ u8(): number;
88
+ u16(): number;
89
+ u24(): number;
90
+ u32(): number;
91
+ /**
92
+ * Returns a JS number, not a BigInt. Values above Number.MAX_SAFE_INTEGER are
93
+ * rejected rather than silently losing precision — a 9-petabyte box size is a
94
+ * malformed file, not something to accommodate.
95
+ */
96
+ u64(): number;
97
+ /** Reads a big-endian unsigned integer of 0, 1, 2, 4 or 8 bytes. `iloc` needs this. */
98
+ uint(byteCount: number): number;
99
+ /** Four-character box type. Non-printable bytes are escaped so error messages stay readable. */
100
+ fourCC(): string;
101
+ /** NUL-terminated UTF-8 string. Stops at the window end if the NUL is missing. */
102
+ cString(): string;
103
+ /** A view onto the next `count` bytes. No copy — do not retain past the buffer's life. */
104
+ view_(count: number): Uint8Array;
105
+ /** A copy of the next `count` bytes. Use when the result outlives the source buffer. */
106
+ copy(count: number): Uint8Array;
107
+ /** A sub-reader over `count` bytes, advancing this reader past them. */
108
+ sub(count: number): Reader;
109
+ /** A sub-reader over the rest of the window, without advancing this reader. */
110
+ peekRest(): Reader;
111
+ }
112
+
113
+ interface HvccNalArray {
114
+ arrayCompleteness: boolean;
115
+ nalUnitType: number;
116
+ /** Views into the source buffer, not copies. */
117
+ nalus: Uint8Array[];
118
+ }
119
+ /** Parsed HEVCDecoderConfigurationRecord (ISO/IEC 14496-15 §8.3.3.1). */
120
+ interface HvcC {
121
+ configurationVersion: number;
122
+ generalProfileSpace: number;
123
+ generalTierFlag: number;
124
+ generalProfileIdc: number;
125
+ generalProfileCompatibilityFlags: number;
126
+ /** Six bytes, big-endian order as stored. */
127
+ generalConstraintIndicatorFlags: Uint8Array;
128
+ generalLevelIdc: number;
129
+ minSpatialSegmentationIdc: number;
130
+ parallelismType: number;
131
+ chromaFormat: number;
132
+ bitDepthLumaMinus8: number;
133
+ bitDepthChromaMinus8: number;
134
+ avgFrameRate: number;
135
+ constantFrameRate: number;
136
+ numTemporalLayers: number;
137
+ temporalIdNested: number;
138
+ /** Byte width of the length prefix on each NAL unit in the item payload. */
139
+ lengthSizeMinusOne: number;
140
+ arrays: HvccNalArray[];
141
+ /** The raw record, which is what VideoDecoderConfig.description wants. */
142
+ raw: Uint8Array;
143
+ }
144
+ declare function parseHvcC(reader: Reader): HvcC;
145
+ /**
146
+ * Builds the RFC 6381 codec string for a VideoDecoderConfig.
147
+ *
148
+ * Format: `{fourcc}.{space}{profile_idc}.{compat}.{tier}{level}.{constraints}`
149
+ * A typical iPhone Main-profile record produces `hvc1.1.6.L93.B0`.
150
+ */
151
+ declare function hvccToCodecString(hvcc: HvcC, fourCC?: 'hvc1' | 'hev1'): string;
152
+ /**
153
+ * Builds an Annex B parameter-set prologue (VPS/SPS/PPS, each start-code
154
+ * prefixed) for the `hev1` fallback configuration mode.
155
+ */
156
+ declare function hvccToAnnexBPrologue(hvcc: HvcC): Uint8Array;
157
+ /**
158
+ * Rewrites a length-prefixed NAL unit stream to Annex B start codes.
159
+ * `lengthSize` is `lengthSizeMinusOne + 1` from the hvcC.
160
+ */
161
+ declare function lengthPrefixedToAnnexB(data: Uint8Array, lengthSize: number): Uint8Array;
162
+
163
+ interface ItemInfo {
164
+ itemId: number;
165
+ protectionIndex: number;
166
+ /** 'hvc1', 'grid', 'Exif', 'mime', ... Empty for version 0/1 infe boxes. */
167
+ itemType: string;
168
+ itemName: string;
169
+ contentType?: string;
170
+ /** Set when the infe declares the item hidden. */
171
+ hidden: boolean;
172
+ }
173
+ interface ItemExtent {
174
+ /** Offset, relative to whatever `constructionMethod` selects. */
175
+ offset: number;
176
+ length: number;
177
+ }
178
+ interface ItemLocation {
179
+ itemId: number;
180
+ /** 0 = offset into the file, 1 = offset into idat, 2 = offset into another item. */
181
+ constructionMethod: number;
182
+ baseOffset: number;
183
+ extents: ItemExtent[];
184
+ }
185
+ interface IspeProperty {
186
+ type: 'ispe';
187
+ width: number;
188
+ height: number;
189
+ }
190
+ interface HvccProperty {
191
+ type: 'hvcC';
192
+ hvcc: HvcC;
193
+ }
194
+ interface IrotProperty {
195
+ type: 'irot';
196
+ /** Counter-clockwise rotation in degrees. */
197
+ angle: 0 | 90 | 180 | 270;
198
+ }
199
+ interface ImirProperty {
200
+ type: 'imir';
201
+ /** Raw `axis` field. See render/transform.ts for the semantics, which are verified empirically. */
202
+ axis: 0 | 1;
203
+ }
204
+ interface ColrNclxProperty {
205
+ type: 'colr';
206
+ colorType: 'nclx';
207
+ primaries: number;
208
+ transfer: number;
209
+ matrix: number;
210
+ fullRange: boolean;
211
+ }
212
+ interface ColrIccProperty {
213
+ type: 'colr';
214
+ colorType: 'icc';
215
+ profile: Uint8Array;
216
+ }
217
+ interface PixiProperty {
218
+ type: 'pixi';
219
+ bitsPerChannel: number[];
220
+ }
221
+ interface ClapProperty {
222
+ type: 'clap';
223
+ widthN: number;
224
+ widthD: number;
225
+ heightN: number;
226
+ heightD: number;
227
+ horizOffN: number;
228
+ horizOffD: number;
229
+ vertOffN: number;
230
+ vertOffD: number;
231
+ }
232
+ interface AuxCProperty {
233
+ type: 'auxC';
234
+ auxType: string;
235
+ }
236
+ interface UnknownProperty {
237
+ type: 'unknown';
238
+ boxType: string;
239
+ }
240
+ type ItemProperty = IspeProperty | HvccProperty | IrotProperty | ImirProperty | ColrNclxProperty | ColrIccProperty | PixiProperty | ClapProperty | AuxCProperty | UnknownProperty;
241
+ interface PropertyAssociation {
242
+ /** 1-based index into the ipco child list. */
243
+ index: number;
244
+ essential: boolean;
245
+ }
246
+ interface ItemProperties {
247
+ /** 1-indexed in the file; index 0 is "no property" and is never present here. */
248
+ properties: ItemProperty[];
249
+ associations: Map<number, PropertyAssociation[]>;
250
+ }
251
+ /** referenceType → fromItemId → toItemIds, in file order. */
252
+ type ItemReferences = Map<string, Map<number, number[]>>;
253
+ interface HeifFile {
254
+ majorBrand: string;
255
+ minorVersion: number;
256
+ compatibleBrands: string[];
257
+ /** Item ID named by `pitm`. */
258
+ primaryItemId: number;
259
+ handlerType: string;
260
+ items: Map<number, ItemInfo>;
261
+ locations: Map<number, ItemLocation>;
262
+ itemProperties: ItemProperties;
263
+ references: ItemReferences;
264
+ /** Payload of the `idat` box, for construction_method 1. */
265
+ itemData: Uint8Array | undefined;
266
+ /** The whole source buffer, for construction_method 0 offsets. */
267
+ source: Uint8Array;
268
+ }
269
+ interface ParseOptions {
270
+ /**
271
+ * Tolerate a top-level box that runs past the end of the buffer, stopping the
272
+ * walk there instead of throwing. Only for detection, which is deliberately
273
+ * handed a truncated prefix; a full-file parse stays strict so that genuine
274
+ * truncation is reported rather than silently half-decoded.
275
+ */
276
+ truncated?: boolean;
277
+ }
278
+ declare function parseHeif(input: ArrayBuffer | Uint8Array, options?: ParseOptions): HeifFile;
279
+ /** Properties associated with an item, in `ipma` order. Order matters for transforms. */
280
+ declare function propertiesForItem(file: HeifFile, itemId: number): ItemProperty[];
281
+ /** First property of the given kind for an item, or undefined. */
282
+ declare function findProperty<T extends ItemProperty['type']>(properties: readonly ItemProperty[], type: T): Extract<ItemProperty, {
283
+ type: T;
284
+ }> | undefined;
285
+ /**
286
+ * Assembles an item's payload from its extents.
287
+ *
288
+ * Extents are concatenated in order. Every offset and length is validated
289
+ * against the actual buffer before a single byte is allocated, so a malformed
290
+ * `extent_length` fails here rather than in the allocator.
291
+ */
292
+ declare function readItemData(file: HeifFile, itemId: number): Uint8Array;
293
+
294
+ interface GridDescriptor {
295
+ rows: number;
296
+ columns: number;
297
+ /** Output dimensions the grid declares in its payload. */
298
+ outputWidth: number;
299
+ outputHeight: number;
300
+ /** Tile item IDs in raster order (left to right, top to bottom). */
301
+ tileItemIds: number[];
302
+ }
303
+ /**
304
+ * Parses an ImageGrid payload (HEIF §6.6.2.3.1).
305
+ *
306
+ * ```
307
+ * u8 version (0)
308
+ * u8 flags bit 0 = field_length_size: 0 -> u16 dims, 1 -> u32 dims
309
+ * u8 rows_minus_one
310
+ * u8 columns_minus_one
311
+ * output_width u16 or u32
312
+ * output_height u16 or u32
313
+ * ```
314
+ */
315
+ declare function parseGridPayload(payload: Uint8Array): Omit<GridDescriptor, 'tileItemIds'>;
316
+ interface GridWarning {
317
+ code: string;
318
+ message: string;
319
+ }
320
+ /**
321
+ * Reads the full grid description for an item, cross-checking the declared
322
+ * dimensions against `ispe` and the declared tile count against `dimg`.
323
+ */
324
+ declare function readGrid(file: HeifFile, itemId: number, warnings?: GridWarning[]): GridDescriptor;
325
+
326
+ /** A transform to apply, in the order the file associated it. */
327
+ type TransformOp = {
328
+ kind: 'rotate';
329
+ angle: 90 | 180 | 270;
330
+ } | {
331
+ kind: 'mirror';
332
+ axis: 0 | 1;
333
+ } | {
334
+ kind: 'crop';
335
+ width: number;
336
+ height: number;
337
+ offsetX: number;
338
+ offsetY: number;
339
+ };
340
+ interface TileGroup {
341
+ /** ipco property index of the hvcC these tiles share. */
342
+ configIndex: number;
343
+ hvcc: HvcC;
344
+ codec: string;
345
+ /** Indices into `ImagePlan.tiles`, in submission order. */
346
+ tileIndices: number[];
347
+ }
348
+ interface PlannedTile {
349
+ itemId: number;
350
+ /** Position in the composited canvas, before any transform. */
351
+ x: number;
352
+ y: number;
353
+ width: number;
354
+ height: number;
355
+ }
356
+ interface ImagePlan {
357
+ file: HeifFile;
358
+ primaryItemId: number;
359
+ isGrid: boolean;
360
+ /** Coded dimensions, before transforms. */
361
+ codedWidth: number;
362
+ codedHeight: number;
363
+ /** Dimensions as displayed, after transforms. What the caller sees. */
364
+ displayWidth: number;
365
+ displayHeight: number;
366
+ tiles: PlannedTile[];
367
+ /** Tiles grouped by decoder configuration. Normally exactly one group. */
368
+ tileGroups: TileGroup[];
369
+ /** Transforms in `ipma` association order. */
370
+ transforms: TransformOp[];
371
+ bitDepth: number;
372
+ sourceColor: SourceColor;
373
+ warnings: HeicWarning[];
374
+ }
375
+ /**
376
+ * Parses the container and works out everything every strategy needs.
377
+ *
378
+ * Always runs, whichever strategy ends up decoding: the native path needs the
379
+ * dimensions to validate its output, the WebCodecs path needs the tiles, and the
380
+ * wasm path needs the metadata we report back.
381
+ */
382
+ declare function planDecode(input: ArrayBuffer | Uint8Array): ImagePlan;
383
+
384
+ /**
385
+ * Reports what this environment can decode, before anything is downloaded.
386
+ *
387
+ * The point is to let a caller decide whether to preload the wasm fallback:
388
+ * `recommended === 'wasm'` means the ~1.2 MB codec will be needed, and knowing
389
+ * that at page load is much better than discovering it when a user drops a file.
390
+ *
391
+ * Cheap: `isConfigSupported` is a capability query, not a decoder, and the native
392
+ * probe decodes a 471-byte inline image.
393
+ */
394
+ declare function probeSupport(): Promise<SupportReport>;
395
+
396
+ type BinaryInput = Blob | ArrayBuffer | Uint8Array;
397
+ interface IsHeicResult {
398
+ isHeic: boolean;
399
+ /** ftyp major brand, when the file had one. */
400
+ brand?: string | undefined;
401
+ /** item_type of the primary item ('hvc1', 'grid', 'av01', ...). */
402
+ primaryItemType?: string | undefined;
403
+ /** What the primary item is coded with. 'av1' means this is an AVIF. */
404
+ coding?: 'hevc' | 'av1' | 'unknown' | undefined;
405
+ }
406
+ /**
407
+ * Identifies a HEIC file from its contents.
408
+ *
409
+ * Reads the `ftyp` brands and the primary item type — never the filename or the
410
+ * MIME type the browser guessed, both of which are routinely wrong for photos
411
+ * copied off a phone.
412
+ *
413
+ * Given a Blob, only the first 64 KB are read, because this gets called
414
+ * speculatively on every file a user drops. AVIF also uses the `mif1` brand, so
415
+ * the result is discriminated rather than boolean: `{ isHeic: false, coding:
416
+ * 'av1' }` tells a caller to route the file to an AVIF decoder instead of
417
+ * treating it as garbage.
418
+ */
419
+ declare function isHeic(input: BinaryInput): Promise<IsHeicResult>;
420
+ /**
421
+ * Decodes a HEIC image to an `ImageBitmap`.
422
+ *
423
+ * The container is parsed first regardless of which strategy ends up decoding —
424
+ * parsing costs microseconds and every strategy needs its output. The cascade
425
+ * then picks a *decode* path:
426
+ *
427
+ * 1. `createImageBitmap` — free, works on Safari and some Chrome builds
428
+ * 2. WebCodecs `VideoDecoder` — ~15 KB of JS, hardware decode, most Chromium
429
+ * 3. a wasm adapter — ~1.2 MB, only if the caller supplied one
430
+ *
431
+ * Step 3 never happens behind the caller's back: without `wasmLoader` or a
432
+ * registered adapter, a file that needs wasm throws `HeicUnsupportedError`
433
+ * naming what was missing, rather than silently fetching a megabyte.
434
+ */
435
+ declare function decodeHeic(input: BinaryInput, options?: DecodeOptions): Promise<DecodedImage>;
436
+ /**
437
+ * Registers a wasm (or other) fallback adapter for every subsequent decode.
438
+ *
439
+ * An alternative to passing `wasmLoader` on each call. Either way the caller
440
+ * chooses when the megabyte is paid for; nothing is fetched implicitly.
441
+ */
442
+ declare function registerDecoderAdapter(adapter: DecoderAdapter | undefined): void;
443
+ declare function getRegisteredAdapter(): DecoderAdapter | undefined;
444
+
445
+ export { type BinaryInput, DecodeOptions, DecodedImage, DecoderAdapter, type GridDescriptor, HeicAbortError, HeicDecodeError, HeicError, type HeicErrorContext, HeicParseError, HeicUnsupportedError, HeicWarning, type HeifFile, type HvcC, type ImagePlan, type IsHeicResult, type ItemInfo, type ItemLocation, type ItemProperties, type ItemProperty, type ItemReferences, type PlannedTile, SourceColor, SupportReport, type TileGroup, type TransformOp, decodeHeic, findProperty, getRegisteredAdapter, hvccToAnnexBPrologue, hvccToCodecString, isHeic, lengthPrefixedToAnnexB, parseGridPayload, parseHeif, parseHvcC, planDecode, probeSupport, propertiesForItem, readGrid, readItemData, registerDecoderAdapter };