@woosh/meep-engine 3.9.0 → 3.10.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.
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +3 -2
- package/src/avif/encode_image_source.d.ts +70 -0
- package/src/avif/encode_image_source.d.ts.map +1 -0
- package/src/avif/encode_image_source.js +117 -0
- package/src/avif/encoder_worker.js +15 -126
- package/src/avif/index.d.ts +4 -2
- package/src/avif/index.js +21 -2
- package/src/avif/native/DECISIONS.md +537 -0
- package/src/avif/native/NOTICE.md +61 -0
- package/src/avif/native/api/AvifDecoder.d.ts +67 -0
- package/src/avif/native/api/AvifDecoder.d.ts.map +1 -0
- package/src/avif/native/api/AvifDecoder.js +121 -0
- package/src/avif/native/api/apply_transformations.d.ts +30 -0
- package/src/avif/native/api/apply_transformations.d.ts.map +1 -0
- package/src/avif/native/api/apply_transformations.js +120 -0
- package/src/avif/native/api/avif_to_sampler2d.d.ts +25 -0
- package/src/avif/native/api/avif_to_sampler2d.d.ts.map +1 -0
- package/src/avif/native/api/avif_to_sampler2d.js +42 -0
- package/src/avif/native/api/convert_to_rgba.d.ts +54 -0
- package/src/avif/native/api/convert_to_rgba.d.ts.map +1 -0
- package/src/avif/native/api/convert_to_rgba.js +204 -0
- package/src/avif/native/api/decode_avif.d.ts +24 -0
- package/src/avif/native/api/decode_avif.d.ts.map +1 -0
- package/src/avif/native/api/decode_avif.js +111 -0
- package/src/avif/native/api/decode_image_item.d.ts +18 -0
- package/src/avif/native/api/decode_image_item.d.ts.map +1 -0
- package/src/avif/native/api/decode_image_item.js +234 -0
- package/src/avif/native/api/encode_avif.d.ts +73 -0
- package/src/avif/native/api/encode_avif.d.ts.map +1 -0
- package/src/avif/native/api/encode_avif.js +488 -0
- package/src/avif/native/api/sampler2d_to_avif.d.ts +22 -0
- package/src/avif/native/api/sampler2d_to_avif.d.ts.map +1 -0
- package/src/avif/native/api/sampler2d_to_avif.js +108 -0
- package/src/avif/native/av1/decode/Av1FrameContext.d.ts +286 -0
- package/src/avif/native/av1/decode/Av1FrameContext.d.ts.map +1 -0
- package/src/avif/native/av1/decode/Av1FrameContext.js +622 -0
- package/src/avif/native/av1/decode/block_decoded.d.ts +27 -0
- package/src/avif/native/av1/decode/block_decoded.d.ts.map +1 -0
- package/src/avif/native/av1/decode/block_decoded.js +59 -0
- package/src/avif/native/av1/decode/decode_av1_still.d.ts +22 -0
- package/src/avif/native/av1/decode/decode_av1_still.d.ts.map +1 -0
- package/src/avif/native/av1/decode/decode_av1_still.js +309 -0
- package/src/avif/native/av1/decode/decode_coefficients.d.ts +20 -0
- package/src/avif/native/av1/decode/decode_coefficients.d.ts.map +1 -0
- package/src/avif/native/av1/decode/decode_coefficients.js +259 -0
- package/src/avif/native/av1/decode/decode_palette.d.ts +28 -0
- package/src/avif/native/av1/decode/decode_palette.d.ts.map +1 -0
- package/src/avif/native/av1/decode/decode_palette.js +442 -0
- package/src/avif/native/av1/decode/decode_tile.d.ts +14 -0
- package/src/avif/native/av1/decode/decode_tile.d.ts.map +1 -0
- package/src/avif/native/av1/decode/decode_tile.js +1213 -0
- package/src/avif/native/av1/decode/get_scan.d.ts +16 -0
- package/src/avif/native/av1/decode/get_scan.d.ts.map +1 -0
- package/src/avif/native/av1/decode/get_scan.js +138 -0
- package/src/avif/native/av1/decode/read_lr.d.ts +35 -0
- package/src/avif/native/av1/decode/read_lr.d.ts.map +1 -0
- package/src/avif/native/av1/decode/read_lr.js +277 -0
- package/src/avif/native/av1/decode/read_transform_type.d.ts +19 -0
- package/src/avif/native/av1/decode/read_transform_type.d.ts.map +1 -0
- package/src/avif/native/av1/decode/read_transform_type.js +92 -0
- package/src/avif/native/av1/decode/reconstruct.d.ts +36 -0
- package/src/avif/native/av1/decode/reconstruct.d.ts.map +1 -0
- package/src/avif/native/av1/decode/reconstruct.js +241 -0
- package/src/avif/native/av1/decode/transform_type.d.ts +49 -0
- package/src/avif/native/av1/decode/transform_type.d.ts.map +1 -0
- package/src/avif/native/av1/decode/transform_type.js +137 -0
- package/src/avif/native/av1/encode/Av1BlockSnapshot.d.ts +81 -0
- package/src/avif/native/av1/encode/Av1BlockSnapshot.d.ts.map +1 -0
- package/src/avif/native/av1/encode/Av1BlockSnapshot.js +299 -0
- package/src/avif/native/av1/encode/Av1EncodeContext.d.ts +384 -0
- package/src/avif/native/av1/encode/Av1EncodeContext.d.ts.map +1 -0
- package/src/avif/native/av1/encode/Av1EncodeContext.js +590 -0
- package/src/avif/native/av1/encode/build_headers.d.ts +50 -0
- package/src/avif/native/av1/encode/build_headers.d.ts.map +1 -0
- package/src/avif/native/av1/encode/build_headers.js +359 -0
- package/src/avif/native/av1/encode/choose_chroma_alpha.d.ts +26 -0
- package/src/avif/native/av1/encode/choose_chroma_alpha.d.ts.map +1 -0
- package/src/avif/native/av1/encode/choose_chroma_alpha.js +90 -0
- package/src/avif/native/av1/encode/choose_filters.d.ts +21 -0
- package/src/avif/native/av1/encode/choose_filters.d.ts.map +1 -0
- package/src/avif/native/av1/encode/choose_filters.js +318 -0
- package/src/avif/native/av1/encode/choose_restoration.d.ts +35 -0
- package/src/avif/native/av1/encode/choose_restoration.d.ts.map +1 -0
- package/src/avif/native/av1/encode/choose_restoration.js +616 -0
- package/src/avif/native/av1/encode/decode_effort.d.ts +57 -0
- package/src/avif/native/av1/encode/decode_effort.d.ts.map +1 -0
- package/src/avif/native/av1/encode/decode_effort.js +59 -0
- package/src/avif/native/av1/encode/encode_av1_still.d.ts +17 -0
- package/src/avif/native/av1/encode/encode_av1_still.d.ts.map +1 -0
- package/src/avif/native/av1/encode/encode_av1_still.js +194 -0
- package/src/avif/native/av1/encode/encode_effort.d.ts +56 -0
- package/src/avif/native/av1/encode/encode_effort.d.ts.map +1 -0
- package/src/avif/native/av1/encode/encode_effort.js +97 -0
- package/src/avif/native/av1/encode/encode_tile.d.ts +26 -0
- package/src/avif/native/av1/encode/encode_tile.d.ts.map +1 -0
- package/src/avif/native/av1/encode/encode_tile.js +2331 -0
- package/src/avif/native/av1/encode/quantise.d.ts +51 -0
- package/src/avif/native/av1/encode/quantise.d.ts.map +1 -0
- package/src/avif/native/av1/encode/quantise.js +212 -0
- package/src/avif/native/av1/encode/write_coefficients.d.ts +42 -0
- package/src/avif/native/av1/encode/write_coefficients.d.ts.map +1 -0
- package/src/avif/native/av1/encode/write_coefficients.js +367 -0
- package/src/avif/native/av1/encode/write_lr.d.ts +33 -0
- package/src/avif/native/av1/encode/write_lr.d.ts.map +1 -0
- package/src/avif/native/av1/encode/write_lr.js +174 -0
- package/src/avif/native/av1/encode/write_transform_type.d.ts +21 -0
- package/src/avif/native/av1/encode/write_transform_type.d.ts.map +1 -0
- package/src/avif/native/av1/encode/write_transform_type.js +70 -0
- package/src/avif/native/av1/entropy/CdfContext.d.ts +167 -0
- package/src/avif/native/av1/entropy/CdfContext.d.ts.map +1 -0
- package/src/avif/native/av1/entropy/CdfContext.js +433 -0
- package/src/avif/native/av1/entropy/SymbolReader.d.ts +103 -0
- package/src/avif/native/av1/entropy/SymbolReader.d.ts.map +1 -0
- package/src/avif/native/av1/entropy/SymbolReader.js +317 -0
- package/src/avif/native/av1/entropy/SymbolWriter.d.ts +107 -0
- package/src/avif/native/av1/entropy/SymbolWriter.d.ts.map +1 -0
- package/src/avif/native/av1/entropy/SymbolWriter.js +352 -0
- package/src/avif/native/av1/entropy/coefficient_context.d.ts +71 -0
- package/src/avif/native/av1/entropy/coefficient_context.d.ts.map +1 -0
- package/src/avif/native/av1/entropy/coefficient_context.js +325 -0
- package/src/avif/native/av1/entropy/partition_cdf.d.ts +54 -0
- package/src/avif/native/av1/entropy/partition_cdf.d.ts.map +1 -0
- package/src/avif/native/av1/entropy/partition_cdf.js +98 -0
- package/src/avif/native/av1/entropy/symbol_cost.d.ts +37 -0
- package/src/avif/native/av1/entropy/symbol_cost.d.ts.map +1 -0
- package/src/avif/native/av1/entropy/symbol_cost.js +76 -0
- package/src/avif/native/av1/filter/cdef.d.ts +15 -0
- package/src/avif/native/av1/filter/cdef.d.ts.map +1 -0
- package/src/avif/native/av1/filter/cdef.js +313 -0
- package/src/avif/native/av1/filter/loop_filter.d.ts +14 -0
- package/src/avif/native/av1/filter/loop_filter.d.ts.map +1 -0
- package/src/avif/native/av1/filter/loop_filter.js +377 -0
- package/src/avif/native/av1/filter/loop_restoration.d.ts +42 -0
- package/src/avif/native/av1/filter/loop_restoration.d.ts.map +1 -0
- package/src/avif/native/av1/filter/loop_restoration.js +451 -0
- package/src/avif/native/av1/filter/superres.d.ts +18 -0
- package/src/avif/native/av1/filter/superres.d.ts.map +1 -0
- package/src/avif/native/av1/filter/superres.js +86 -0
- package/src/avif/native/av1/grain/FilmGrainState.d.ts +62 -0
- package/src/avif/native/av1/grain/FilmGrainState.d.ts.map +1 -0
- package/src/avif/native/av1/grain/FilmGrainState.js +87 -0
- package/src/avif/native/av1/grain/film_grain.d.ts +16 -0
- package/src/avif/native/av1/grain/film_grain.d.ts.map +1 -0
- package/src/avif/native/av1/grain/film_grain.js +623 -0
- package/src/avif/native/av1/obu/FrameHeader.d.ts +248 -0
- package/src/avif/native/av1/obu/FrameHeader.d.ts.map +1 -0
- package/src/avif/native/av1/obu/FrameHeader.js +381 -0
- package/src/avif/native/av1/obu/ObuHeader.d.ts +53 -0
- package/src/avif/native/av1/obu/ObuHeader.d.ts.map +1 -0
- package/src/avif/native/av1/obu/ObuHeader.js +62 -0
- package/src/avif/native/av1/obu/SequenceHeader.d.ts +157 -0
- package/src/avif/native/av1/obu/SequenceHeader.d.ts.map +1 -0
- package/src/avif/native/av1/obu/SequenceHeader.js +225 -0
- package/src/avif/native/av1/obu/for_each_obu.d.ts +24 -0
- package/src/avif/native/av1/obu/for_each_obu.d.ts.map +1 -0
- package/src/avif/native/av1/obu/for_each_obu.js +50 -0
- package/src/avif/native/av1/obu/parse_frame_header.d.ts +18 -0
- package/src/avif/native/av1/obu/parse_frame_header.d.ts.map +1 -0
- package/src/avif/native/av1/obu/parse_frame_header.js +841 -0
- package/src/avif/native/av1/obu/parse_obu_header.d.ts +23 -0
- package/src/avif/native/av1/obu/parse_obu_header.d.ts.map +1 -0
- package/src/avif/native/av1/obu/parse_obu_header.js +69 -0
- package/src/avif/native/av1/obu/parse_sequence_header.d.ts +17 -0
- package/src/avif/native/av1/obu/parse_sequence_header.d.ts.map +1 -0
- package/src/avif/native/av1/obu/parse_sequence_header.js +263 -0
- package/src/avif/native/av1/obu/write_frame_header.d.ts +16 -0
- package/src/avif/native/av1/obu/write_frame_header.d.ts.map +1 -0
- package/src/avif/native/av1/obu/write_frame_header.js +579 -0
- package/src/avif/native/av1/obu/write_sequence_header.d.ts +17 -0
- package/src/avif/native/av1/obu/write_sequence_header.d.ts.map +1 -0
- package/src/avif/native/av1/obu/write_sequence_header.js +189 -0
- package/src/avif/native/av1/predict/IntraPredictionState.d.ts +49 -0
- package/src/avif/native/av1/predict/IntraPredictionState.d.ts.map +1 -0
- package/src/avif/native/av1/predict/IntraPredictionState.js +58 -0
- package/src/avif/native/av1/predict/intra_filter_type.d.ts +17 -0
- package/src/avif/native/av1/predict/intra_filter_type.d.ts.map +1 -0
- package/src/avif/native/av1/predict/intra_filter_type.js +79 -0
- package/src/avif/native/av1/predict/predict_chroma_from_luma.d.ts +50 -0
- package/src/avif/native/av1/predict/predict_chroma_from_luma.d.ts.map +1 -0
- package/src/avif/native/av1/predict/predict_chroma_from_luma.js +153 -0
- package/src/avif/native/av1/predict/predict_intra.d.ts +42 -0
- package/src/avif/native/av1/predict/predict_intra.d.ts.map +1 -0
- package/src/avif/native/av1/predict/predict_intra.js +803 -0
- package/src/avif/native/av1/predict/predict_palette.d.ts +23 -0
- package/src/avif/native/av1/predict/predict_palette.d.ts.map +1 -0
- package/src/avif/native/av1/predict/predict_palette.js +36 -0
- package/src/avif/native/av1/tables/av1_symbols.d.ts +411 -0
- package/src/avif/native/av1/tables/av1_symbols.d.ts.map +1 -0
- package/src/avif/native/av1/tables/av1_symbols.js +419 -0
- package/src/avif/native/av1/tables/block_tables.d.ts +181 -0
- package/src/avif/native/av1/tables/block_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/block_tables.js +270 -0
- package/src/avif/native/av1/tables/cdf_tables.d.ts +961 -0
- package/src/avif/native/av1/tables/cdf_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/cdf_tables.js +1893 -0
- package/src/avif/native/av1/tables/coefficient_tables.d.ts +41 -0
- package/src/avif/native/av1/tables/coefficient_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/coefficient_tables.js +82 -0
- package/src/avif/native/av1/tables/derived_block_tables.d.ts +15 -0
- package/src/avif/native/av1/tables/derived_block_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/derived_block_tables.js +23 -0
- package/src/avif/native/av1/tables/filter_tables.d.ts +171 -0
- package/src/avif/native/av1/tables/filter_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/filter_tables.js +313 -0
- package/src/avif/native/av1/tables/grain_tables.d.ts +11 -0
- package/src/avif/native/av1/tables/grain_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/grain_tables.js +190 -0
- package/src/avif/native/av1/tables/prediction_tables.d.ts +141 -0
- package/src/avif/native/av1/tables/prediction_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/prediction_tables.js +223 -0
- package/src/avif/native/av1/tables/quantizer_matrix_tables.d.ts +21 -0
- package/src/avif/native/av1/tables/quantizer_matrix_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/quantizer_matrix_tables.js +38 -0
- package/src/avif/native/av1/tables/quantizer_tables.d.ts +21 -0
- package/src/avif/native/av1/tables/quantizer_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/quantizer_tables.js +160 -0
- package/src/avif/native/av1/tables/scan_tables.d.ts +321 -0
- package/src/avif/native/av1/tables/scan_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/scan_tables.js +727 -0
- package/src/avif/native/av1/tables/segmentation_tables.d.ts +31 -0
- package/src/avif/native/av1/tables/segmentation_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/segmentation_tables.js +48 -0
- package/src/avif/native/av1/tables/transform_tables.d.ts +91 -0
- package/src/avif/native/av1/tables/transform_tables.d.ts.map +1 -0
- package/src/avif/native/av1/tables/transform_tables.js +140 -0
- package/src/avif/native/av1/tables/unpack_table.d.ts +33 -0
- package/src/avif/native/av1/tables/unpack_table.d.ts.map +1 -0
- package/src/avif/native/av1/tables/unpack_table.js +73 -0
- package/src/avif/native/av1/transform/forward_transform_2d.d.ts +61 -0
- package/src/avif/native/av1/transform/forward_transform_2d.d.ts.map +1 -0
- package/src/avif/native/av1/transform/forward_transform_2d.js +574 -0
- package/src/avif/native/av1/transform/inverse_transform_1d.d.ts +76 -0
- package/src/avif/native/av1/transform/inverse_transform_1d.d.ts.map +1 -0
- package/src/avif/native/av1/transform/inverse_transform_1d.js +590 -0
- package/src/avif/native/av1/transform/inverse_transform_2d.d.ts +46 -0
- package/src/avif/native/av1/transform/inverse_transform_2d.d.ts.map +1 -0
- package/src/avif/native/av1/transform/inverse_transform_2d.js +180 -0
- package/src/avif/native/av1/util/ceil_log2.d.ts +13 -0
- package/src/avif/native/av1/util/ceil_log2.d.ts.map +1 -0
- package/src/avif/native/av1/util/ceil_log2.js +18 -0
- package/src/avif/native/av1/util/floor_log2.d.ts +20 -0
- package/src/avif/native/av1/util/floor_log2.d.ts.map +1 -0
- package/src/avif/native/av1/util/floor_log2.js +20 -0
- package/src/avif/native/av1/util/round2.d.ts +22 -0
- package/src/avif/native/av1/util/round2.d.ts.map +1 -0
- package/src/avif/native/av1/util/round2.js +27 -0
- package/src/avif/native/av1/util/round2_signed.d.ts +14 -0
- package/src/avif/native/av1/util/round2_signed.d.ts.map +1 -0
- package/src/avif/native/av1/util/round2_signed.js +17 -0
- package/src/avif/native/av1/util/tile_log2.d.ts +16 -0
- package/src/avif/native/av1/util/tile_log2.d.ts.map +1 -0
- package/src/avif/native/av1/util/tile_log2.js +23 -0
- package/src/avif/native/bitstream/BitReader.d.ts +174 -0
- package/src/avif/native/bitstream/BitReader.d.ts.map +1 -0
- package/src/avif/native/bitstream/BitReader.js +391 -0
- package/src/avif/native/bitstream/BitWriter.d.ts +122 -0
- package/src/avif/native/bitstream/BitWriter.d.ts.map +1 -0
- package/src/avif/native/bitstream/BitWriter.js +351 -0
- package/src/avif/native/color/ColourTransform.d.ts +62 -0
- package/src/avif/native/color/ColourTransform.d.ts.map +1 -0
- package/src/avif/native/color/ColourTransform.js +189 -0
- package/src/avif/native/color/YuvImage.d.ts +51 -0
- package/src/avif/native/color/YuvImage.d.ts.map +1 -0
- package/src/avif/native/color/YuvImage.js +66 -0
- package/src/avif/native/color/clamp_sample.d.ts +18 -0
- package/src/avif/native/color/clamp_sample.d.ts.map +1 -0
- package/src/avif/native/color/clamp_sample.js +25 -0
- package/src/avif/native/color/linear_to_transfer.d.ts +24 -0
- package/src/avif/native/color/linear_to_transfer.d.ts.map +1 -0
- package/src/avif/native/color/linear_to_transfer.js +126 -0
- package/src/avif/native/color/primaries.d.ts +20 -0
- package/src/avif/native/color/primaries.d.ts.map +1 -0
- package/src/avif/native/color/primaries.js +94 -0
- package/src/avif/native/color/rgb_row_to_ycbcr.d.ts +20 -0
- package/src/avif/native/color/rgb_row_to_ycbcr.d.ts.map +1 -0
- package/src/avif/native/color/rgb_row_to_ycbcr.js +78 -0
- package/src/avif/native/color/transfer_to_linear.d.ts +49 -0
- package/src/avif/native/color/transfer_to_linear.d.ts.map +1 -0
- package/src/avif/native/color/transfer_to_linear.js +171 -0
- package/src/avif/native/color/upsample_chroma_row.d.ts +35 -0
- package/src/avif/native/color/upsample_chroma_row.d.ts.map +1 -0
- package/src/avif/native/color/upsample_chroma_row.js +89 -0
- package/src/avif/native/color/ycbcr_row_to_rgb.d.ts +23 -0
- package/src/avif/native/color/ycbcr_row_to_rgb.d.ts.map +1 -0
- package/src/avif/native/color/ycbcr_row_to_rgb.js +78 -0
- package/src/avif/native/heif/AvifFile.d.ts +112 -0
- package/src/avif/native/heif/AvifFile.d.ts.map +1 -0
- package/src/avif/native/heif/AvifFile.js +142 -0
- package/src/avif/native/heif/ItemProperty.d.ts +244 -0
- package/src/avif/native/heif/ItemProperty.d.ts.map +1 -0
- package/src/avif/native/heif/ItemProperty.js +321 -0
- package/src/avif/native/heif/find_item_property.d.ts +17 -0
- package/src/avif/native/heif/find_item_property.d.ts.map +1 -0
- package/src/avif/native/heif/find_item_property.js +28 -0
- package/src/avif/native/heif/find_item_references.d.ts +16 -0
- package/src/avif/native/heif/find_item_references.d.ts.map +1 -0
- package/src/avif/native/heif/find_item_references.js +29 -0
- package/src/avif/native/heif/find_items_referencing.d.ts +17 -0
- package/src/avif/native/heif/find_items_referencing.d.ts.map +1 -0
- package/src/avif/native/heif/find_items_referencing.js +36 -0
- package/src/avif/native/heif/parse_avif_file.d.ts +15 -0
- package/src/avif/native/heif/parse_avif_file.d.ts.map +1 -0
- package/src/avif/native/heif/parse_avif_file.js +383 -0
- package/src/avif/native/heif/parse_image_grid.d.ts +18 -0
- package/src/avif/native/heif/parse_image_grid.d.ts.map +1 -0
- package/src/avif/native/heif/parse_image_grid.js +48 -0
- package/src/avif/native/heif/parse_item_property.d.ts +18 -0
- package/src/avif/native/heif/parse_item_property.d.ts.map +1 -0
- package/src/avif/native/heif/parse_item_property.js +243 -0
- package/src/avif/native/heif/read_item_data.d.ts +22 -0
- package/src/avif/native/heif/read_item_data.d.ts.map +1 -0
- package/src/avif/native/heif/read_item_data.js +97 -0
- package/src/avif/native/heif/write_avif_file.d.ts +50 -0
- package/src/avif/native/heif/write_avif_file.d.ts.map +1 -0
- package/src/avif/native/heif/write_avif_file.js +348 -0
- package/src/avif/native/heif/write_item_property.d.ts +16 -0
- package/src/avif/native/heif/write_item_property.d.ts.map +1 -0
- package/src/avif/native/heif/write_item_property.js +182 -0
- package/src/avif/native/index.d.ts +14 -0
- package/src/avif/native/index.d.ts.map +1 -0
- package/src/avif/native/index.js +56 -0
- package/src/avif/native/isobmff/BoxHeader.d.ts +50 -0
- package/src/avif/native/isobmff/BoxHeader.d.ts.map +1 -0
- package/src/avif/native/isobmff/BoxHeader.js +57 -0
- package/src/avif/native/isobmff/BoxWriter.d.ts +118 -0
- package/src/avif/native/isobmff/BoxWriter.d.ts.map +1 -0
- package/src/avif/native/isobmff/BoxWriter.js +264 -0
- package/src/avif/native/isobmff/ByteCursor.d.ts +125 -0
- package/src/avif/native/isobmff/ByteCursor.d.ts.map +1 -0
- package/src/avif/native/isobmff/ByteCursor.js +298 -0
- package/src/avif/native/isobmff/for_each_box.d.ts +23 -0
- package/src/avif/native/isobmff/for_each_box.d.ts.map +1 -0
- package/src/avif/native/isobmff/for_each_box.js +39 -0
- package/src/avif/native/isobmff/read_box_header.d.ts +22 -0
- package/src/avif/native/isobmff/read_box_header.d.ts.map +1 -0
- package/src/avif/native/isobmff/read_box_header.js +59 -0
- package/src/avif/native/isobmff/read_full_box_header.d.ts +26 -0
- package/src/avif/native/isobmff/read_full_box_header.d.ts.map +1 -0
- package/src/avif/native/isobmff/read_full_box_header.js +33 -0
- package/src/avif/threaded_image_encoder.d.ts +7 -2
- package/src/avif/threaded_image_encoder.d.ts.map +1 -1
- package/src/avif/threaded_image_encoder.js +107 -21
- package/src/core/binary/BinaryBuffer.d.ts +40 -0
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +100 -0
- package/src/core/binary/hash/crc32.d.ts +16 -0
- package/src/core/binary/hash/crc32.d.ts.map +1 -0
- package/src/core/binary/hash/crc32.js +96 -0
- package/src/core/binary/utf8/utf8_decode.d.ts +21 -0
- package/src/core/binary/utf8/utf8_decode.d.ts.map +1 -0
- package/src/core/binary/utf8/utf8_decode.js +43 -0
- package/src/core/binary/utf8/utf8_encode_into.d.ts +20 -0
- package/src/core/binary/utf8/utf8_encode_into.d.ts.map +1 -0
- package/src/core/binary/utf8/utf8_encode_into.js +48 -0
- package/src/core/binary/utf8/utf8_encoded_length.d.ts +14 -0
- package/src/core/binary/utf8/utf8_encoded_length.d.ts.map +1 -0
- package/src/core/binary/utf8/utf8_encoded_length.js +36 -0
- package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +417 -0
- package/src/core/color/COLOR_REVIEW_2026_08_28.md +779 -0
- package/src/core/color/Color.d.ts +128 -20
- package/src/core/color/Color.d.ts.map +1 -1
- package/src/core/color/Color.js +959 -866
- package/src/core/color/PQ/PQ_constants.d.ts +10 -0
- package/src/core/color/PQ/PQ_constants.d.ts.map +1 -1
- package/src/core/color/PQ/PQ_constants.js +10 -0
- package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -1
- package/src/core/color/PQ/linear_to_PQ.js +6 -0
- package/src/core/color/REC709_PRIMARIES.d.ts +50 -0
- package/src/core/color/REC709_PRIMARIES.d.ts.map +1 -0
- package/src/core/color/REC709_PRIMARIES.js +53 -0
- package/src/core/color/construct/color_from_hex.d.ts +21 -0
- package/src/core/color/construct/color_from_hex.d.ts.map +1 -0
- package/src/core/color/construct/color_from_hex.js +33 -0
- package/src/core/color/construct/color_from_temperature.d.ts +22 -0
- package/src/core/color/construct/color_from_temperature.d.ts.map +1 -0
- package/src/core/color/construct/color_from_temperature.js +33 -0
- package/src/core/color/construct/color_from_uint24.d.ts +19 -0
- package/src/core/color/construct/color_from_uint24.d.ts.map +1 -0
- package/src/core/color/construct/color_from_uint24.js +26 -0
- package/src/core/color/construct/color_from_uint32.d.ts +20 -0
- package/src/core/color/construct/color_from_uint32.d.ts.map +1 -0
- package/src/core/color/construct/color_from_uint32.js +39 -0
- package/src/core/color/construct/color_gray.d.ts +17 -0
- package/src/core/color/construct/color_gray.d.ts.map +1 -0
- package/src/core/color/construct/color_gray.js +22 -0
- package/src/core/color/hex/hex2rgb.d.ts +1 -11
- package/src/core/color/hex/hex2rgb.d.ts.map +1 -1
- package/src/core/color/hex/hex2rgb.js +7 -24
- package/src/core/color/hex/hex_to_rgb.d.ts +18 -0
- package/src/core/color/hex/hex_to_rgb.d.ts.map +1 -0
- package/src/core/color/hex/hex_to_rgb.js +62 -0
- package/src/core/color/hex/rgb2hex.d.ts +1 -8
- package/src/core/color/hex/rgb2hex.d.ts.map +1 -1
- package/src/core/color/hex/rgb2hex.js +7 -13
- package/src/core/color/hex/rgb_to_hex.d.ts +9 -0
- package/src/core/color/hex/rgb_to_hex.d.ts.map +1 -0
- package/src/core/color/hex/rgb_to_hex.js +13 -0
- package/src/core/color/hex/rgba_to_hex.d.ts +19 -0
- package/src/core/color/hex/rgba_to_hex.d.ts.map +1 -0
- package/src/core/color/hex/rgba_to_hex.js +23 -0
- package/src/core/color/hsv/hsv2rgb.d.ts +1 -12
- package/src/core/color/hsv/hsv2rgb.d.ts.map +1 -1
- package/src/core/color/hsv/hsv2rgb.js +8 -21
- package/src/core/color/hsv/hsv2rgb_float.d.ts +1 -12
- package/src/core/color/hsv/hsv2rgb_float.d.ts.map +1 -1
- package/src/core/color/hsv/hsv2rgb_float.js +8 -56
- package/src/core/color/hsv/hsv_to_rgb.d.ts +13 -0
- package/src/core/color/hsv/hsv_to_rgb.d.ts.map +1 -0
- package/src/core/color/hsv/hsv_to_rgb.js +39 -0
- package/src/core/color/hsv/hsv_to_rgb_uint8.d.ts +13 -0
- package/src/core/color/hsv/hsv_to_rgb_uint8.d.ts.map +1 -0
- package/src/core/color/hsv/hsv_to_rgb_uint8.js +21 -0
- package/src/core/color/hsv/hue_to_rgb_sextant.d.ts +24 -0
- package/src/core/color/hsv/hue_to_rgb_sextant.d.ts.map +1 -0
- package/src/core/color/hsv/hue_to_rgb_sextant.js +67 -0
- package/src/core/color/hsv/rgb2hsv.d.ts +1 -12
- package/src/core/color/hsv/rgb2hsv.d.ts.map +1 -1
- package/src/core/color/hsv/rgb2hsv.js +7 -42
- package/src/core/color/hsv/rgb_to_hsv.d.ts +13 -0
- package/src/core/color/hsv/rgb_to_hsv.d.ts.map +1 -0
- package/src/core/color/hsv/rgb_to_hsv.js +42 -0
- package/src/core/color/hunt/hpe_to_xyz.d.ts +2 -0
- package/src/core/color/hunt/hpe_to_xyz.d.ts.map +1 -0
- package/src/core/color/hunt/hpe_to_xyz.js +4 -0
- package/src/core/color/hunt/xyz_to_hpe.d.ts +1 -6
- package/src/core/color/hunt/xyz_to_hpe.d.ts.map +1 -1
- package/src/core/color/hunt/xyz_to_hpe.js +12 -15
- package/src/core/color/illuminant/D65_spd_tabulated.d.ts.map +1 -1
- package/src/core/color/illuminant/D65_spd_tabulated.js +107 -104
- package/src/core/color/int2rgb.d.ts +1 -10
- package/src/core/color/int2rgb.d.ts.map +1 -1
- package/src/core/color/int2rgb.js +7 -12
- package/src/core/color/int_to_rgb.d.ts +21 -0
- package/src/core/color/int_to_rgb.d.ts.map +1 -0
- package/src/core/color/int_to_rgb.js +33 -0
- package/src/core/color/kelvin/kelvin_to_rgb.d.ts.map +1 -1
- package/src/core/color/kelvin/kelvin_to_rgb.js +22 -2
- package/src/core/color/kelvin/rgb_to_kelvin.d.ts +22 -2
- package/src/core/color/kelvin/rgb_to_kelvin.d.ts.map +1 -1
- package/src/core/color/kelvin/rgb_to_kelvin.js +22 -2
- package/src/core/color/lab/lab_to_xyz.d.ts +20 -0
- package/src/core/color/lab/lab_to_xyz.d.ts.map +1 -0
- package/src/core/color/lab/lab_to_xyz.js +69 -0
- package/src/core/color/oklab/gamut_clip_adaptive_l0.d.ts +34 -0
- package/src/core/color/oklab/gamut_clip_adaptive_l0.d.ts.map +1 -0
- package/src/core/color/oklab/gamut_clip_adaptive_l0.js +46 -0
- package/src/core/color/oklab/gamut_clip_at_l0.d.ts +33 -0
- package/src/core/color/oklab/gamut_clip_at_l0.d.ts.map +1 -0
- package/src/core/color/oklab/gamut_clip_at_l0.js +84 -0
- package/src/core/color/oklab/gamut_clip_preserve_chroma.d.ts +24 -0
- package/src/core/color/oklab/gamut_clip_preserve_chroma.d.ts.map +1 -0
- package/src/core/color/oklab/gamut_clip_preserve_chroma.js +37 -0
- package/src/core/color/oklab/gamut_clip_project_to_lcusp.d.ts +23 -0
- package/src/core/color/oklab/gamut_clip_project_to_lcusp.d.ts.map +1 -0
- package/src/core/color/oklab/gamut_clip_project_to_lcusp.js +46 -0
- package/src/core/color/oklab/linear_srgb_to_okhsl.d.ts +19 -0
- package/src/core/color/oklab/linear_srgb_to_okhsl.d.ts.map +1 -0
- package/src/core/color/oklab/linear_srgb_to_okhsl.js +93 -0
- package/src/core/color/oklab/linear_srgb_to_okhsv.d.ts +12 -4
- package/src/core/color/oklab/linear_srgb_to_okhsv.d.ts.map +1 -1
- package/src/core/color/oklab/linear_srgb_to_okhsv.js +108 -84
- package/src/core/color/oklab/linear_srgb_to_oklab.d.ts +13 -4
- package/src/core/color/oklab/linear_srgb_to_oklab.d.ts.map +1 -1
- package/src/core/color/oklab/linear_srgb_to_oklab.js +28 -19
- package/src/core/color/oklab/okhsl_chroma_bounds.d.ts +51 -0
- package/src/core/color/oklab/okhsl_chroma_bounds.d.ts.map +1 -0
- package/src/core/color/oklab/okhsl_chroma_bounds.js +128 -0
- package/src/core/color/oklab/okhsl_to_linear_srgb.d.ts +19 -0
- package/src/core/color/oklab/okhsl_to_linear_srgb.d.ts.map +1 -0
- package/src/core/color/oklab/okhsl_to_linear_srgb.js +85 -0
- package/src/core/color/oklab/oklab_to_oklch.d.ts +24 -0
- package/src/core/color/oklab/oklab_to_oklch.d.ts.map +1 -0
- package/src/core/color/oklab/oklab_to_oklch.js +35 -0
- package/src/core/color/oklab/oklch_to_oklab.d.ts +18 -0
- package/src/core/color/oklab/oklch_to_oklab.d.ts.map +1 -0
- package/src/core/color/oklab/oklch_to_oklab.js +26 -0
- package/src/core/color/oklab/toe.d.ts +12 -0
- package/src/core/color/oklab/toe.d.ts.map +1 -1
- package/src/core/color/oklab/toe.js +34 -22
- package/src/core/color/operations/color_add.d.ts +21 -0
- package/src/core/color/operations/color_add.d.ts.map +1 -0
- package/src/core/color/operations/color_add.js +23 -0
- package/src/core/color/operations/color_add_scaled.d.ts +20 -0
- package/src/core/color/operations/color_add_scaled.d.ts.map +1 -0
- package/src/core/color/operations/color_add_scaled.js +30 -0
- package/src/core/color/operations/color_clamp.d.ts +22 -0
- package/src/core/color/operations/color_clamp.d.ts.map +1 -0
- package/src/core/color/operations/color_clamp.js +25 -0
- package/src/core/color/operations/color_darken.d.ts +2 -1
- package/src/core/color/operations/color_darken.d.ts.map +1 -1
- package/src/core/color/operations/color_darken.js +25 -24
- package/src/core/color/operations/color_desaturate.d.ts +2 -1
- package/src/core/color/operations/color_desaturate.d.ts.map +1 -1
- package/src/core/color/operations/color_desaturate.js +25 -24
- package/src/core/color/operations/color_get_hsl.d.ts +18 -0
- package/src/core/color/operations/color_get_hsl.d.ts.map +1 -0
- package/src/core/color/operations/color_get_hsl.js +60 -0
- package/src/core/color/operations/color_is_in_gamut.d.ts +20 -0
- package/src/core/color/operations/color_is_in_gamut.d.ts.map +1 -0
- package/src/core/color/operations/color_is_in_gamut.js +30 -0
- package/src/core/color/operations/color_lerp.d.ts +12 -3
- package/src/core/color/operations/color_lerp.d.ts.map +1 -1
- package/src/core/color/operations/color_lerp.js +55 -46
- package/src/core/color/operations/color_lighten.d.ts +9 -5
- package/src/core/color/operations/color_lighten.d.ts.map +1 -1
- package/src/core/color/operations/color_lighten.js +29 -24
- package/src/core/color/operations/color_mix_okhsv_channel.d.ts +24 -0
- package/src/core/color/operations/color_mix_okhsv_channel.d.ts.map +1 -0
- package/src/core/color/operations/color_mix_okhsv_channel.js +48 -0
- package/src/core/color/operations/color_multiply_rgb.d.ts +21 -0
- package/src/core/color/operations/color_multiply_rgb.d.ts.map +1 -0
- package/src/core/color/operations/color_multiply_rgb.js +26 -0
- package/src/core/color/operations/color_saturate.d.ts +9 -5
- package/src/core/color/operations/color_saturate.d.ts.map +1 -1
- package/src/core/color/operations/color_saturate.js +29 -24
- package/src/core/color/operations/color_scale_okhsv_channel.d.ts +1 -1
- package/src/core/color/operations/color_scale_okhsv_channel.d.ts.map +1 -1
- package/src/core/color/operations/color_scale_okhsv_channel.js +58 -45
- package/src/core/color/operations/color_srgb_apply.d.ts +28 -0
- package/src/core/color/operations/color_srgb_apply.d.ts.map +1 -0
- package/src/core/color/operations/color_srgb_apply.js +40 -0
- package/src/core/color/operations/color_sub.d.ts +16 -0
- package/src/core/color/operations/color_sub.d.ts.map +1 -0
- package/src/core/color/operations/color_sub.js +18 -0
- package/src/core/color/parse_color.d.ts +10 -1
- package/src/core/color/parse_color.d.ts.map +1 -1
- package/src/core/color/parse_color.js +100 -84
- package/src/core/color/parse_color_normalized.d.ts +24 -0
- package/src/core/color/parse_color_normalized.d.ts.map +1 -0
- package/src/core/color/parse_color_normalized.js +38 -0
- package/src/core/color/rgb2uint24.d.ts +1 -8
- package/src/core/color/rgb2uint24.d.ts.map +1 -1
- package/src/core/color/rgb2uint24.js +7 -20
- package/src/core/color/rgb2uint32.d.ts +1 -9
- package/src/core/color/rgb2uint32.d.ts.map +1 -1
- package/src/core/color/rgb2uint32.js +7 -18
- package/src/core/color/rgb_to_luminance.d.ts +14 -5
- package/src/core/color/rgb_to_luminance.d.ts.map +1 -1
- package/src/core/color/rgb_to_luminance.js +24 -13
- package/src/core/color/rgb_to_uint24.d.ts +9 -0
- package/src/core/color/rgb_to_uint24.d.ts.map +1 -0
- package/src/core/color/rgb_to_uint24.js +20 -0
- package/src/core/color/rgb_to_uint32.d.ts +10 -0
- package/src/core/color/rgb_to_uint32.d.ts.map +1 -0
- package/src/core/color/rgb_to_uint32.js +21 -0
- package/src/core/color/sRGB/linear_to_sRGB.d.ts +7 -0
- package/src/core/color/sRGB/linear_to_sRGB.d.ts.map +1 -1
- package/src/core/color/sRGB/linear_to_sRGB.js +39 -32
- package/src/core/color/xyz/hpe_to_xyz.d.ts +24 -0
- package/src/core/color/xyz/hpe_to_xyz.d.ts.map +1 -0
- package/src/core/color/xyz/hpe_to_xyz.js +39 -0
- package/src/core/color/xyz/rgb_to_xyz.d.ts +17 -2
- package/src/core/color/xyz/rgb_to_xyz.d.ts.map +1 -1
- package/src/core/color/xyz/rgb_to_xyz.js +44 -18
- package/src/core/color/xyz/xyz_cmf_tabulated.d.ts.map +1 -1
- package/src/core/color/xyz/xyz_cmf_tabulated.js +217 -211
- package/src/core/color/xyz/xyz_cmf_wyman.d.ts +4 -0
- package/src/core/color/xyz/xyz_cmf_wyman.d.ts.map +1 -1
- package/src/core/color/xyz/xyz_cmf_wyman.js +74 -65
- package/src/core/color/xyz/xyz_to_hpe.d.ts +18 -0
- package/src/core/color/xyz/xyz_to_hpe.d.ts.map +1 -0
- package/src/core/color/xyz/xyz_to_hpe.js +33 -0
- package/src/core/color/xyz/xyz_to_rgb.d.ts +17 -2
- package/src/core/color/xyz/xyz_to_rgb.d.ts.map +1 -1
- package/src/core/color/xyz/xyz_to_rgb.js +39 -16
- package/src/core/color/ycxcz/xyz_to_ycxcz.d.ts +7 -2
- package/src/core/color/ycxcz/xyz_to_ycxcz.d.ts.map +1 -1
- package/src/core/color/ycxcz/xyz_to_ycxcz.js +33 -28
- package/src/core/events/signal/Signal.js +820 -820
- package/src/core/math/clamp_index.d.ts +18 -0
- package/src/core/math/clamp_index.d.ts.map +1 -0
- package/src/core/math/clamp_index.js +23 -0
- package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +803 -0
- package/src/engine/asset/loaders/image/jpeg/JpegFrame.d.ts +5 -3
- package/src/engine/asset/loaders/image/jpeg/JpegFrame.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/jpeg/JpegFrame.js +5 -3
- package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.d.ts +2 -1
- package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.js +2 -1
- package/src/engine/asset/loaders/image/jpeg/JpegImage.d.ts +17 -0
- package/src/engine/asset/loaders/image/jpeg/JpegImage.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/jpeg/JpegImage.js +197 -157
- package/src/engine/asset/loaders/image/jpeg/idct8x8_float.d.ts +0 -1
- package/src/engine/asset/loaders/image/jpeg/idct8x8_float.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/jpeg/idct8x8_float.js +0 -1
- package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.d.ts +28 -0
- package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.js +31 -0
- package/src/engine/asset/loaders/image/png/PNG.d.ts +3 -18
- package/src/engine/asset/loaders/image/png/PNG.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/PNG.js +3 -139
- package/src/engine/asset/loaders/image/png/PNGReader.d.ts +16 -16
- package/src/engine/asset/loaders/image/png/PNGReader.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/PNGReader.js +143 -160
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_iTXt.js +3 -3
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_zTXt.js +3 -3
- package/src/engine/asset/loaders/image/png/crc32.d.ts +1 -15
- package/src/engine/asset/loaders/image/png/crc32.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/crc32.js +5 -89
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts +12 -10
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +20 -45
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts +2 -1
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +2 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts +2 -1
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +2 -0
- package/src/engine/asset/loaders/image/png/filter/png_unfilter.d.ts +22 -0
- package/src/engine/asset/loaders/image/png/filter/png_unfilter.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/filter/png_unfilter.js +68 -0
- package/src/engine/asset/loaders/image/png/png_inflate.d.ts +20 -0
- package/src/engine/asset/loaders/image/png/png_inflate.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/png_inflate.js +85 -0
- package/src/engine/asset/loaders/image/png/png_unpack_samples.d.ts +22 -0
- package/src/engine/asset/loaders/image/png/png_unpack_samples.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/png_unpack_samples.js +38 -0
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +135 -132
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +16 -3
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1157 -1149
- package/src/engine/graphics/render/frame_graph/FrameGraph.js +900 -900
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +72 -81
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +86 -77
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +451 -0
- package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +625 -0
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts +14 -3
- package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts.map +1 -1
- package/src/shade/renderer/scene/serialization/decode_image_source_hdr.js +50 -51
- package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
- package/src/shade/renderer/scene/serialization/deserialize_scene.js +531 -521
- package/src/shade/renderer/scene/serialization/write_image_source.d.ts.map +1 -1
- package/src/shade/renderer/scene/serialization/write_image_source.js +6 -4
- package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.js +34 -32
- package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts +31 -0
- package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.js +62 -31
- package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts +10 -0
- package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.js +32 -22
- package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.js +22 -21
- package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts +6 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.js +110 -104
- package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.js +23 -21
- package/src/view/elements/ColorPickerView.js +296 -296
- package/src/avif/codec/dec/Readme.md +0 -14
- package/src/avif/codec/dec/avif_dec.cpp +0 -234
- package/src/avif/codec/dec/avif_dec.d.ts +0 -3
- package/src/avif/codec/dec/avif_dec.d.ts.map +0 -1
- package/src/avif/codec/dec/avif_dec.js +0 -16
- package/src/avif/codec/dec/avif_dec.wasm +0 -0
- package/src/avif/codec/enc/README.md +0 -37
- package/src/avif/codec/enc/avif_enc.cpp +0 -216
- package/src/avif/codec/enc/avif_enc.d.ts +0 -3
- package/src/avif/codec/enc/avif_enc.d.ts.map +0 -1
- package/src/avif/codec/enc/avif_enc.js +0 -6144
- package/src/avif/codec/enc/avif_enc.wasm +0 -0
- package/src/avif/codec/enc/avif_enc_mt.d.ts +0 -3
- package/src/avif/codec/enc/avif_enc_mt.d.ts.map +0 -1
- package/src/avif/codec/enc/avif_enc_mt.js +0 -6811
- package/src/avif/codec/enc/avif_enc_mt.wasm +0 -0
- package/src/avif/codec/enc/avif_enc_mt.worker.d.mts +0 -2
- package/src/avif/codec/enc/avif_enc_mt.worker.d.mts.map +0 -1
- package/src/avif/codec/enc/avif_enc_mt.worker.d.ts +0 -11
- package/src/avif/codec/enc/avif_enc_mt.worker.d.ts.map +0 -1
- package/src/avif/codec/enc/avif_enc_mt.worker.js +0 -171
- package/src/avif/codec/enc/avif_enc_mt.worker.mjs +0 -1
- package/src/avif/decode.d.ts +0 -3
- package/src/avif/decode.d.ts.map +0 -1
- package/src/avif/decode.js +0 -42
- package/src/avif/encode.d.ts +0 -3
- package/src/avif/encode.d.ts.map +0 -1
- package/src/avif/encode.js +0 -75
- package/src/avif/meta.d.ts +0 -24
- package/src/avif/meta.d.ts.map +0 -1
- package/src/avif/meta.js +0 -23
- package/src/avif/tsconfig.tsbuildinfo +0 -1
- package/src/avif/utils.d.ts +0 -17
- package/src/avif/utils.d.ts.map +0 -1
- package/src/avif/utils.js +0 -31
- package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts +0 -23
- package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts.map +0 -1
- package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.js +0 -214
- package/src/engine/asset/loaders/image/png/inflate.d.ts +0 -7
- package/src/engine/asset/loaders/image/png/inflate.d.ts.map +0 -1
- package/src/engine/asset/loaders/image/png/inflate.js +0 -20
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
# Image codecs — duplication and shared-construct review
|
|
2
|
+
|
|
3
|
+
Review, 2026-08-28. Alex Goldring / Company Named Limited.
|
|
4
|
+
|
|
5
|
+
Scope: `src/avif/native/`, `src/engine/asset/loaders/image/png/`,
|
|
6
|
+
`src/engine/asset/loaders/image/jpeg/`. Everything below is an itemised proposal; nothing has been
|
|
7
|
+
changed.
|
|
8
|
+
|
|
9
|
+
Placed here rather than under `src/avif/native/` because the majority of the *cross-tree* items land
|
|
10
|
+
on the loader side, and `src/avif/native/DECISIONS.md` is a decision log rather than a review.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 0. Verdict
|
|
15
|
+
|
|
16
|
+
Three decoders, written at three different times against three different sets of conventions, doing
|
|
17
|
+
substantially the same job.
|
|
18
|
+
|
|
19
|
+
**The AVIF tree is the disciplined one.** It already has the abstractions the other two lack —
|
|
20
|
+
a bounds-checked cursor, a container walker, a parameterised colour transform, generated tables, a
|
|
21
|
+
decision log. Its own duplication is residue rather than structure: about **fifteen verbatim
|
|
22
|
+
copy-paste sites**, almost all of them the encode/decode mirror that
|
|
23
|
+
[D30](../../../../avif/native/DECISIONS.md) already set out to eliminate and did not quite finish.
|
|
24
|
+
Cheap to close, and closing it is the difference between a mirror that stays honest and one that
|
|
25
|
+
drifts.
|
|
26
|
+
|
|
27
|
+
**The cross-tree duplication is the expensive one, and it is not symmetrical.** PNG and JPEG each
|
|
28
|
+
reimplement, badly and privately, things AVIF already has generalised — channel expansion to RGBA,
|
|
29
|
+
YCbCr conversion, chroma upsampling, chunk walking, bounds checking. The single most valuable move
|
|
30
|
+
in this document is not a merge: it is **registering AVIF and JPEG behind the `Codec` interface that
|
|
31
|
+
already exists** ([Codec.js](codec/Codec.js)) so that all three formats reach the engine through one
|
|
32
|
+
door with one result shape. Today PNG goes through a worker, JPEG goes nowhere at all, and AVIF
|
|
33
|
+
bypasses the asset-loader system entirely via `avif_to_sampler2d`.
|
|
34
|
+
|
|
35
|
+
**One number worth stating up front:** `JpegImage.getData` hard-codes the coefficients `1.402`,
|
|
36
|
+
`-0.3441363`, `-0.71413636`, `1.772`. Those are *exactly* what
|
|
37
|
+
`colour_transform_init(t, MC_BT_601, ·, 1, 8)` computes
|
|
38
|
+
([ColourTransform.js:98](../../../../avif/native/color/ColourTransform.js#L98)). The JPEG decoder is
|
|
39
|
+
a hand-inlined special case of a general transform that already exists two directories away.
|
|
40
|
+
|
|
41
|
+
To be clear about what that is and is not worth: JFIF is full-range BT.601, so those constants are
|
|
42
|
+
*correct* and JPEG has no need for the other matrices. The win is not new format coverage. It is
|
|
43
|
+
that the derivation `cr_to_r = 2(1 − kr)`, `cb_to_g = −2·kb(1 − kb)/kg`, … is computed once in
|
|
44
|
+
`ColourTransform` and hand-transcribed twice more — as BT.601 literals in `JpegImage`, as BT.709
|
|
45
|
+
literals in `core/color/YCbCr/` — and that adopting the row-based transform is what makes C4
|
|
46
|
+
possible.
|
|
47
|
+
|
|
48
|
+
**What not to do:** do not merge `ByteCursor` into `BinaryBuffer`, and do not try to fold
|
|
49
|
+
`encode_tile` into `decode_tile`. D10 and D30 already settled both, with reasons that still hold.
|
|
50
|
+
Section 6 says so explicitly so nobody re-litigates them from this document.
|
|
51
|
+
|
|
52
|
+
Priority ordering is in section 7.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Status
|
|
57
|
+
|
|
58
|
+
Eleven items were selected and have landed. **File and line references below are as of the review
|
|
59
|
+
date and pre-date those changes** — the proposals for the remaining items still hold, but their
|
|
60
|
+
anchors have moved.
|
|
61
|
+
|
|
62
|
+
| Item | Commit | Departure from the proposal |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| D1, D4 | `chore(image): the unreachable halves…` | none |
|
|
65
|
+
| P4 | `refactor(png): the reader keeps one cursor…` | also removed `this.bytes`, assigned and never read |
|
|
66
|
+
| J2, J4 | `refactor(jpeg): frame components become a Map…` | none |
|
|
67
|
+
| C7 | `refactor(binary): crc32 moves to core…` | none |
|
|
68
|
+
| C8 | `refactor(binary): one UTF-8 codec…` | three functions, not two: splitting `utf8_encoded_length` off is what lets `BoxWriter` grow once and encode straight into the buffer |
|
|
69
|
+
| A10 | `refactor(avif): the small numeric helpers…` | six inline `clz32` sites, not seven — see the correction in A10 |
|
|
70
|
+
| P2 | `refactor(png): one inflate path…` | no deprecation shim; the export changes name, arity and return type |
|
|
71
|
+
| P3 | `fix(png): bit depths of 1, 2 and 4 decode correctly…` | the suspected bug was real, and worse than described — see P3 |
|
|
72
|
+
| C4 | `refactor(jpeg): one row sampler…` | **`upsample_chroma_row` is not reusable here** — see the correction in C4 |
|
|
73
|
+
|
|
74
|
+
Not taken this round: C1, C2, C3, C5, C6, A1–A9, A11, A12, D2, D3, D5, D6, P1, P5, P6, J1, J3.
|
|
75
|
+
D5 and D6 came along with P4 and J2 respectively.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 1. Cross-package items
|
|
80
|
+
|
|
81
|
+
These are the ones where a construct exists in one tree and is re-invented in another.
|
|
82
|
+
|
|
83
|
+
### C1 — One `Codec` door for all three formats *(highest value)*
|
|
84
|
+
|
|
85
|
+
**Today.** Three formats, three unrelated entry conventions, three result shapes:
|
|
86
|
+
|
|
87
|
+
| Format | Entry point | Result shape |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| PNG | worker → [ImageDecoderWorker.js:3](ImageDecoderWorker.js#L3) → `PNGReader` | `{data, width, height, itemSize, bitDepth}` |
|
|
90
|
+
| JPEG | [jpeg_decode.js:9](jpeg/jpeg_decode.js#L9) — **not reachable from any loader** | `{data, width, height, exifBuffer, comments?}` — no `itemSize`, no `bitDepth`, always 3-channel |
|
|
91
|
+
| AVIF | [avif_to_sampler2d.js:26](../../../../avif/native/api/avif_to_sampler2d.js#L26) — bypasses the loader entirely, called direct from `deserialize_scene` | `{data, width, height}` — no `itemSize`, no `bitDepth`, always RGBA |
|
|
92
|
+
|
|
93
|
+
`ImageDecoderWorker.decode` has a `switch (type)` with exactly one arm and a `default: throw new
|
|
94
|
+
Error('Unsupported type')`. `ThreadedImageDecoder.test` sniffs `PNG_HEADER_BYTES` and returns `false`
|
|
95
|
+
for everything else, so a JPEG or AVIF asset silently falls through to `NativeImageDecoder` — the
|
|
96
|
+
browser canvas path — which is why the native JPEG decoder has never needed to work.
|
|
97
|
+
|
|
98
|
+
**Proposal.**
|
|
99
|
+
|
|
100
|
+
1. Define one result struct, `DecodedImage {data, width, height, itemSize, bitDepth, ...}`, next to
|
|
101
|
+
`Codec.js`. Fill it in from all three decoders. `exifBuffer` and `comments` become optional
|
|
102
|
+
fields rather than a shape difference.
|
|
103
|
+
2. Add `PngCodec`, `JpegCodec`, `AvifCodec`, each a `Codec` subclass whose `test(data)` is a magic
|
|
104
|
+
sniff and whose `decode(data)` returns a `DecodedImage`. The sniffs already exist in three places
|
|
105
|
+
in three styles — `PNG_HEADER_BYTES` as an exported array, `0xFFD8` inline at
|
|
106
|
+
[JpegImage.js:139](jpeg/JpegImage.js#L139), and `ftyp`/`meta` presence at
|
|
107
|
+
[parse_avif_file.js:51](../../../../avif/native/heif/parse_avif_file.js#L51). Give each format a
|
|
108
|
+
`*_HEADER_BYTES` (or a `test` predicate) beside its decoder and have the codec use it.
|
|
109
|
+
3. `CodecWithFallback` ([CodecWithFallback.js:11](codec/CodecWithFallback.js#L11)) already does
|
|
110
|
+
ordered try-with-sniff correctly. Compose `[Threaded(PNG), Jpeg, Avif, Native]` and the format
|
|
111
|
+
switch disappears from `ImageRGBADataLoader` and from `ImageDecoderWorker`.
|
|
112
|
+
4. `avif_to_sampler2d` stays — it is the HDR-aware adapter and the exact inverse of
|
|
113
|
+
`sampler2d_to_avif` (D32) — but it becomes a thin wrapper over `AvifCodec` rather than a second
|
|
114
|
+
independent path into the decoder.
|
|
115
|
+
|
|
116
|
+
**Payoff.** JPEG becomes reachable. AVIF becomes loadable as an asset. The worker learns a second
|
|
117
|
+
format for free. One place decides what "a decoded image" is.
|
|
118
|
+
|
|
119
|
+
**Risk.** Low. `CodecWithFallback` and `Codec` already exist and are spec-covered; this is
|
|
120
|
+
population, not new machinery.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### C2 — Channel expansion to RGBA is written four times
|
|
125
|
+
|
|
126
|
+
Same job — take interleaved samples with `itemSize` channels and produce RGBA — in four places:
|
|
127
|
+
|
|
128
|
+
- [PNG.js:303](png/PNG.js#L303) `getRGBA8Array` + `getRGBA8Array_fromRGB` +
|
|
129
|
+
`getRGBA8Array_generic` + `getPixel` — **all four dead**, see D1 below
|
|
130
|
+
- [PNG.js:325](png/PNG.js#L325) `getUint8Data_case3`, palette expansion with the `tRNS` alpha rule
|
|
131
|
+
- [JpegImage.js:561](jpeg/JpegImage.js#L561) `copyToImageData`, one loop per component count with
|
|
132
|
+
`formatAsRGBA` deciding whether to emit the fourth channel
|
|
133
|
+
- [sampler2d_to_avif.js:68](../../../../avif/native/api/sampler2d_to_avif.js#L68) `to_rgba`, the
|
|
134
|
+
cleanest of the four and the only one that handles half-float input
|
|
135
|
+
|
|
136
|
+
And the engine already has a fifth: [sampler2d_to_uint8_RGBA.js](../../../graphics/texture/sampler/sampler2d_to_uint8_RGBA.js),
|
|
137
|
+
wrapped by `sampler2d_ensure_uint8_RGBA`.
|
|
138
|
+
|
|
139
|
+
**Proposal.** One free function in `core/` or beside `Codec.js`:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
expand_to_rgba(source, item_size, count, destination, opaque_value)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
with the three input families (`Uint8`, `Uint16` half-float, `Float32`) selected by the caller, not
|
|
146
|
+
sniffed. `to_rgba`'s structure is the right one to generalise from: it is the only one that already
|
|
147
|
+
handles `item_size < 3` by zero-filling and `item_size < 4` by writing opaque.
|
|
148
|
+
|
|
149
|
+
Palette expansion (`getUint8Data_case3`) stays PNG-specific — a palette lookup is not a channel
|
|
150
|
+
expansion — but it should call the shared function for the final write rather than open-coding the
|
|
151
|
+
`destination_address + 0..3` stores.
|
|
152
|
+
|
|
153
|
+
**Payoff.** Removes ~120 lines of dead code plus ~150 lines of live duplication, and makes the
|
|
154
|
+
"what fills alpha when the source has none" rule a single decision instead of four (today: `255`,
|
|
155
|
+
`255`, `255`, and `1` for float — all correct, but nothing says so in one place).
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### C3 — YCbCr → RGB exists three times, with three different capability levels
|
|
160
|
+
|
|
161
|
+
| Where | Matrix | Range | Depth | Parameterised? |
|
|
162
|
+
| --- | --- | --- | --- | --- |
|
|
163
|
+
| [ycbcr_row_to_rgb.js:24](../../../../avif/native/color/ycbcr_row_to_rgb.js#L24) | any `MC_*`, plus identity and YCgCo | full or limited | 8/10/12 | yes, via `ColourTransform` |
|
|
164
|
+
| [JpegImage.js:491](jpeg/JpegImage.js#L491) | BT.601 only, hard-coded | full only | 8 only | no |
|
|
165
|
+
| [YCbCr_to_rgb_uint24.js](../../../../core/color/YCbCr/YCbCr_to_rgb_uint24.js) | BT.709 only, hard-coded | full only | 8 only | no |
|
|
166
|
+
|
|
167
|
+
The JPEG constants are not "close to" BT.601 — they *are* `colour_transform_init` with
|
|
168
|
+
`MC_BT_601`, `color_range = 1`, `bit_depth = 8`. `cr_to_r = 2(1 − 0.299) = 1.402`,
|
|
169
|
+
`cb_to_b = 2(1 − 0.114) = 1.772`, `cb_to_g = −2·0.114·0.886/0.587 = −0.3441363`,
|
|
170
|
+
`cr_to_g = −2·0.299·0.701/0.587 = −0.71413636`. Digit for digit.
|
|
171
|
+
|
|
172
|
+
The same holds for YCgCo. `rgb_row_to_ycbcr`'s `CONVERSION_YCGCO` branch
|
|
173
|
+
([rgb_row_to_ycbcr.js:40](../../../../avif/native/color/rgb_row_to_ycbcr.js#L40)) computes
|
|
174
|
+
`0.25r + 0.5g + 0.25b`, `−0.25r + 0.5g − 0.25b`, `0.5r − 0.5b` — which is
|
|
175
|
+
[rgb_to_ycocg.js](../../../../core/color/YCoCg/rgb_to_ycocg.js) verbatim, with Cg and Co swapped in
|
|
176
|
+
the output order. Its inverse likewise reproduces `ycocg_to_rgb`.
|
|
177
|
+
|
|
178
|
+
**Proposal.** Promote `ColourTransform` + `colour_transform_init` + the two row functions out of
|
|
179
|
+
`src/avif/native/color/` into `src/core/color/`. They have no AV1 dependency beyond the `MC_*` /
|
|
180
|
+
`CP_*` symbol constants, which should move with them (they are CICP codes from ITU-T H.273, not AV1
|
|
181
|
+
inventions — the AVIF tree just happens to be where they were first needed).
|
|
182
|
+
|
|
183
|
+
Then:
|
|
184
|
+
|
|
185
|
+
- `JpegImage.getData` builds a `ColourTransform` once per image from `adobe.transformCode` /
|
|
186
|
+
component count and calls `ycbcr_row_to_rgb` per row. The arithmetic is unchanged — JFIF is
|
|
187
|
+
full-range BT.601 and that is exactly what `MC_BT_601` with `color_range = 1` produces — so this
|
|
188
|
+
is deduplication, not a behaviour change. What it buys is the row-based shape that C4 needs, and
|
|
189
|
+
one fewer transcription of the coefficients.
|
|
190
|
+
- `YCbCr_to_rgb_uint24` / `rgb_to_YCbCr_uint24` keep their packed-uint24 signature but derive their
|
|
191
|
+
coefficients from a module-level `ColourTransform` initialised with `MC_BT_709`, so the constants
|
|
192
|
+
stop being transcribed.
|
|
193
|
+
- The `CONVERSION_YCGCO` branches delegate to `rgb_to_ycocg` / `ycocg_to_rgb`.
|
|
194
|
+
|
|
195
|
+
**Risk.** Medium. This moves live colour code. `color.spec.js` and the AVIF fixture round-trips cover
|
|
196
|
+
the AVIF side; the JPEG side is a bit-exactness question — the current code computes in `number` with
|
|
197
|
+
those literals, the shared path computes the same coefficients from `kr`/`kb`, and the two can differ
|
|
198
|
+
in the last ulp before rounding. Extend `jpeg_decode.spec.js` with a per-pixel comparison against the
|
|
199
|
+
current output (tolerance 0, then 1) before it lands. The JPEG path is currently unreachable (C1), so
|
|
200
|
+
the exposure while that is settled is nil.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### C4 — Chroma upsampling is written twice, one of them by accident
|
|
205
|
+
|
|
206
|
+
[upsample_chroma_row.js:36](../../../../avif/native/color/upsample_chroma_row.js#L36) has nearest and
|
|
207
|
+
bilinear modes, edge clamping, and honours `subsampling_x` / `subsampling_y` independently.
|
|
208
|
+
|
|
209
|
+
`JpegImage.getData` does the same thing implicitly, in every one of its four `switch` arms: the
|
|
210
|
+
expression `0 | (· * componentN.scale· · ·)` appears **27 times** in that one function (17 on the
|
|
211
|
+
column axis, 10 on the row axis). That is
|
|
212
|
+
nearest-neighbour upsampling combined with output rescaling, inlined per component per arm. It is
|
|
213
|
+
also why JPEG 4:2:0 chroma is visibly blockier than AVIF 4:2:0 chroma at the same subsampling.
|
|
214
|
+
|
|
215
|
+
**Proposal.** Have the JPEG component loop upsample each component to a full-width row, then run
|
|
216
|
+
one interleave loop instead of four near-identical ones. `getData`'s four cases collapse to: build
|
|
217
|
+
N full-width rows, then one component-count-dependent combine.
|
|
218
|
+
|
|
219
|
+
**Correction, from doing it.** The row sampler could *not* be `upsample_chroma_row`.
|
|
220
|
+
`upsample_chroma_row` expresses subsampling as a shift — `sub_x` and `sub_y` are 0 or 1 — so it can
|
|
221
|
+
say "half" and not "a third", while JPEG's `h` and `v` sampling factors run from one to four and
|
|
222
|
+
`getData` also rescales to an arbitrary requested output size. Generalising it to rational scaling
|
|
223
|
+
would have changed the AVIF decoder's per-row path for no AVIF benefit. The sampler lives in
|
|
224
|
+
`jpeg/jpeg_component_row.js` instead, with that reasoning on it, and stays nearest-neighbour: the
|
|
225
|
+
four arms were nearest-neighbour, and interpolating would change every decoded picture.
|
|
226
|
+
|
|
227
|
+
So the shared-with-AVIF half of this item is withdrawn. The deduplication half stands and was worth
|
|
228
|
+
it on its own.
|
|
229
|
+
|
|
230
|
+
**Payoff, realised.** `getData` drops from ~140 lines to ~55 across three functions, the twenty-seven
|
|
231
|
+
sampling expressions become one, and the colour-transform decision — spelt out twice with a
|
|
232
|
+
different default each time — becomes one method. Verified byte-for-byte against the previous
|
|
233
|
+
implementation over 300+ combinations of sampling ratio, output size, component count, Adobe marker
|
|
234
|
+
and option state.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### C5 — A container walker for length-prefixed records
|
|
239
|
+
|
|
240
|
+
The AVIF tree has this construct twice, correctly, in two variants:
|
|
241
|
+
|
|
242
|
+
- [for_each_box.js:24](../../../../avif/native/isobmff/for_each_box.js#L24) — reusable header
|
|
243
|
+
instance, visitor returns `false` to stop, cursor repositioned to the next record regardless of how
|
|
244
|
+
much the visitor consumed
|
|
245
|
+
- [for_each_obu.js:25](../../../../avif/native/av1/obu/for_each_obu.js#L25) — same contract, two
|
|
246
|
+
reusable readers
|
|
247
|
+
|
|
248
|
+
PNG has the same construct, open-coded and split across two methods:
|
|
249
|
+
[PNGReader.decodeChunk:151](png/PNGReader.js#L151) reads `length / type / payload / crc` and then
|
|
250
|
+
`switch`es on type in the same function, and [PNGReader.parse:654](png/PNGReader.js#L654) is the
|
|
251
|
+
`for(;;)` that drives it and detects `IEND`. JPEG has it as a 200-line `switch` inside
|
|
252
|
+
`JpegImage.parse` with `offset` mutated by nested closures.
|
|
253
|
+
|
|
254
|
+
**Proposal.** Two separable pieces, and the second matters more than the first:
|
|
255
|
+
|
|
256
|
+
1. `for_each_png_chunk(cursor, visit)` in the PNG folder, mirroring `for_each_box`'s contract
|
|
257
|
+
exactly — reusable `PngChunkHeader {type, offset, payload_offset, payload_size, crc}`, visitor
|
|
258
|
+
returns `false` to stop. `decodeChunk` becomes a visitor with a `switch` and nothing else; the
|
|
259
|
+
CRC check moves into the walker where it belongs.
|
|
260
|
+
2. **Separate parsing from dispatch in JPEG.** `JpegImage.parse` currently reads the segment,
|
|
261
|
+
interprets it, and mutates decoder state in one 250-line function with four closures
|
|
262
|
+
(`readUint16`, `readDataBlock`, `prepareComponents`) capturing `offset`. Split it into
|
|
263
|
+
`for_each_jpeg_segment(data, visit)` plus one handler per marker family. The malformed-APP1
|
|
264
|
+
recovery ([JpegImage.js:368](jpeg/JpegImage.js#L368)) and the eaten-`0xFF` recovery belong to the
|
|
265
|
+
walker, not to the `default:` arm of a dispatch switch.
|
|
266
|
+
|
|
267
|
+
I am **not** proposing one generic TLV walker across all three formats. The record headers differ
|
|
268
|
+
too much (ISOBMFF has three size encodings and a `uuid` case; PNG has a trailing CRC; JPEG has
|
|
269
|
+
stuffed bytes and standalone markers). What should be shared is the *contract* — reusable header
|
|
270
|
+
struct, visitor returns `false`, walker guarantees forward progress — so that the three read alike.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### C6 — Bounds checking exists on one side of the fence only
|
|
275
|
+
|
|
276
|
+
`ByteCursor.require(count, what)` ([ByteCursor.js:96](../../../../avif/native/isobmff/ByteCursor.js#L96))
|
|
277
|
+
validates every read and names the field in the error. `BitReader` does the same for every
|
|
278
|
+
descriptor. There is a `robustness.spec.js` that feeds the decoder truncated and hostile files.
|
|
279
|
+
|
|
280
|
+
PNG reads through two unchecked private helpers
|
|
281
|
+
([PNGReader.js:28](png/PNGReader.js#L28), [PNGReader.js:42](png/PNGReader.js#L42)):
|
|
282
|
+
|
|
283
|
+
```js
|
|
284
|
+
function readUInt32(buffer, offset) {
|
|
285
|
+
return (buffer[offset] << 24) | (buffer[offset+1] << 16) | (buffer[offset+2] << 8) | buffer[offset+3];
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Past the end this yields `NaN`-free garbage — `undefined << 24` is `0` — so a truncated `IHDR`
|
|
290
|
+
produces a plausible width and height rather than an error. JPEG reads `data[offset++]` throughout
|
|
291
|
+
with no checks at all.
|
|
292
|
+
|
|
293
|
+
Both of those helpers are also **already redundant with the `BinaryBuffer` the same constructor
|
|
294
|
+
built**: `PNGReader` creates `this.buffer` with `endianness = BigEndian`
|
|
295
|
+
([PNGReader.js:83](png/PNGReader.js#L83)) and then reads chunk payloads with the unchecked free
|
|
296
|
+
functions anyway. `buffer.readUint32()` / `readUint8()` were there the whole time.
|
|
297
|
+
|
|
298
|
+
**Proposal.** Delete `readUInt32` and `readUInt8` from `PNGReader.js`; read chunk payloads through a
|
|
299
|
+
`BinaryBuffer` positioned on the chunk (or through the `for_each_png_chunk` header from C5). Add a
|
|
300
|
+
`robustness.spec.js` to the PNG and JPEG folders modelled on the AVIF one — truncated header,
|
|
301
|
+
truncated chunk, chunk length exceeding the file, garbage after `IEND`.
|
|
302
|
+
|
|
303
|
+
**Payoff.** Removes two functions and closes the class of failure where a malformed image decodes to
|
|
304
|
+
a plausible-looking wrong picture instead of throwing.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
### C7 — `crc32` is a general utility living in the PNG folder
|
|
309
|
+
|
|
310
|
+
[crc32.js](png/crc32.js) is a complete, spec-cited, tested CRC-32/ISO-3309 implementation with an
|
|
311
|
+
`(buffer, offset, length)` signature. It sits under `png/` and is imported by exactly one file.
|
|
312
|
+
|
|
313
|
+
Meanwhile [unpack_usdz.js:66](../../../../shade/renderer/loader/usd/unpack_usdz.js#L66) reads ZIP
|
|
314
|
+
local file headers and *skips* the CRC field with a comment, because verifying it would mean writing
|
|
315
|
+
this function again.
|
|
316
|
+
|
|
317
|
+
**Proposal.** Move to `src/core/binary/hash/crc32.js`, beside `xxh3_64` and `xxh3_128`. Re-export
|
|
318
|
+
from `png/crc32.js` for one release if any external consumer matters, otherwise just move it and fix
|
|
319
|
+
the two imports.
|
|
320
|
+
|
|
321
|
+
Also worth noting while it moves: `PNGReader.crc_enabled` defaults to `false`
|
|
322
|
+
([PNGReader.js:91](png/PNGReader.js#L91)) and, when enabled, `console.warn`s rather than throwing.
|
|
323
|
+
That is a defensible choice for a game engine — but it is an undocumented one, and the AVIF tree
|
|
324
|
+
records choices like this in `DECISIONS.md`. Give the PNG folder the same treatment.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
### C8 — NUL-terminated UTF-8, encoded and decoded privately in two places
|
|
329
|
+
|
|
330
|
+
[ByteCursor.js:307](../../../../avif/native/isobmff/ByteCursor.js#L307) has a private `utf8_decode`
|
|
331
|
+
with the stated reason that `TextDecoder` is not universally available.
|
|
332
|
+
[BoxWriter.js:205](../../../../avif/native/isobmff/BoxWriter.js#L205) has the matching encoder inline
|
|
333
|
+
in `utf8_string`. `BinaryBuffer` has `readUTF8String` / `writeUTF8String` but they are
|
|
334
|
+
**length-prefixed** (meep's own serialisation format) and therefore genuinely unusable here — this
|
|
335
|
+
is a real gap, not a missed reuse.
|
|
336
|
+
|
|
337
|
+
**Proposal.** One pair of free functions in `core/binary/`:
|
|
338
|
+
|
|
339
|
+
```js
|
|
340
|
+
utf8_decode_range(bytes, start, end) // → string
|
|
341
|
+
utf8_encode_into(text, buffer) // → bytes written
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
`ByteCursor.utf8_string` and `BoxWriter.utf8_string` become three-line wrappers that add the
|
|
345
|
+
NUL handling. Optionally give `BinaryBuffer` `readUTF8StringNullTerminated` /
|
|
346
|
+
`writeUTF8StringNullTerminated` built on the same pair, which would let a future ISOBMFF-adjacent
|
|
347
|
+
reader use it directly.
|
|
348
|
+
|
|
349
|
+
This does **not** conflict with D10: it shares the character codec, not the cursor.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 2. Inside `src/avif/native/` — finishing D30
|
|
354
|
+
|
|
355
|
+
D30 says derived state is shared and traversal is mirrored. That is the right rule. These are the
|
|
356
|
+
places where derived state is still written twice, and they are the residue D30 was aiming at.
|
|
357
|
+
|
|
358
|
+
### A1 — Tile geometry derivation, 18 identical lines
|
|
359
|
+
|
|
360
|
+
[parse_frame_header.js:250–267](../../../../avif/native/av1/obu/parse_frame_header.js#L250) and
|
|
361
|
+
[write_frame_header.js:162–179](../../../../avif/native/av1/obu/write_frame_header.js#L162) open with
|
|
362
|
+
the same eighteen lines: `sb_shift`, `sb_size`, `sb_cols`, `sb_rows`, `max_tile_width_sb`,
|
|
363
|
+
`max_tile_area_sb`, `min_log2_tile_cols`, `max_log2_tile_cols`, `max_log2_tile_rows`,
|
|
364
|
+
`min_log2_tiles`. Character for character, `let` versus `const` on `max_tile_area_sb` aside.
|
|
365
|
+
|
|
366
|
+
**Proposal.** `derive_tile_limits(sequence, header, out)` in a shared module (`av1/obu/tile_limits.js`
|
|
367
|
+
or extending the existing `av1/util/`), filling a small struct or an `Int32Array`. Both call it. This
|
|
368
|
+
is the highest-consequence one in this section: these ten values decide how many `f(1)` bits the
|
|
369
|
+
uniform-spacing loop reads and writes, so a divergence here is a bitstream desync, not a wrong pixel.
|
|
370
|
+
|
|
371
|
+
### A2 — CDF adaptation, 24 identical lines in reader and writer
|
|
372
|
+
|
|
373
|
+
[SymbolReader.js:178–202](../../../../avif/native/av1/entropy/SymbolReader.js#L178) and
|
|
374
|
+
[SymbolWriter.js:216–240](../../../../avif/native/av1/entropy/SymbolWriter.js#L216) are the same
|
|
375
|
+
block verbatim: the `rate` computation, the `tmp` sweep, the count saturation at 32.
|
|
376
|
+
|
|
377
|
+
**Proposal.** `cdf_adapt(cdf, offset, n, symbol)` beside `symbol_cost.js`. Both call it. The
|
|
378
|
+
adaptation rate is normative and shared by construction; there is no reason for two copies.
|
|
379
|
+
|
|
380
|
+
While there: the interval boundary
|
|
381
|
+
`((range_high * (f >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT)) + EC_MIN_PROB * (n - symbol - 1)`
|
|
382
|
+
appears once in the reader and twice in the writer
|
|
383
|
+
([SymbolWriter.js:200](../../../../avif/native/av1/entropy/SymbolWriter.js#L200) and
|
|
384
|
+
[:206](../../../../avif/native/av1/entropy/SymbolWriter.js#L206)). A `symbol_boundary(range_high,
|
|
385
|
+
cdf, offset, n, symbol)` helper covers all three; it is on the per-symbol path, so measure before
|
|
386
|
+
committing, but it is a pure function of five numbers and should inline.
|
|
387
|
+
|
|
388
|
+
### A3 — `clear_above_context` / `clear_left_context`, byte-identical
|
|
389
|
+
|
|
390
|
+
[decode_tile.js:115](../../../../avif/native/av1/decode/decode_tile.js#L115) and
|
|
391
|
+
[:125](../../../../avif/native/av1/decode/decode_tile.js#L125); the same two functions verbatim at
|
|
392
|
+
[encode_tile.js:277](../../../../avif/native/av1/encode/encode_tile.js#L277) and
|
|
393
|
+
[:287](../../../../avif/native/av1/encode/encode_tile.js#L287).
|
|
394
|
+
|
|
395
|
+
**Proposal.** Move both onto `Av1FrameContext` as methods, or export them from
|
|
396
|
+
`av1/entropy/coefficient_context.js` — which is already the shared home for exactly this kind of
|
|
397
|
+
context state. Twelve lines, zero risk.
|
|
398
|
+
|
|
399
|
+
### A4 — `dc_delta` / `ac_delta`, byte-identical
|
|
400
|
+
|
|
401
|
+
[reconstruct.js:222](../../../../avif/native/av1/decode/reconstruct.js#L222) and
|
|
402
|
+
[:235](../../../../avif/native/av1/decode/reconstruct.js#L235); identical copies at
|
|
403
|
+
[quantise.js:193](../../../../avif/native/av1/encode/quantise.js#L193) and
|
|
404
|
+
[:206](../../../../avif/native/av1/encode/quantise.js#L206). `reconstruct.js` already exports
|
|
405
|
+
`dc_quant`, `ac_quant` and `get_qindex` and `quantise.js` already imports from it — these two just
|
|
406
|
+
never made the trip.
|
|
407
|
+
|
|
408
|
+
**Proposal.** Export them from `reconstruct.js` alongside their siblings. Delete the copies.
|
|
409
|
+
|
|
410
|
+
### A5 — `BINARY_PARTITION_CDF` construction
|
|
411
|
+
|
|
412
|
+
[decode_tile.js:273](../../../../avif/native/av1/decode/decode_tile.js#L273) and
|
|
413
|
+
[encode_tile.js:838](../../../../avif/native/av1/encode/encode_tile.js#L838) each declare a
|
|
414
|
+
three-element scratch and each fill it with `(1<<15) - psum, 1<<15, 0`, each save and restore
|
|
415
|
+
`disable_cdf_update` around the call. The decoder explains in a comment why the adaptation is
|
|
416
|
+
skipped (the spec rebuilds this CDF from scratch every time, so any update is discarded); the
|
|
417
|
+
encoder does the same thing with no explanation, which is the half of the pair that will get
|
|
418
|
+
"simplified" by someone who does not know.
|
|
419
|
+
|
|
420
|
+
**Proposal.** `build_binary_partition_cdf(psum, out)` in
|
|
421
|
+
[partition_cdf.js](../../../../avif/native/av1/entropy/partition_cdf.js), which is already the shared
|
|
422
|
+
module for partition CDF selection. The scratch array stays per-module (correctly — it prevents
|
|
423
|
+
reentrancy coupling), only the fill and the save/restore discipline move.
|
|
424
|
+
|
|
425
|
+
### A6 — `plane_residual_size`, one expression in seven places
|
|
426
|
+
|
|
427
|
+
`SUBSAMPLED_SIZE[(size * 2 + sub_x) * 2 + sub_y]` appears at:
|
|
428
|
+
|
|
429
|
+
- [decode_tile.js:572](../../../../avif/native/av1/decode/decode_tile.js#L572) (as a named function)
|
|
430
|
+
- [encode_tile.js:525](../../../../avif/native/av1/encode/encode_tile.js#L525),
|
|
431
|
+
[:1728](../../../../avif/native/av1/encode/encode_tile.js#L1728),
|
|
432
|
+
[:2088](../../../../avif/native/av1/encode/encode_tile.js#L2088),
|
|
433
|
+
[:2179](../../../../avif/native/av1/encode/encode_tile.js#L2179) (four times, inline)
|
|
434
|
+
- [coefficient_context.js:50](../../../../avif/native/av1/entropy/coefficient_context.js#L50)
|
|
435
|
+
- [loop_filter.js:110](../../../../avif/native/av1/filter/loop_filter.js#L110)
|
|
436
|
+
|
|
437
|
+
**Proposal.** Export `plane_residual_size(mi_size, sub_x, sub_y)` from
|
|
438
|
+
[derived_block_tables.js](../../../../avif/native/av1/tables/derived_block_tables.js) — which already
|
|
439
|
+
exists for precisely this purpose ("Deriving beats transcribing: the two can never drift apart") —
|
|
440
|
+
and use it in all seven sites.
|
|
441
|
+
|
|
442
|
+
**And flag while you are there:** the decoder's `get_tx_size`
|
|
443
|
+
([decode_tile.js:963](../../../../avif/native/av1/decode/decode_tile.js#L963)) clamps a chroma
|
|
444
|
+
transform of 64 down to 32/16x32/32x16; the encoder's `transform_size`
|
|
445
|
+
([encode_tile.js:2167](../../../../avif/native/av1/encode/encode_tile.js#L2167)) does not, and adds a
|
|
446
|
+
`lossless → TX_4X4` case the decoder handles elsewhere. They may both be correct given the encoder's
|
|
447
|
+
narrow syntax (D30: blocks 8x8 to 32x32, so 64 is unreachable) — but that is an invariant holding by
|
|
448
|
+
accident across two files, which is exactly the shape of bug D30 exists to prevent. Either share the
|
|
449
|
+
function with the encoder's extra cases guarded, or leave a comment on both stating the invariant.
|
|
450
|
+
|
|
451
|
+
### A7 — Losslessness derived twice, and the two do not agree
|
|
452
|
+
|
|
453
|
+
[parse_frame_header.js:527](../../../../avif/native/av1/obu/parse_frame_header.js#L527)
|
|
454
|
+
`compute_losslessness` walks all `MAX_SEGMENTS`, checks all five quantiser deltas, fills
|
|
455
|
+
`lossless_array` and `seg_qm_level`, and derives `all_lossless` from `frame_width ===
|
|
456
|
+
upscaled_width`.
|
|
457
|
+
|
|
458
|
+
[build_headers.js:236](../../../../avif/native/av1/encode/build_headers.js#L236) does:
|
|
459
|
+
|
|
460
|
+
```js
|
|
461
|
+
const lossless = frame.base_q_idx === 0;
|
|
462
|
+
frame.coded_lossless = lossless ? 1 : 0;
|
|
463
|
+
frame.all_lossless = frame.coded_lossless;
|
|
464
|
+
frame.lossless_array.fill(lossless ? 1 : 0);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Correct *today*, because the encoder emits no segmentation and no non-zero deltas (D30). It stops
|
|
468
|
+
being correct the moment either is added — and `seg_qm_level` is never populated on the encode side
|
|
469
|
+
at all, which matters if `using_qmatrix` is ever turned on.
|
|
470
|
+
|
|
471
|
+
**Proposal.** Export `compute_losslessness` and have `build_headers` call it after setting
|
|
472
|
+
`base_q_idx` and the deltas. The encoder's assumptions then become inputs to the shared derivation
|
|
473
|
+
rather than a parallel implementation of it.
|
|
474
|
+
|
|
475
|
+
### A8 — Shared constants declared twice
|
|
476
|
+
|
|
477
|
+
| Constant | Sites |
|
|
478
|
+
| --- | --- |
|
|
479
|
+
| `ALPHA_AUX_TYPE` | [decode_avif.js:15](../../../../avif/native/api/decode_avif.js#L15), [encode_avif.js:41](../../../../avif/native/api/encode_avif.js#L41) |
|
|
480
|
+
| `QM_TOTAL_SIZE` | [av1_symbols.js:261](../../../../avif/native/av1/tables/av1_symbols.js#L261) (exported) and [reconstruct.js:33](../../../../avif/native/av1/decode/reconstruct.js#L33) (local copy of the same `3344`) |
|
|
481
|
+
| `HLG_A/B/C` | [transfer_to_linear.js:50](../../../../avif/native/color/transfer_to_linear.js#L50), [linear_to_transfer.js:18](../../../../avif/native/color/linear_to_transfer.js#L18) |
|
|
482
|
+
|
|
483
|
+
**Proposal.** `ALPHA_AUX_TYPE` → a shared `heif/aux_types.js` (there will be others: depth, gain
|
|
484
|
+
map). `QM_TOTAL_SIZE` → delete the local copy, import the exported one. `HLG_A/B/C` → a small
|
|
485
|
+
`color/hlg_constants.js`, or better, a `core/color/HLG/` module beside the existing `core/color/PQ/`
|
|
486
|
+
— the two curves are peers and only one of them has a home today.
|
|
487
|
+
|
|
488
|
+
### A9 — `MAX_TX_DEPTH` is two different exported symbols
|
|
489
|
+
|
|
490
|
+
[av1_symbols.js:176](../../../../avif/native/av1/tables/av1_symbols.js#L176) exports
|
|
491
|
+
`MAX_TX_DEPTH = 2` (a scalar bound).
|
|
492
|
+
[block_tables.js:12](../../../../avif/native/av1/tables/block_tables.js#L12) exports
|
|
493
|
+
`MAX_TX_DEPTH = new Uint8Array([...])` (a 22-entry table).
|
|
494
|
+
|
|
495
|
+
`decode_tile.js` imports the table under the alias `MAX_TX_DEPTH_TABLE`; `encode_tile.js` imports it
|
|
496
|
+
unaliased. Nothing currently imports both into one module, so nothing is broken — but the next file
|
|
497
|
+
that needs both gets a shadowing bug that will look like a table lookup returning a number.
|
|
498
|
+
|
|
499
|
+
**Proposal.** Rename the generated table to `MAX_TX_DEPTH_BY_BLOCK` in the generator's naming rule
|
|
500
|
+
(D22 — the generator is out of tree, so this is a rename in `block_tables.js` plus a note in
|
|
501
|
+
`DECISIONS.md` recording the deviation from the spec's name), or rename the scalar to
|
|
502
|
+
`MAX_TX_DEPTH_LIMIT`. Either way, not both.
|
|
503
|
+
|
|
504
|
+
### A10 — `clamp_unit`, `clamp_index`, and friends against `core/math`
|
|
505
|
+
|
|
506
|
+
- `clamp_unit` is duplicated at
|
|
507
|
+
[convert_to_rgba.js:217](../../../../avif/native/api/convert_to_rgba.js#L217) and
|
|
508
|
+
[ycbcr_row_to_rgb.js:83](../../../../avif/native/color/ycbcr_row_to_rgb.js#L83) — and is
|
|
509
|
+
byte-for-byte [core/math/clamp01.js](../../../../core/math/clamp01.js), which `loop_restoration.js`
|
|
510
|
+
already knows how to import from (it uses `core/math/clamp.js`).
|
|
511
|
+
- `clamp_index` is duplicated at
|
|
512
|
+
[choose_restoration.js:622](../../../../avif/native/av1/encode/choose_restoration.js#L622) and
|
|
513
|
+
[upsample_chroma_row.js:94](../../../../avif/native/color/upsample_chroma_row.js#L94) — same body,
|
|
514
|
+
different parameter names.
|
|
515
|
+
- [floor_log2.js:15](../../../../avif/native/av1/util/floor_log2.js#L15) is `31 - Math.clz32(x)`,
|
|
516
|
+
which is [core/binary/msb_32.js](../../../../core/binary/msb_32.js) exactly. And it is
|
|
517
|
+
*re-inlined* six more times in `SymbolReader`/`SymbolWriter` rather than called. (Six, not seven:
|
|
518
|
+
`SymbolWriter.#normalize`'s `32 - Math.clz32(range)` is a bit *length*, libaom's `OD_ILOG_NZ`,
|
|
519
|
+
not a floor log. It computes the same shift as the reader's `15 - floor_log2(x)` and is
|
|
520
|
+
deliberately left alone — rewriting it would break the correspondence with the source it was
|
|
521
|
+
transliterated from.)
|
|
522
|
+
- [convert_to_rgba.js:199](../../../../avif/native/api/convert_to_rgba.js#L199) `quantise` and
|
|
523
|
+
[encode_avif.js:415](../../../../avif/native/api/encode_avif.js#L415) `clamp_sample` are the same
|
|
524
|
+
"round then clamp to `0..maximum`", differing only in whether the scale-up happens inside.
|
|
525
|
+
|
|
526
|
+
**Proposal.** Import `clamp01` for `clamp_unit`. Extract one `clamp_index(index, extent)` beside
|
|
527
|
+
`clamp01`. Make `floor_log2` an alias — `export const floor_log2 = msb_32;` with the AV1 spec
|
|
528
|
+
docstring retained, since the spec-named alias is genuinely useful — and replace the six inline
|
|
529
|
+
`31 - Math.clz32(...)` sites with calls. Unify `quantise` / `clamp_sample` into one function.
|
|
530
|
+
|
|
531
|
+
**Note on the entropy coder.** `SymbolReader` says of itself that it is the hottest code in the
|
|
532
|
+
decoder, and it bypasses `BitReader` for that reason, so replacing an inline expression with a call
|
|
533
|
+
there needs evidence rather than taste. Three runs of the AVIF suite before and after: 13.77s,
|
|
534
|
+
15.19s, 12.74s against 13.03s, 12.91s, 12.77s. No regression, so the change stands; had there been
|
|
535
|
+
one, the inline forms should have kept a comment naming them instead.
|
|
536
|
+
|
|
537
|
+
The landed shape put `clamp_sample(value, maximum)` — round then clamp — in `avif/native/color/`,
|
|
538
|
+
with `convert_to_rgba` keeping `quantise` as a one-line normalised wrapper over it, so the inner
|
|
539
|
+
loop reads as it did.
|
|
540
|
+
|
|
541
|
+
The `w`/`m` derivation for `ns(n)` is a fourth instance of the same theme: it appears at
|
|
542
|
+
[BitReader.js:216](../../../../avif/native/bitstream/BitReader.js#L216),
|
|
543
|
+
`BitWriter.ns`, [SymbolReader.js:286](../../../../avif/native/av1/entropy/SymbolReader.js#L286) and
|
|
544
|
+
[SymbolWriter.js:283](../../../../avif/native/av1/entropy/SymbolWriter.js#L283). The two symbol-coder
|
|
545
|
+
copies use the inline `clz32` form rather than calling `floor_log2`, which is how they drifted apart
|
|
546
|
+
in style in the first place.
|
|
547
|
+
|
|
548
|
+
### A11 — Name collisions across unrelated modules
|
|
549
|
+
|
|
550
|
+
Not duplication, but the same trap as A9 and worth one pass:
|
|
551
|
+
|
|
552
|
+
- `filter_edge` — [loop_filter.js](../../../../avif/native/av1/filter/loop_filter.js) (a deblocking
|
|
553
|
+
edge) and [predict_intra.js](../../../../avif/native/av1/predict/predict_intra.js) (an intra edge
|
|
554
|
+
smoothing kernel). Unrelated jobs, identical name.
|
|
555
|
+
- `restore_block` — [loop_restoration.js](../../../../avif/native/av1/filter/loop_restoration.js)
|
|
556
|
+
(Wiener/SGR restoration) and
|
|
557
|
+
[Av1BlockSnapshot.js](../../../../avif/native/av1/encode/Av1BlockSnapshot.js) (undo an encoder
|
|
558
|
+
trial). Opposite meanings of "restore".
|
|
559
|
+
- `MAX_SIDE` — 64 in `Av1BlockSnapshot.js`, a different 64 in `forward_transform_2d.js`.
|
|
560
|
+
|
|
561
|
+
**Proposal.** Rename to the job: `filter_deblock_edge` / `filter_intra_edge`,
|
|
562
|
+
`restore_lr_block` / `snapshot_restore_block`, `SNAPSHOT_MAX_SIDE` / `TRANSFORM_MAX_SIDE`. All six are
|
|
563
|
+
file-local, so each rename is contained to one file.
|
|
564
|
+
|
|
565
|
+
### A12 — Table shape exports are almost entirely unused
|
|
566
|
+
|
|
567
|
+
`block_tables.js` exports a `*_SHAPE` array next to every table (`SUBSAMPLED_SIZE_SHAPE`,
|
|
568
|
+
`MAX_TX_DEPTH_SHAPE`, …). Exactly one is consumed outside the tables folder:
|
|
569
|
+
`PARTITION_SUBSIZE_SHAPE` at
|
|
570
|
+
[encode_tile.js:142](../../../../avif/native/av1/encode/encode_tile.js#L142), to derive a stride.
|
|
571
|
+
|
|
572
|
+
**Proposal.** Either keep them (they are generated, they cost nothing at runtime, and they document
|
|
573
|
+
the flattening) — in which case say so in `DECISIONS.md` so nobody prunes them — or have the
|
|
574
|
+
generator emit `*_STRIDE` scalars for the multi-dimensional tables only, which is the thing callers
|
|
575
|
+
actually want. I lean towards keeping and documenting: D24 validates the generated tables
|
|
576
|
+
structurally, and the shapes are what that validation reads.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## 3. Inside the PNG loader
|
|
581
|
+
|
|
582
|
+
### P1 — `PNGReader` is prototype-style in an ES-class codebase
|
|
583
|
+
|
|
584
|
+
[PNGReader.js:51](png/PNGReader.js#L51) is a `function` constructor with fourteen
|
|
585
|
+
`PNGReader.prototype.x = function` assignments. Everything around it — `PNG`, `Codec`,
|
|
586
|
+
`ColourTransform`, `ByteCursor`, `JpegImage` — is an ES class. The file also opens with
|
|
587
|
+
`"use strict"` and a `/*global Uint8Array:true ArrayBuffer:true */` comment, both vestigial.
|
|
588
|
+
|
|
589
|
+
**Proposal.** Straight mechanical conversion to `class PNGReader`. No behaviour change, no signature
|
|
590
|
+
change. Worth doing as its own commit before any of the other PNG items so their diffs stay readable.
|
|
591
|
+
|
|
592
|
+
### P2 — Two inflate paths in one package
|
|
593
|
+
|
|
594
|
+
[PNGReader.decodePixels:356](png/PNGReader.js#L356) constructs a `zlib.Inflate`, pushes chunks,
|
|
595
|
+
and on error falls back to `inflateRaw` on the concatenated bytes with the 2-byte header stripped —
|
|
596
|
+
about 30 lines with a well-argued comment about real-world encoders and libpng leniency.
|
|
597
|
+
|
|
598
|
+
[inflate.js:8](png/inflate.js#L8) is a separate, simpler wrapper used only by the two text-chunk
|
|
599
|
+
decoders, with no leniency and a different error shape (`throw new Error(inflator.err)` — a numeric
|
|
600
|
+
code, not a message).
|
|
601
|
+
|
|
602
|
+
**Proposal.** One `png_inflate(chunks, {lenient})` in `inflate.js` covering both. The multi-chunk
|
|
603
|
+
concatenation is the general case; single-chunk is `[chunk]`. The lenient fallback stays opt-in and
|
|
604
|
+
keeps its comment, which is the most valuable thing in that function.
|
|
605
|
+
|
|
606
|
+
### P3 — Unfilter functions have five different signatures
|
|
607
|
+
|
|
608
|
+
| Filter | Signature |
|
|
609
|
+
| --- | --- |
|
|
610
|
+
| `None` | `(data, addr, out, out_offset, length, bit_depth)` |
|
|
611
|
+
| `Sub` | `(data, addr, out, bpp, out_offset, length)` |
|
|
612
|
+
| `Up` | `(data, addr, out, out_offset, prev_offset, length)` |
|
|
613
|
+
| `Average` | `(data, addr, out, bpp, out_offset, prev_offset, length)` |
|
|
614
|
+
| `Paeth` | `(data, addr, out, bpp, out_offset, prev_offset, length)` |
|
|
615
|
+
|
|
616
|
+
Which forces [PNGReader.unFilter:576](png/PNGReader.js#L576) to be a 60-line `switch` that
|
|
617
|
+
reassembles a different argument list per case.
|
|
618
|
+
|
|
619
|
+
**Proposal.** One uniform signature — `(data, scanline_address, output, bytes_per_pixel,
|
|
620
|
+
output_offset, output_offset_previous, length, bit_depth)` — with each filter ignoring what it does
|
|
621
|
+
not need, and a frozen dispatch table `PNG_UNFILTER = [none, sub, up, average, paeth]`. `unFilter`
|
|
622
|
+
becomes a bounds check plus `PNG_UNFILTER[filter_type](...)`.
|
|
623
|
+
|
|
624
|
+
**The signature mess is hiding a bug — check this before refactoring.** `png_filter_unFilterNone` is
|
|
625
|
+
doing two jobs: unfiltering *and* unpacking sub-byte samples (bit depths 1/2/4). No other filter
|
|
626
|
+
unpacks. Two consequences follow from reading `interlaceNone`
|
|
627
|
+
([PNGReader.js:420](png/PNGReader.js#L420)) against it:
|
|
628
|
+
|
|
629
|
+
- It passes `length = color_bytes_per_row`, a **byte** count, and `unFilterNone` treats that as a
|
|
630
|
+
**sample** count when `bit_depth < 8`. A 16-pixel-wide 1-bit greyscale row has
|
|
631
|
+
`color_bytes_per_row = 2`, so two samples get unpacked instead of sixteen — and `pixels` is
|
|
632
|
+
allocated at `color_bytes_per_row * height`, so there is nowhere to put the other fourteen anyway.
|
|
633
|
+
- Filters 1–4 write packed bytes into an output that `PNG.getUint8Data` then reads as unpacked
|
|
634
|
+
samples, so any sub-byte-depth image using them is wrong on a second axis.
|
|
635
|
+
|
|
636
|
+
`PNGReader.spec.js` tests bit depths 8 and 16 only — **no test anywhere uses depth 1, 2 or 4**, which
|
|
637
|
+
is why this has survived. Write that test first; the refactor must not preserve the behaviour.
|
|
638
|
+
Separating "unfilter" (byte-wise, all five filters) from "unpack samples" (a second pass, depth-aware)
|
|
639
|
+
fixes both axes at once and is the right shape regardless of whether you agree with the diagnosis.
|
|
640
|
+
|
|
641
|
+
**Confirmed, and there was a third axis.** The tests were written first and failed exactly as
|
|
642
|
+
predicted: a 16-wide 1-bit row produced two samples, and a Sub-filtered 4-bit row came back as raw
|
|
643
|
+
packed bytes. What the diagnosis missed is `interlaceAdam7`, which was worse — its bytes-per-pixel
|
|
644
|
+
was `colors * bitDepth / 8`, a *fraction* below eight bits, and every length and buffer size derived
|
|
645
|
+
from it. Seven new decoder tests cover it now, two of them rebuilding an 8x8 image from all seven
|
|
646
|
+
Adam7 passes at 1 and 4 bits.
|
|
647
|
+
|
|
648
|
+
### P4 — `PNGReader` carries two cursors and uses one
|
|
649
|
+
|
|
650
|
+
The constructor sets `this.i = 0` ([PNGReader.js:56](png/PNGReader.js#L56)) *and* builds a
|
|
651
|
+
`BinaryBuffer` with its own `position`. `this.i` is written once, read once (in `decodeHeader`'s
|
|
652
|
+
`if (this.i !== 0) throw`) and never advanced — a dead cursor guarding a condition that can never be
|
|
653
|
+
false.
|
|
654
|
+
|
|
655
|
+
**Proposal.** Delete `this.i` and the guard. `this.header` (an 8-byte `Uint8Array` field) can be a
|
|
656
|
+
local in `decodeHeader`.
|
|
657
|
+
|
|
658
|
+
### P5 — `crc_enabled` defaults off with a `console.warn` on failure
|
|
659
|
+
|
|
660
|
+
Covered in C7. Restating here because it is a PNG-side decision that deserves a written rationale in
|
|
661
|
+
the same place the other PNG decisions will live.
|
|
662
|
+
|
|
663
|
+
### P6 — Text chunks are decoded and thrown away
|
|
664
|
+
|
|
665
|
+
`decodeChunk` calls `png_chunk_decode_zTXt(chunk)` and `png_chunk_decode_iTXt(chunk)`
|
|
666
|
+
([PNGReader.js:212](png/PNGReader.js#L212)) and **discards both return values**. `png.text` is only
|
|
667
|
+
ever written by `decodetEXt` / `decodeiEXt`, which the dispatch switch never reaches (D2 below).
|
|
668
|
+
|
|
669
|
+
So the PNG decoder pays for inflating every compressed text chunk and keeps none of it.
|
|
670
|
+
|
|
671
|
+
**Proposal.** Either wire the results into `png.text` (three lines, and it makes the `text` field
|
|
672
|
+
mean something), or stop calling them from `decodeChunk` and keep the two decoders as a library the
|
|
673
|
+
metadata path can use when there is one. Pick one; the current state is the cost of the first with
|
|
674
|
+
the benefit of neither.
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## 4. Inside the JPEG loader
|
|
679
|
+
|
|
680
|
+
### J1 — `JpegImage.parse` is one function doing six jobs
|
|
681
|
+
|
|
682
|
+
250 lines, four nested closures capturing a mutable `offset`, a `switch` with sixteen cases, two
|
|
683
|
+
error-recovery paths, and the component-preparation pass all in one scope. There is also a
|
|
684
|
+
duplicated `case 0xFFEC:` (APP12 appears twice, [JpegImage.js:161](jpeg/JpegImage.js#L161) and
|
|
685
|
+
[:162](jpeg/JpegImage.js#L162)) — harmless, but a symptom.
|
|
686
|
+
|
|
687
|
+
**Proposal.** Covered structurally by C5.2. Concretely: `for_each_jpeg_segment` walker, then
|
|
688
|
+
`parse_app_segment`, `parse_dqt`, `parse_sof`, `parse_dht`, `parse_sos`, `prepare_components` as
|
|
689
|
+
free functions taking explicit state. `prepareComponents` in particular is a pure function of a
|
|
690
|
+
`JpegFrame` and belongs in its own file next to `JpegFrame.js`.
|
|
691
|
+
|
|
692
|
+
### J2 — `for...in` over `frame.components` with `hasOwnProperty` guards
|
|
693
|
+
|
|
694
|
+
`frame.components` is a plain object keyed by numeric component id, iterated with `for (componentId
|
|
695
|
+
in frame.components)` plus `hasOwnProperty` checks
|
|
696
|
+
([JpegImage.js:74](jpeg/JpegImage.js#L74)), and later mutated with `delete cp[j].quantizationIdx`
|
|
697
|
+
([JpegImage.js:390](jpeg/JpegImage.js#L390)). `frame.componentsOrder` already carries the ids in
|
|
698
|
+
order.
|
|
699
|
+
|
|
700
|
+
**Proposal.** `components` becomes a `Map<number, JpegFrameComponent>`, or the array indexed by
|
|
701
|
+
`componentsOrder` that the code already maintains alongside it. The `delete` goes away — a
|
|
702
|
+
`quantizationIdx` that has been resolved into `quantizationTable` can simply stay, or be set to
|
|
703
|
+
`-1`. Deleting a property to mark state is a deopt and a lie about the struct's shape, and
|
|
704
|
+
`JpegFrameComponent` is a declared class with declared fields
|
|
705
|
+
([JpegFrameComponent.js](jpeg/JpegFrameComponent.js)) — the delete contradicts it.
|
|
706
|
+
|
|
707
|
+
### J3 — `getData` and `copyToImageData` both switch on component count
|
|
708
|
+
|
|
709
|
+
[JpegImage.js:421](jpeg/JpegImage.js#L421) `getData` has arms for 1, 2, 3, 4 components.
|
|
710
|
+
[JpegImage.js:561](jpeg/JpegImage.js#L561) `copyToImageData` has arms for 1, 3, 4 — no 2, so a
|
|
711
|
+
two-component PDF-style JPEG parses and then throws at the last step. The CMYK arm inverts twice
|
|
712
|
+
(`255 - C` inside `getData`, `255 - clampTo8bit(C * (1 - K/255) + K)` in `copyToImageData`), which is
|
|
713
|
+
correct but only legible if you read both functions together.
|
|
714
|
+
|
|
715
|
+
**Proposal.** After C3 and C4 land, `getData` produces normalised RGB (or RGBA) rows directly and
|
|
716
|
+
`copyToImageData` becomes the channel-expansion call from C2. The component-count switch survives in
|
|
717
|
+
exactly one place: choosing the colour transform.
|
|
718
|
+
|
|
719
|
+
### J4 — `clampTo8bit` against `core/math`
|
|
720
|
+
|
|
721
|
+
[JpegImage.js:39](jpeg/JpegImage.js#L39) is `clamp(a, 0, 255)` with a nested ternary.
|
|
722
|
+
`core/math/clamp.js` exists and `core/binary/float_to_uint8.js` exists. Use one.
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
726
|
+
## 5. Dead code
|
|
727
|
+
|
|
728
|
+
Removing these first makes every other item in this document a smaller diff.
|
|
729
|
+
|
|
730
|
+
| # | What | Lines | Evidence |
|
|
731
|
+
| --- | --- | --- | --- |
|
|
732
|
+
| D1 | [PNG.js:188](png/PNG.js#L188) `getPixel`, [:257](png/PNG.js#L257) `getRGBA8Array_fromRGB`, [:282](png/PNG.js#L282) `getRGBA8Array_generic`, [:303](png/PNG.js#L303) `getRGBA8Array` | ~120 | No importer, no spec. Only `getUint8Data` is called. |
|
|
733
|
+
| D2 | [PNGReader.js:260](png/PNGReader.js#L260) `decodetEXt`, [:273](png/PNGReader.js#L273) `decodeiEXt` | ~35 | `decodeChunk`'s switch has no `tEXt` or `iTXt`→`decodeiEXt` arm. Also **both are wrong if revived**: `decodetEXt` reads the value with `readASCIICharacters(keyword.length - 1)` (the *keyword's* length), and `decodeiEXt` calls `readUTF8String()`, which reads a 4-byte length prefix PNG does not have. |
|
|
734
|
+
| D3 | [prototypePNG.js](png/prototypePNG.js) | 37 | A hard-coded-path browser demo with top-level side effects, sitting in `src/`. No importer. |
|
|
735
|
+
| D4 | [idct8x8_fixed.js](jpeg/idct8x8_fixed.js) | 213 | No importer. `buildComponentData` uses `idct8x8_float`. If it is kept as a documented alternative, say so in a header comment and give it a spec — otherwise the MBOIT-chunk precedent (guarded + spec'd + a note saying why) is the bar. |
|
|
736
|
+
| D5 | [PNGReader.js:56](png/PNGReader.js#L56) `this.i` | 3 | P4. |
|
|
737
|
+
| D6 | [JpegImage.js:162](jpeg/JpegImage.js#L162) duplicate `case 0xFFEC` | 1 | J1. |
|
|
738
|
+
|
|
739
|
+
Total: roughly 410 lines, none of it reachable.
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
## 6. What *not* to unify
|
|
744
|
+
|
|
745
|
+
Recording these so the document cannot be read as arguing for them.
|
|
746
|
+
|
|
747
|
+
- **`ByteCursor` into `BinaryBuffer`.** D10 settled this: growable stream for writing, zero-copy
|
|
748
|
+
`DataView` cursor for reading. The reasoning still holds, and `ByteCursor.sub_cursor` has no sane
|
|
749
|
+
`BinaryBuffer` equivalent. C8 shares the UTF-8 codec, not the cursor. What *is* worth doing is
|
|
750
|
+
making the naming rhyme — `ByteCursor.u32()` versus `BoxWriter.u32()` versus
|
|
751
|
+
`BinaryBuffer.readUint32BE()` are three spellings of one operation, and `BoxWriter` already chose
|
|
752
|
+
the cursor's spelling for its own methods, so the cursor pair is self-consistent and only
|
|
753
|
+
`BinaryBuffer` reads differently. Leave it.
|
|
754
|
+
- **`encode_tile` into `decode_tile`.** D30 settled this. The traversal is mirrored deliberately;
|
|
755
|
+
inverting the decoder's control flow to serve both would be worse. Every item in section 2 shares
|
|
756
|
+
*derived state*, which is precisely what D30 asks for, and none of them touches traversal.
|
|
757
|
+
- **`BitReader` / `BitWriter` into one class.** They are a clean symmetric pair with no shared
|
|
758
|
+
implementation to extract — A10's `ns` width derivation is the only overlap and that is four
|
|
759
|
+
lines.
|
|
760
|
+
- **A single generic TLV walker for boxes, chunks and markers.** See C5. Share the contract, not the
|
|
761
|
+
implementation.
|
|
762
|
+
- **The generated tables.** D4 and D22 settled provenance; A9 and A12 are naming and documentation
|
|
763
|
+
items, not proposals to hand-edit generated output.
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
## 7. Sequencing
|
|
768
|
+
|
|
769
|
+
Ordered by value per unit of risk. Each row is independently landable.
|
|
770
|
+
|
|
771
|
+
| Order | Items | Effort | Risk | Why here |
|
|
772
|
+
| --- | --- | --- | --- | --- |
|
|
773
|
+
| 1 | **D1–D6** (dead code) | ~1h | none | Shrinks every later diff. |
|
|
774
|
+
| 2 | **A3, A4, A5, A8, A10** (verbatim AVIF duplicates) | ~2h | none | Pure deletion + import. Fixture round-trips cover it. |
|
|
775
|
+
| 3 | **A1, A2** (tile limits, CDF adapt) | ~2h | low | Highest-consequence duplicates: divergence here is a bitstream desync. Do them while the memory of section 2 is fresh. |
|
|
776
|
+
| 4 | **P1, P4, C6** (PNG class conversion, dead cursor, checked reads) | ~3h | low | Mechanical, and C6 needs P1's shape to be clean. |
|
|
777
|
+
| 5 | **C7, C8** (crc32 and UTF-8 to `core`) | ~2h | low | Unblocks USDZ CRC verification as a side effect. |
|
|
778
|
+
| 6 | **C1** (`Codec` registration) | ~1d | low | The one that changes what the engine can load. Everything above makes it a smaller change. |
|
|
779
|
+
| 7 | **A6, A7, A9** (plane_residual_size, losslessness, MAX_TX_DEPTH) | ~half day | low–med | A7 needs a decision about whether the encoder's assumptions become inputs or stay assumptions. |
|
|
780
|
+
| 8 | **P2, P3, J1, J2** (PNG inflate, filter table, JPEG parse split) | ~1d | med | P3 wants the sub-byte-depth test from section P3 written **first**. |
|
|
781
|
+
| 9 | **C3, C4, C2** (colour transform, upsampling, channel expansion to `core`) | ~2d | med | The largest win and the largest blast radius. Needs the JPEG fixtures extended first; do it last, when JPEG is reachable (C1) and therefore testable end to end. |
|
|
782
|
+
| 10 | **A11, A12, P5, P6** (naming, docs, text chunks) | ~half day | none | Housekeeping; can slot in anywhere. |
|
|
783
|
+
|
|
784
|
+
Items 1–5 are about a day and a half and are all deletions or moves. If only one thing from this
|
|
785
|
+
document gets done, do row 1 and row 3.
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## 8. One thing this review did not do
|
|
790
|
+
|
|
791
|
+
I read `src/avif/native/DECISIONS.md` before writing, and several proposals here are shaped by it —
|
|
792
|
+
sections 2 and 6 in particular. **PNG and JPEG have no equivalent.** There is a `jpeg/NOTES.md` with
|
|
793
|
+
two URLs in it and nothing else.
|
|
794
|
+
|
|
795
|
+
That asymmetry is itself a finding. The AVIF tree's `crc_enabled`-style choices are all written
|
|
796
|
+
down; the PNG tree's are not, which is why C6 (unchecked reads), P5 (CRC off by default) and P6
|
|
797
|
+
(text decoded and discarded) each read as "possible oversight" rather than "decision". Some of them
|
|
798
|
+
almost certainly *are* decisions — a game engine that warns on a bad CRC instead of refusing the
|
|
799
|
+
texture is making a defensible call — but nothing in the tree says so, so the next person to touch it
|
|
800
|
+
has to guess.
|
|
801
|
+
|
|
802
|
+
Whatever subset of this document gets acted on, a `src/engine/asset/loaders/image/DECISIONS.md`
|
|
803
|
+
recording the ones that get **rejected** is worth as much as the refactors that get accepted.
|