@woosh/meep-engine 3.29.0 → 3.30.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 +1 -1
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +14 -2
- package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/BVH.js +14 -1
- package/src/core/cache/FrequencySketch.d.ts +3 -1
- package/src/core/cache/FrequencySketch.d.ts.map +1 -1
- package/src/core/cache/FrequencySketch.js +10 -4
- package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
- package/src/core/cache/wtinylfu/CacheWTinylfu.js +24 -2
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +69 -4
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +48 -18
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +262 -70
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts +25 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.js +228 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts +13 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.js +10 -128
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.js +11 -1
- package/src/core/graph/mn_graph_collapse_weighted_edge.js +6 -6
- package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
- package/src/core/math/lookup/ParameterLookupTable.js +10 -5
- package/src/core/math/noise/sdnoise.d.ts.map +1 -1
- package/src/core/math/noise/sdnoise.js +16 -10
- package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
- package/src/core/model/object/ImmutableObjectPool.js +17 -7
- package/src/core/process/executor/ConcurrentExecutor.d.ts +1 -5
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +95 -1
- package/src/core/process/task/task_find_dependency_cycle.d.ts +17 -0
- package/src/core/process/task/task_find_dependency_cycle.d.ts.map +1 -0
- package/src/core/process/task/task_find_dependency_cycle.js +81 -0
- package/src/core/wfc/WFCCellChoice.d.ts +17 -0
- package/src/core/wfc/WFCCellChoice.d.ts.map +1 -0
- package/src/core/wfc/WFCCellChoice.js +36 -0
- package/src/core/wfc/WFCLattice.d.ts +105 -0
- package/src/core/wfc/WFCLattice.d.ts.map +1 -0
- package/src/core/wfc/WFCLattice.js +235 -0
- package/src/core/wfc/WFCRuleTable.d.ts +172 -0
- package/src/core/wfc/WFCRuleTable.d.ts.map +1 -0
- package/src/core/wfc/WFCRuleTable.js +334 -0
- package/src/core/wfc/WFCState.d.ts +273 -0
- package/src/core/wfc/WFCState.d.ts.map +1 -0
- package/src/core/wfc/WFCState.js +552 -0
- package/src/core/wfc/WFCStatus.d.ts +11 -0
- package/src/core/wfc/WFCStatus.d.ts.map +1 -0
- package/src/core/wfc/WFCStatus.js +28 -0
- package/src/core/wfc/WFCTileWeights.d.ts +48 -0
- package/src/core/wfc/WFCTileWeights.d.ts.map +1 -0
- package/src/core/wfc/WFCTileWeights.js +86 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts +12 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts.map +1 -0
- package/src/core/wfc/WFC_UNRESOLVED.js +11 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts +24 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts.map +1 -0
- package/src/core/wfc/wfc_estimate_bytes.js +47 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts +27 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts.map +1 -0
- package/src/core/wfc/wfc_rule_table_tile_components.js +80 -0
- package/src/core/wfc/wfc_solve.d.ts +21 -0
- package/src/core/wfc/wfc_solve.d.ts.map +1 -0
- package/src/core/wfc/wfc_solve.js +154 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts +18 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_apply_boundary.js +61 -0
- package/src/core/wfc/wfc_state_assign.d.ts +11 -0
- package/src/core/wfc/wfc_state_assign.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_assign.js +36 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts +17 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_ban_bits.js +81 -0
- package/src/core/wfc/wfc_state_collapse.d.ts +12 -0
- package/src/core/wfc/wfc_state_collapse.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_collapse.js +73 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts +12 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_flush_dirty.js +39 -0
- package/src/core/wfc/wfc_state_propagate.d.ts +14 -0
- package/src/core/wfc/wfc_state_propagate.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_propagate.js +155 -0
- package/src/core/wfc/wfc_state_read.d.ts +11 -0
- package/src/core/wfc/wfc_state_read.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_read.js +38 -0
- package/src/core/wfc/wfc_state_restrict.d.ts +18 -0
- package/src/core/wfc/wfc_state_restrict.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_restrict.js +39 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts +24 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_set_eligible.js +49 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts +13 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_undo_to.js +84 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts +11 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts.map +1 -0
- package/src/core/wfc/wfc_words_per_cell.js +16 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts +8 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +37 -14
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +8 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +37 -2
- package/src/engine/ecs/guid/UUID.d.ts.map +1 -1
- package/src/engine/ecs/guid/UUID.js +45 -17
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +3 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +366 -360
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +24 -10
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +24 -5
- package/src/engine/interpolation/Interpoland.d.ts +7 -1
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/Interpoland.js +8 -1
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +3 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +5 -3
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -3
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/pose_interpoland.js +2 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +15 -10
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +17 -0
- package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
- package/src/engine/physics/mls-mpm/MLS_MPM.js +14 -0
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +1 -1
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +12 -3
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +4 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.d.ts.map +1 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.js +248 -259
- package/src/format/image/avif/av1/decode/read_golomb.d.ts +21 -0
- package/src/format/image/avif/av1/decode/read_golomb.d.ts.map +1 -0
- package/src/format/image/avif/av1/decode/read_golomb.js +49 -0
- package/src/format/image/avif/av1/filter/loop_restoration.js +461 -451
- package/src/format/image/avif/av1/util/round2.d.ts +19 -2
- package/src/format/image/avif/av1/util/round2.d.ts.map +1 -1
- package/src/format/image/avif/av1/util/round2.js +51 -27
- package/src/format/image/avif/heif/parse_avif_file.js +24 -0
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +14 -0
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +46 -3
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +91 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +0 -13
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +0 -15
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +5 -5
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +11 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +11 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +18 -9
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +20 -26
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +1 -3
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.js +2 -5
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +14 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +12 -3
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.js +7 -1
- package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/GPULightMap.js +349 -336
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/load_gltf.js +1137 -1128
- package/src/shade/renderer/particles/DESIGN.md +14 -5
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +25 -10
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +182 -38
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +16 -10
- package/src/shade/renderer/particles/graph_particles.d.ts +4 -2
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +8 -2
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +8 -2
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +10 -4
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +31 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.js +62 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +13 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +95 -17
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +21 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +28 -0
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +19 -2
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +19 -2
- package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
- package/src/shade/renderer/shader/type/WebGPUStruct.js +12 -0
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts +8 -2
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +41 -6
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2383 -2356
- package/src/view/tooltip/DomTooltipManager.d.ts +7 -0
- package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipManager.js +22 -0
- package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipObserver.js +13 -3
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.js +0 -45
|
@@ -1,451 +1,461 @@
|
|
|
1
|
-
import { clamp } from "../../../../../core/math/clamp.js";
|
|
2
|
-
import {
|
|
3
|
-
FILTER_BITS,
|
|
4
|
-
MI_SIZE,
|
|
5
|
-
RESTORE_NONE,
|
|
6
|
-
RESTORE_SGRPROJ,
|
|
7
|
-
RESTORE_WIENER,
|
|
8
|
-
SGRPROJ_MTABLE_BITS,
|
|
9
|
-
SGRPROJ_PRJ_BITS,
|
|
10
|
-
SGRPROJ_RECIP_BITS,
|
|
11
|
-
SGRPROJ_RST_BITS,
|
|
12
|
-
SGRPROJ_SGR_BITS
|
|
13
|
-
} from "../tables/av1_symbols.js";
|
|
14
|
-
import { SGR_PARAMS } from "../tables/filter_tables.js";
|
|
15
|
-
import { round2 } from "../util/round2.js";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* `A` and `B` from the box filter, valid over one extra sample in each direction.
|
|
19
|
-
* @type {number}
|
|
20
|
-
*/
|
|
21
|
-
const BOX_STRIDE = MI_SIZE + 2;
|
|
22
|
-
|
|
23
|
-
/** @type {Int32Array} */
|
|
24
|
-
const BOX_A = new Int32Array(BOX_STRIDE * BOX_STRIDE);
|
|
25
|
-
|
|
26
|
-
/** @type {Int32Array} */
|
|
27
|
-
const BOX_B = new Int32Array(BOX_STRIDE * BOX_STRIDE);
|
|
28
|
-
|
|
29
|
-
/** @type {Int32Array} the two self-guided passes' outputs */
|
|
30
|
-
const FLT0 = new Int32Array(MI_SIZE * MI_SIZE);
|
|
31
|
-
|
|
32
|
-
/** @type {Int32Array} */
|
|
33
|
-
const FLT1 = new Int32Array(MI_SIZE * MI_SIZE);
|
|
34
|
-
|
|
35
|
-
/** @type {Int32Array} the Wiener filter's horizontal pass */
|
|
36
|
-
const WIENER_INTERMEDIATE = new Int32Array((MI_SIZE + 6) * MI_SIZE);
|
|
37
|
-
|
|
38
|
-
/** @type {Int32Array} the seven symmetric taps expanded from the three coded ones */
|
|
39
|
-
const WIENER_VERTICAL = new Int32Array(7);
|
|
40
|
-
|
|
41
|
-
/** @type {Int32Array} */
|
|
42
|
-
const WIENER_HORIZONTAL = new Int32Array(7);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* State the source-sample fetch needs, kept here rather than threaded through every call.
|
|
46
|
-
*
|
|
47
|
-
* The fetch decides between the deringed frame and the merely deblocked one based on which stripe
|
|
48
|
-
* the sample falls in, and that boundary changes per block. Hoisting it into a small module-level
|
|
49
|
-
* record keeps the innermost loops free of parameters they would otherwise carry six deep.
|
|
50
|
-
*/
|
|
51
|
-
const SOURCE = {
|
|
52
|
-
ctx: null,
|
|
53
|
-
plane: 0,
|
|
54
|
-
stripe_start_y: 0,
|
|
55
|
-
stripe_end_y: 0,
|
|
56
|
-
plane_end_x: 0,
|
|
57
|
-
plane_end_y: 0,
|
|
58
|
-
stride: 0,
|
|
59
|
-
cdef: null,
|
|
60
|
-
current: null
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Apply loop restoration.
|
|
65
|
-
*
|
|
66
|
-
* Reads the deringed frame and the pre-CDEF frame, and writes the restored result. The two inputs
|
|
67
|
-
* are not interchangeable: within a stripe the filter reads deringed samples, and outside it reads
|
|
68
|
-
* deblocked ones, which is what lets a hardware decoder run restoration a stripe behind CDEF
|
|
69
|
-
* without an extra line buffer.
|
|
70
|
-
*
|
|
71
|
-
* @param {Av1FrameContext} ctx
|
|
72
|
-
* @param {Uint16Array[]} current the deblocked frame, upscaled if superres is in use
|
|
73
|
-
* @param {Uint16Array[]} cdef the deringed frame, upscaled if superres is in use
|
|
74
|
-
* @param {Uint16Array[]} output
|
|
75
|
-
* @param {Int32Array} stride
|
|
76
|
-
* @see AV1 spec 7.17 "Loop restoration process"
|
|
77
|
-
*
|
|
78
|
-
* @author Alex Goldring
|
|
79
|
-
* @copyright Company Named Limited (c) 2026
|
|
80
|
-
*/
|
|
81
|
-
export function apply_loop_restoration(ctx, current, cdef, output, stride) {
|
|
82
|
-
const frame = ctx.frame;
|
|
83
|
-
|
|
84
|
-
for (let plane = 0; plane < ctx.num_planes; plane++) {
|
|
85
|
-
// the buffers are reused across frames, so the source may be longer than this frame
|
|
86
|
-
const samples = stride[plane] * ctx.plane_rows[plane];
|
|
87
|
-
|
|
88
|
-
output[plane].set(cdef[plane].subarray(0, samples));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (frame.uses_lr === 0) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
for (let y = 0; y < frame.frame_height; y += MI_SIZE) {
|
|
96
|
-
for (let x = 0; x < frame.upscaled_width; x += MI_SIZE) {
|
|
97
|
-
for (let plane = 0; plane < ctx.num_planes; plane++) {
|
|
98
|
-
if (frame.frame_restoration_type[plane] === RESTORE_NONE) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
restore_block(ctx, current, cdef, output, stride, plane, y >> 2, x >> 2);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* @param {Av1FrameContext} ctx
|
|
110
|
-
* @param {Uint16Array[]} current
|
|
111
|
-
* @param {Uint16Array[]} cdef
|
|
112
|
-
* @param {Uint16Array[]} output
|
|
113
|
-
* @param {Int32Array} stride
|
|
114
|
-
* @param {number} plane
|
|
115
|
-
* @param {number} row
|
|
116
|
-
* @param {number} col
|
|
117
|
-
* @see AV1 spec 7.17.1 "Loop restore block process"
|
|
118
|
-
*/
|
|
119
|
-
function restore_block(ctx, current, cdef, output, stride, plane, row, col) {
|
|
120
|
-
const frame = ctx.frame;
|
|
121
|
-
|
|
122
|
-
const luma_y = row * MI_SIZE;
|
|
123
|
-
|
|
124
|
-
// stripes are offset up by eight luma samples so a pipelined decoder can restore a stripe as
|
|
125
|
-
// soon as the superblock row above it is deblocked
|
|
126
|
-
const stripe = Math.floor((luma_y + 8) / 64);
|
|
127
|
-
|
|
128
|
-
const sub_x = plane > 0 ? ctx.subsampling_x : 0;
|
|
129
|
-
const sub_y = plane > 0 ? ctx.subsampling_y : 0;
|
|
130
|
-
|
|
131
|
-
SOURCE.ctx = ctx;
|
|
132
|
-
SOURCE.plane = plane;
|
|
133
|
-
SOURCE.stripe_start_y = (-8 + stripe * 64) >> sub_y;
|
|
134
|
-
SOURCE.stripe_end_y = SOURCE.stripe_start_y + (64 >> sub_y) - 1;
|
|
135
|
-
SOURCE.plane_end_x = round2(frame.upscaled_width, sub_x) - 1;
|
|
136
|
-
SOURCE.plane_end_y = round2(frame.frame_height, sub_y) - 1;
|
|
137
|
-
SOURCE.stride = stride[plane];
|
|
138
|
-
SOURCE.cdef = cdef[plane];
|
|
139
|
-
SOURCE.current = current[plane];
|
|
140
|
-
|
|
141
|
-
const x = (col * MI_SIZE) >> sub_x;
|
|
142
|
-
const y = (row * MI_SIZE) >> sub_y;
|
|
143
|
-
|
|
144
|
-
const w = Math.min(MI_SIZE >> sub_x, SOURCE.plane_end_x - x + 1);
|
|
145
|
-
const h = Math.min(MI_SIZE >> sub_y, SOURCE.plane_end_y - y + 1);
|
|
146
|
-
|
|
147
|
-
if (w <= 0 || h <= 0) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const unit_index = restoration_unit_of(ctx, plane, row, col);
|
|
152
|
-
|
|
153
|
-
const type = ctx.lr_type[unit_index];
|
|
154
|
-
|
|
155
|
-
if (type === RESTORE_WIENER) {
|
|
156
|
-
wiener_filter(ctx, output, stride, plane, unit_index, x, y, w, h);
|
|
157
|
-
} else if (type === RESTORE_SGRPROJ) {
|
|
158
|
-
self_guided_filter(ctx, output, stride, plane, unit_index, x, y, w, h);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* `count_units_in_frame`: how many restoration units of one size a frame is divided into.
|
|
164
|
-
*
|
|
165
|
-
* @param {number} unit_size
|
|
166
|
-
* @param {number} frame_size
|
|
167
|
-
* @returns {number}
|
|
168
|
-
*/
|
|
169
|
-
export function count_units_in_frame(unit_size, frame_size) {
|
|
170
|
-
return Math.max(Math.floor((frame_size + (unit_size >> 1)) / unit_size), 1);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Which restoration unit a 4x4 block is filtered by.
|
|
175
|
-
*
|
|
176
|
-
* The vertical mapping is offset by eight luma samples, because restoration works in stripes that
|
|
177
|
-
* are offset by that much. Exported because the encoder has to attribute a block's error to the
|
|
178
|
-
* same unit this attributes its filtering to, and the two agreeing is the whole point.
|
|
179
|
-
*
|
|
180
|
-
* @param {Av1FrameContext} ctx
|
|
181
|
-
* @param {number} plane
|
|
182
|
-
* @param {number} row 4x4 block row
|
|
183
|
-
* @param {number} col 4x4 block column
|
|
184
|
-
* @returns {number} index into the per-unit arrays
|
|
185
|
-
*/
|
|
186
|
-
export function restoration_unit_of(ctx, plane, row, col) {
|
|
187
|
-
const frame = ctx.frame;
|
|
188
|
-
|
|
189
|
-
const sub_x = plane > 0 ? ctx.subsampling_x : 0;
|
|
190
|
-
const sub_y = plane > 0 ? ctx.subsampling_y : 0;
|
|
191
|
-
|
|
192
|
-
const unit_size = frame.loop_restoration_size[plane];
|
|
193
|
-
|
|
194
|
-
const unit_rows = count_units_in_frame(unit_size, round2(frame.frame_height, sub_y));
|
|
195
|
-
const unit_cols = count_units_in_frame(unit_size, round2(frame.upscaled_width, sub_x));
|
|
196
|
-
|
|
197
|
-
const unit_row = Math.min(unit_rows - 1, Math.floor(((row * MI_SIZE + 8) >> sub_y) / unit_size));
|
|
198
|
-
const unit_col = Math.min(unit_cols - 1, Math.floor(((col * MI_SIZE) >> sub_x) / unit_size));
|
|
199
|
-
|
|
200
|
-
return ctx.lr_unit_index(plane, unit_row, unit_col);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* `get_source_sample`: deringed inside the current stripe, merely deblocked outside it.
|
|
205
|
-
*
|
|
206
|
-
* @param {number} x
|
|
207
|
-
* @param {number} y
|
|
208
|
-
* @returns {number}
|
|
209
|
-
* @see AV1 spec 7.17.6 "Get source sample process"
|
|
210
|
-
*/
|
|
211
|
-
function source_sample(x, y) {
|
|
212
|
-
const clamped_x = clamp(x, 0, SOURCE.plane_end_x);
|
|
213
|
-
|
|
214
|
-
let clamped_y = clamp(y, 0, SOURCE.plane_end_y);
|
|
215
|
-
|
|
216
|
-
if (clamped_y < SOURCE.stripe_start_y) {
|
|
217
|
-
clamped_y = Math.max(SOURCE.stripe_start_y - 2, clamped_y);
|
|
218
|
-
|
|
219
|
-
return SOURCE.current[clamped_y * SOURCE.stride + clamped_x];
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (clamped_y > SOURCE.stripe_end_y) {
|
|
223
|
-
clamped_y = Math.min(SOURCE.stripe_end_y + 2, clamped_y);
|
|
224
|
-
|
|
225
|
-
return SOURCE.current[clamped_y * SOURCE.stride + clamped_x];
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return SOURCE.cdef[clamped_y * SOURCE.stride + clamped_x];
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* @param {Av1FrameContext} ctx
|
|
233
|
-
* @param {Uint16Array[]} output
|
|
234
|
-
* @param {Int32Array} stride
|
|
235
|
-
* @param {number} plane
|
|
236
|
-
* @param {number} unit_index
|
|
237
|
-
* @param {number} x
|
|
238
|
-
* @param {number} y
|
|
239
|
-
* @param {number} w
|
|
240
|
-
* @param {number} h
|
|
241
|
-
* @see AV1 spec 7.17.4 "Wiener filter process"
|
|
242
|
-
*/
|
|
243
|
-
function wiener_filter(ctx, output, stride, plane, unit_index, x, y, w, h) {
|
|
244
|
-
const bit_depth = ctx.bit_depth;
|
|
245
|
-
|
|
246
|
-
// the rounding the specification derives for a non-compound inter prediction, reused here
|
|
247
|
-
const round0 = bit_depth === 12 ? 5 : 3;
|
|
248
|
-
const round1 = bit_depth === 12 ? 9 : 11;
|
|
249
|
-
|
|
250
|
-
expand_wiener(ctx.lr_wiener, unit_index * 6, WIENER_VERTICAL);
|
|
251
|
-
expand_wiener(ctx.lr_wiener, unit_index * 6 + 3, WIENER_HORIZONTAL);
|
|
252
|
-
|
|
253
|
-
const offset = 1 << (bit_depth + FILTER_BITS - round0 - 1);
|
|
254
|
-
const limit = (1 << (bit_depth + 1 + FILTER_BITS - round0)) - 1;
|
|
255
|
-
|
|
256
|
-
for (let r = 0; r < h + 6; r++) {
|
|
257
|
-
for (let c = 0; c < w; c++) {
|
|
258
|
-
let s = 0;
|
|
259
|
-
|
|
260
|
-
for (let t = 0; t < 7; t++) {
|
|
261
|
-
s += WIENER_HORIZONTAL[t] * source_sample(x + c + t - 3, y + r - 3);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
WIENER_INTERMEDIATE[r * MI_SIZE + c] = clamp(round2(s, round0), -offset, limit - offset);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const max_value = (1 << bit_depth) - 1;
|
|
269
|
-
|
|
270
|
-
const plane_output = output[plane];
|
|
271
|
-
const plane_stride = stride[plane];
|
|
272
|
-
|
|
273
|
-
for (let r = 0; r < h; r++) {
|
|
274
|
-
for (let c = 0; c < w; c++) {
|
|
275
|
-
let s = 0;
|
|
276
|
-
|
|
277
|
-
for (let t = 0; t < 7; t++) {
|
|
278
|
-
s += WIENER_VERTICAL[t] * WIENER_INTERMEDIATE[(r + t) * MI_SIZE + c];
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
plane_output[(y + r) * plane_stride + x + c] = clamp(round2(s, round1), 0, max_value);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Expand three coded taps into the seven of a symmetric unit-gain filter.
|
|
288
|
-
*
|
|
289
|
-
* @param {Int8Array} coefficients
|
|
290
|
-
* @param {number} offset
|
|
291
|
-
* @param {Int32Array} filter
|
|
292
|
-
* @see AV1 spec 7.17.5 "Wiener coefficient process"
|
|
293
|
-
*/
|
|
294
|
-
function expand_wiener(coefficients, offset, filter) {
|
|
295
|
-
filter[3] = 128;
|
|
296
|
-
|
|
297
|
-
for (let i = 0; i < 3; i++) {
|
|
298
|
-
const c = coefficients[offset + i];
|
|
299
|
-
|
|
300
|
-
filter[i] = c;
|
|
301
|
-
filter[6 - i] = c;
|
|
302
|
-
filter[3] -= 2 * c;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* @param {Av1FrameContext} ctx
|
|
308
|
-
* @param {Uint16Array[]} output
|
|
309
|
-
* @param {Int32Array} stride
|
|
310
|
-
* @param {number} plane
|
|
311
|
-
* @param {number} unit_index
|
|
312
|
-
* @param {number} x
|
|
313
|
-
* @param {number} y
|
|
314
|
-
* @param {number} w
|
|
315
|
-
* @param {number} h
|
|
316
|
-
* @see AV1 spec 7.17.2 "Self guided filter process"
|
|
317
|
-
*/
|
|
318
|
-
function self_guided_filter(ctx, output, stride, plane, unit_index, x, y, w, h) {
|
|
319
|
-
const set = ctx.lr_sgr_set[unit_index];
|
|
320
|
-
|
|
321
|
-
box_filter(ctx, x, y, w, h, set, 0, FLT0);
|
|
322
|
-
box_filter(ctx, x, y, w, h, set, 1, FLT1);
|
|
323
|
-
|
|
324
|
-
const w0 = ctx.lr_sgr_xqd[unit_index * 2];
|
|
325
|
-
const w1 = ctx.lr_sgr_xqd[unit_index * 2 + 1];
|
|
326
|
-
const w2 = (1 << SGRPROJ_PRJ_BITS) - w0 - w1;
|
|
327
|
-
|
|
328
|
-
const r0 = SGR_PARAMS[set * 4];
|
|
329
|
-
const r1 = SGR_PARAMS[set * 4 + 2];
|
|
330
|
-
|
|
331
|
-
const max_value = (1 << ctx.bit_depth) - 1;
|
|
332
|
-
|
|
333
|
-
const plane_output = output[plane];
|
|
334
|
-
const plane_stride = stride[plane];
|
|
335
|
-
|
|
336
|
-
for (let i = 0; i < h; i++) {
|
|
337
|
-
for (let j = 0; j < w; j++) {
|
|
338
|
-
const u = SOURCE.cdef[(y + i) * SOURCE.stride + x + j] << SGRPROJ_RST_BITS;
|
|
339
|
-
|
|
340
|
-
let v = w1 * u;
|
|
341
|
-
|
|
342
|
-
v += r0 !== 0 ? w0 * FLT0[i * MI_SIZE + j] : w0 * u;
|
|
343
|
-
v += r1 !== 0 ? w2 * FLT1[i * MI_SIZE + j] : w2 * u;
|
|
344
|
-
|
|
345
|
-
plane_output[(y + i) * plane_stride + x + j] = clamp(
|
|
346
|
-
round2(v, SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS),
|
|
347
|
-
0,
|
|
348
|
-
max_value
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* @param {Av1FrameContext} ctx
|
|
356
|
-
* @param {number} x
|
|
357
|
-
* @param {number} y
|
|
358
|
-
* @param {number} w
|
|
359
|
-
* @param {number} h
|
|
360
|
-
* @param {number} set
|
|
361
|
-
* @param {number} pass
|
|
362
|
-
* @param {Int32Array} out
|
|
363
|
-
* @see AV1 spec 7.17.3 "Box filter process"
|
|
364
|
-
*/
|
|
365
|
-
function box_filter(ctx, x, y, w, h, set, pass, out) {
|
|
366
|
-
const r = SGR_PARAMS[set * 4 + pass * 2];
|
|
367
|
-
|
|
368
|
-
if (r === 0) {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const eps = SGR_PARAMS[set * 4 + pass * 2 + 1];
|
|
373
|
-
|
|
374
|
-
const bit_depth = ctx.bit_depth;
|
|
375
|
-
|
|
376
|
-
const n = (2 * r + 1) * (2 * r + 1);
|
|
377
|
-
const n2e = n * n * eps;
|
|
378
|
-
const s = Math.floor(((1 << SGRPROJ_MTABLE_BITS) + Math.floor(n2e / 2)) / n2e);
|
|
379
|
-
|
|
380
|
-
const one_over_n = Math.floor(((1 << SGRPROJ_RECIP_BITS) + Math.floor(n / 2)) / n);
|
|
381
|
-
|
|
382
|
-
for (let i = -1; i < h + 1; i++) {
|
|
383
|
-
for (let j = -1; j < w + 1; j++) {
|
|
384
|
-
let a = 0;
|
|
385
|
-
let b = 0;
|
|
386
|
-
|
|
387
|
-
for (let dy = -r; dy <= r; dy++) {
|
|
388
|
-
for (let dx = -r; dx <= r; dx++) {
|
|
389
|
-
const c = source_sample(x + j + dx, y + i + dy);
|
|
390
|
-
|
|
391
|
-
a += c * c;
|
|
392
|
-
b += c;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
a = round2(a, 2 * (bit_depth - 8));
|
|
397
|
-
|
|
398
|
-
const d = round2(b, bit_depth - 8);
|
|
399
|
-
|
|
400
|
-
const p = Math.max(0, a * n - d * d);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
1
|
+
import { clamp } from "../../../../../core/math/clamp.js";
|
|
2
|
+
import {
|
|
3
|
+
FILTER_BITS,
|
|
4
|
+
MI_SIZE,
|
|
5
|
+
RESTORE_NONE,
|
|
6
|
+
RESTORE_SGRPROJ,
|
|
7
|
+
RESTORE_WIENER,
|
|
8
|
+
SGRPROJ_MTABLE_BITS,
|
|
9
|
+
SGRPROJ_PRJ_BITS,
|
|
10
|
+
SGRPROJ_RECIP_BITS,
|
|
11
|
+
SGRPROJ_RST_BITS,
|
|
12
|
+
SGRPROJ_SGR_BITS
|
|
13
|
+
} from "../tables/av1_symbols.js";
|
|
14
|
+
import { SGR_PARAMS } from "../tables/filter_tables.js";
|
|
15
|
+
import { round2, round2_wide } from "../util/round2.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `A` and `B` from the box filter, valid over one extra sample in each direction.
|
|
19
|
+
* @type {number}
|
|
20
|
+
*/
|
|
21
|
+
const BOX_STRIDE = MI_SIZE + 2;
|
|
22
|
+
|
|
23
|
+
/** @type {Int32Array} */
|
|
24
|
+
const BOX_A = new Int32Array(BOX_STRIDE * BOX_STRIDE);
|
|
25
|
+
|
|
26
|
+
/** @type {Int32Array} */
|
|
27
|
+
const BOX_B = new Int32Array(BOX_STRIDE * BOX_STRIDE);
|
|
28
|
+
|
|
29
|
+
/** @type {Int32Array} the two self-guided passes' outputs */
|
|
30
|
+
const FLT0 = new Int32Array(MI_SIZE * MI_SIZE);
|
|
31
|
+
|
|
32
|
+
/** @type {Int32Array} */
|
|
33
|
+
const FLT1 = new Int32Array(MI_SIZE * MI_SIZE);
|
|
34
|
+
|
|
35
|
+
/** @type {Int32Array} the Wiener filter's horizontal pass */
|
|
36
|
+
const WIENER_INTERMEDIATE = new Int32Array((MI_SIZE + 6) * MI_SIZE);
|
|
37
|
+
|
|
38
|
+
/** @type {Int32Array} the seven symmetric taps expanded from the three coded ones */
|
|
39
|
+
const WIENER_VERTICAL = new Int32Array(7);
|
|
40
|
+
|
|
41
|
+
/** @type {Int32Array} */
|
|
42
|
+
const WIENER_HORIZONTAL = new Int32Array(7);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* State the source-sample fetch needs, kept here rather than threaded through every call.
|
|
46
|
+
*
|
|
47
|
+
* The fetch decides between the deringed frame and the merely deblocked one based on which stripe
|
|
48
|
+
* the sample falls in, and that boundary changes per block. Hoisting it into a small module-level
|
|
49
|
+
* record keeps the innermost loops free of parameters they would otherwise carry six deep.
|
|
50
|
+
*/
|
|
51
|
+
const SOURCE = {
|
|
52
|
+
ctx: null,
|
|
53
|
+
plane: 0,
|
|
54
|
+
stripe_start_y: 0,
|
|
55
|
+
stripe_end_y: 0,
|
|
56
|
+
plane_end_x: 0,
|
|
57
|
+
plane_end_y: 0,
|
|
58
|
+
stride: 0,
|
|
59
|
+
cdef: null,
|
|
60
|
+
current: null
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Apply loop restoration.
|
|
65
|
+
*
|
|
66
|
+
* Reads the deringed frame and the pre-CDEF frame, and writes the restored result. The two inputs
|
|
67
|
+
* are not interchangeable: within a stripe the filter reads deringed samples, and outside it reads
|
|
68
|
+
* deblocked ones, which is what lets a hardware decoder run restoration a stripe behind CDEF
|
|
69
|
+
* without an extra line buffer.
|
|
70
|
+
*
|
|
71
|
+
* @param {Av1FrameContext} ctx
|
|
72
|
+
* @param {Uint16Array[]} current the deblocked frame, upscaled if superres is in use
|
|
73
|
+
* @param {Uint16Array[]} cdef the deringed frame, upscaled if superres is in use
|
|
74
|
+
* @param {Uint16Array[]} output
|
|
75
|
+
* @param {Int32Array} stride
|
|
76
|
+
* @see AV1 spec 7.17 "Loop restoration process"
|
|
77
|
+
*
|
|
78
|
+
* @author Alex Goldring
|
|
79
|
+
* @copyright Company Named Limited (c) 2026
|
|
80
|
+
*/
|
|
81
|
+
export function apply_loop_restoration(ctx, current, cdef, output, stride) {
|
|
82
|
+
const frame = ctx.frame;
|
|
83
|
+
|
|
84
|
+
for (let plane = 0; plane < ctx.num_planes; plane++) {
|
|
85
|
+
// the buffers are reused across frames, so the source may be longer than this frame
|
|
86
|
+
const samples = stride[plane] * ctx.plane_rows[plane];
|
|
87
|
+
|
|
88
|
+
output[plane].set(cdef[plane].subarray(0, samples));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (frame.uses_lr === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (let y = 0; y < frame.frame_height; y += MI_SIZE) {
|
|
96
|
+
for (let x = 0; x < frame.upscaled_width; x += MI_SIZE) {
|
|
97
|
+
for (let plane = 0; plane < ctx.num_planes; plane++) {
|
|
98
|
+
if (frame.frame_restoration_type[plane] === RESTORE_NONE) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
restore_block(ctx, current, cdef, output, stride, plane, y >> 2, x >> 2);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {Av1FrameContext} ctx
|
|
110
|
+
* @param {Uint16Array[]} current
|
|
111
|
+
* @param {Uint16Array[]} cdef
|
|
112
|
+
* @param {Uint16Array[]} output
|
|
113
|
+
* @param {Int32Array} stride
|
|
114
|
+
* @param {number} plane
|
|
115
|
+
* @param {number} row
|
|
116
|
+
* @param {number} col
|
|
117
|
+
* @see AV1 spec 7.17.1 "Loop restore block process"
|
|
118
|
+
*/
|
|
119
|
+
function restore_block(ctx, current, cdef, output, stride, plane, row, col) {
|
|
120
|
+
const frame = ctx.frame;
|
|
121
|
+
|
|
122
|
+
const luma_y = row * MI_SIZE;
|
|
123
|
+
|
|
124
|
+
// stripes are offset up by eight luma samples so a pipelined decoder can restore a stripe as
|
|
125
|
+
// soon as the superblock row above it is deblocked
|
|
126
|
+
const stripe = Math.floor((luma_y + 8) / 64);
|
|
127
|
+
|
|
128
|
+
const sub_x = plane > 0 ? ctx.subsampling_x : 0;
|
|
129
|
+
const sub_y = plane > 0 ? ctx.subsampling_y : 0;
|
|
130
|
+
|
|
131
|
+
SOURCE.ctx = ctx;
|
|
132
|
+
SOURCE.plane = plane;
|
|
133
|
+
SOURCE.stripe_start_y = (-8 + stripe * 64) >> sub_y;
|
|
134
|
+
SOURCE.stripe_end_y = SOURCE.stripe_start_y + (64 >> sub_y) - 1;
|
|
135
|
+
SOURCE.plane_end_x = round2(frame.upscaled_width, sub_x) - 1;
|
|
136
|
+
SOURCE.plane_end_y = round2(frame.frame_height, sub_y) - 1;
|
|
137
|
+
SOURCE.stride = stride[plane];
|
|
138
|
+
SOURCE.cdef = cdef[plane];
|
|
139
|
+
SOURCE.current = current[plane];
|
|
140
|
+
|
|
141
|
+
const x = (col * MI_SIZE) >> sub_x;
|
|
142
|
+
const y = (row * MI_SIZE) >> sub_y;
|
|
143
|
+
|
|
144
|
+
const w = Math.min(MI_SIZE >> sub_x, SOURCE.plane_end_x - x + 1);
|
|
145
|
+
const h = Math.min(MI_SIZE >> sub_y, SOURCE.plane_end_y - y + 1);
|
|
146
|
+
|
|
147
|
+
if (w <= 0 || h <= 0) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const unit_index = restoration_unit_of(ctx, plane, row, col);
|
|
152
|
+
|
|
153
|
+
const type = ctx.lr_type[unit_index];
|
|
154
|
+
|
|
155
|
+
if (type === RESTORE_WIENER) {
|
|
156
|
+
wiener_filter(ctx, output, stride, plane, unit_index, x, y, w, h);
|
|
157
|
+
} else if (type === RESTORE_SGRPROJ) {
|
|
158
|
+
self_guided_filter(ctx, output, stride, plane, unit_index, x, y, w, h);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* `count_units_in_frame`: how many restoration units of one size a frame is divided into.
|
|
164
|
+
*
|
|
165
|
+
* @param {number} unit_size
|
|
166
|
+
* @param {number} frame_size
|
|
167
|
+
* @returns {number}
|
|
168
|
+
*/
|
|
169
|
+
export function count_units_in_frame(unit_size, frame_size) {
|
|
170
|
+
return Math.max(Math.floor((frame_size + (unit_size >> 1)) / unit_size), 1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Which restoration unit a 4x4 block is filtered by.
|
|
175
|
+
*
|
|
176
|
+
* The vertical mapping is offset by eight luma samples, because restoration works in stripes that
|
|
177
|
+
* are offset by that much. Exported because the encoder has to attribute a block's error to the
|
|
178
|
+
* same unit this attributes its filtering to, and the two agreeing is the whole point.
|
|
179
|
+
*
|
|
180
|
+
* @param {Av1FrameContext} ctx
|
|
181
|
+
* @param {number} plane
|
|
182
|
+
* @param {number} row 4x4 block row
|
|
183
|
+
* @param {number} col 4x4 block column
|
|
184
|
+
* @returns {number} index into the per-unit arrays
|
|
185
|
+
*/
|
|
186
|
+
export function restoration_unit_of(ctx, plane, row, col) {
|
|
187
|
+
const frame = ctx.frame;
|
|
188
|
+
|
|
189
|
+
const sub_x = plane > 0 ? ctx.subsampling_x : 0;
|
|
190
|
+
const sub_y = plane > 0 ? ctx.subsampling_y : 0;
|
|
191
|
+
|
|
192
|
+
const unit_size = frame.loop_restoration_size[plane];
|
|
193
|
+
|
|
194
|
+
const unit_rows = count_units_in_frame(unit_size, round2(frame.frame_height, sub_y));
|
|
195
|
+
const unit_cols = count_units_in_frame(unit_size, round2(frame.upscaled_width, sub_x));
|
|
196
|
+
|
|
197
|
+
const unit_row = Math.min(unit_rows - 1, Math.floor(((row * MI_SIZE + 8) >> sub_y) / unit_size));
|
|
198
|
+
const unit_col = Math.min(unit_cols - 1, Math.floor(((col * MI_SIZE) >> sub_x) / unit_size));
|
|
199
|
+
|
|
200
|
+
return ctx.lr_unit_index(plane, unit_row, unit_col);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* `get_source_sample`: deringed inside the current stripe, merely deblocked outside it.
|
|
205
|
+
*
|
|
206
|
+
* @param {number} x
|
|
207
|
+
* @param {number} y
|
|
208
|
+
* @returns {number}
|
|
209
|
+
* @see AV1 spec 7.17.6 "Get source sample process"
|
|
210
|
+
*/
|
|
211
|
+
function source_sample(x, y) {
|
|
212
|
+
const clamped_x = clamp(x, 0, SOURCE.plane_end_x);
|
|
213
|
+
|
|
214
|
+
let clamped_y = clamp(y, 0, SOURCE.plane_end_y);
|
|
215
|
+
|
|
216
|
+
if (clamped_y < SOURCE.stripe_start_y) {
|
|
217
|
+
clamped_y = Math.max(SOURCE.stripe_start_y - 2, clamped_y);
|
|
218
|
+
|
|
219
|
+
return SOURCE.current[clamped_y * SOURCE.stride + clamped_x];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (clamped_y > SOURCE.stripe_end_y) {
|
|
223
|
+
clamped_y = Math.min(SOURCE.stripe_end_y + 2, clamped_y);
|
|
224
|
+
|
|
225
|
+
return SOURCE.current[clamped_y * SOURCE.stride + clamped_x];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return SOURCE.cdef[clamped_y * SOURCE.stride + clamped_x];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {Av1FrameContext} ctx
|
|
233
|
+
* @param {Uint16Array[]} output
|
|
234
|
+
* @param {Int32Array} stride
|
|
235
|
+
* @param {number} plane
|
|
236
|
+
* @param {number} unit_index
|
|
237
|
+
* @param {number} x
|
|
238
|
+
* @param {number} y
|
|
239
|
+
* @param {number} w
|
|
240
|
+
* @param {number} h
|
|
241
|
+
* @see AV1 spec 7.17.4 "Wiener filter process"
|
|
242
|
+
*/
|
|
243
|
+
function wiener_filter(ctx, output, stride, plane, unit_index, x, y, w, h) {
|
|
244
|
+
const bit_depth = ctx.bit_depth;
|
|
245
|
+
|
|
246
|
+
// the rounding the specification derives for a non-compound inter prediction, reused here
|
|
247
|
+
const round0 = bit_depth === 12 ? 5 : 3;
|
|
248
|
+
const round1 = bit_depth === 12 ? 9 : 11;
|
|
249
|
+
|
|
250
|
+
expand_wiener(ctx.lr_wiener, unit_index * 6, WIENER_VERTICAL);
|
|
251
|
+
expand_wiener(ctx.lr_wiener, unit_index * 6 + 3, WIENER_HORIZONTAL);
|
|
252
|
+
|
|
253
|
+
const offset = 1 << (bit_depth + FILTER_BITS - round0 - 1);
|
|
254
|
+
const limit = (1 << (bit_depth + 1 + FILTER_BITS - round0)) - 1;
|
|
255
|
+
|
|
256
|
+
for (let r = 0; r < h + 6; r++) {
|
|
257
|
+
for (let c = 0; c < w; c++) {
|
|
258
|
+
let s = 0;
|
|
259
|
+
|
|
260
|
+
for (let t = 0; t < 7; t++) {
|
|
261
|
+
s += WIENER_HORIZONTAL[t] * source_sample(x + c + t - 3, y + r - 3);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
WIENER_INTERMEDIATE[r * MI_SIZE + c] = clamp(round2(s, round0), -offset, limit - offset);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const max_value = (1 << bit_depth) - 1;
|
|
269
|
+
|
|
270
|
+
const plane_output = output[plane];
|
|
271
|
+
const plane_stride = stride[plane];
|
|
272
|
+
|
|
273
|
+
for (let r = 0; r < h; r++) {
|
|
274
|
+
for (let c = 0; c < w; c++) {
|
|
275
|
+
let s = 0;
|
|
276
|
+
|
|
277
|
+
for (let t = 0; t < 7; t++) {
|
|
278
|
+
s += WIENER_VERTICAL[t] * WIENER_INTERMEDIATE[(r + t) * MI_SIZE + c];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
plane_output[(y + r) * plane_stride + x + c] = clamp(round2(s, round1), 0, max_value);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Expand three coded taps into the seven of a symmetric unit-gain filter.
|
|
288
|
+
*
|
|
289
|
+
* @param {Int8Array} coefficients
|
|
290
|
+
* @param {number} offset
|
|
291
|
+
* @param {Int32Array} filter
|
|
292
|
+
* @see AV1 spec 7.17.5 "Wiener coefficient process"
|
|
293
|
+
*/
|
|
294
|
+
function expand_wiener(coefficients, offset, filter) {
|
|
295
|
+
filter[3] = 128;
|
|
296
|
+
|
|
297
|
+
for (let i = 0; i < 3; i++) {
|
|
298
|
+
const c = coefficients[offset + i];
|
|
299
|
+
|
|
300
|
+
filter[i] = c;
|
|
301
|
+
filter[6 - i] = c;
|
|
302
|
+
filter[3] -= 2 * c;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @param {Av1FrameContext} ctx
|
|
308
|
+
* @param {Uint16Array[]} output
|
|
309
|
+
* @param {Int32Array} stride
|
|
310
|
+
* @param {number} plane
|
|
311
|
+
* @param {number} unit_index
|
|
312
|
+
* @param {number} x
|
|
313
|
+
* @param {number} y
|
|
314
|
+
* @param {number} w
|
|
315
|
+
* @param {number} h
|
|
316
|
+
* @see AV1 spec 7.17.2 "Self guided filter process"
|
|
317
|
+
*/
|
|
318
|
+
function self_guided_filter(ctx, output, stride, plane, unit_index, x, y, w, h) {
|
|
319
|
+
const set = ctx.lr_sgr_set[unit_index];
|
|
320
|
+
|
|
321
|
+
box_filter(ctx, x, y, w, h, set, 0, FLT0);
|
|
322
|
+
box_filter(ctx, x, y, w, h, set, 1, FLT1);
|
|
323
|
+
|
|
324
|
+
const w0 = ctx.lr_sgr_xqd[unit_index * 2];
|
|
325
|
+
const w1 = ctx.lr_sgr_xqd[unit_index * 2 + 1];
|
|
326
|
+
const w2 = (1 << SGRPROJ_PRJ_BITS) - w0 - w1;
|
|
327
|
+
|
|
328
|
+
const r0 = SGR_PARAMS[set * 4];
|
|
329
|
+
const r1 = SGR_PARAMS[set * 4 + 2];
|
|
330
|
+
|
|
331
|
+
const max_value = (1 << ctx.bit_depth) - 1;
|
|
332
|
+
|
|
333
|
+
const plane_output = output[plane];
|
|
334
|
+
const plane_stride = stride[plane];
|
|
335
|
+
|
|
336
|
+
for (let i = 0; i < h; i++) {
|
|
337
|
+
for (let j = 0; j < w; j++) {
|
|
338
|
+
const u = SOURCE.cdef[(y + i) * SOURCE.stride + x + j] << SGRPROJ_RST_BITS;
|
|
339
|
+
|
|
340
|
+
let v = w1 * u;
|
|
341
|
+
|
|
342
|
+
v += r0 !== 0 ? w0 * FLT0[i * MI_SIZE + j] : w0 * u;
|
|
343
|
+
v += r1 !== 0 ? w2 * FLT1[i * MI_SIZE + j] : w2 * u;
|
|
344
|
+
|
|
345
|
+
plane_output[(y + i) * plane_stride + x + j] = clamp(
|
|
346
|
+
round2(v, SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS),
|
|
347
|
+
0,
|
|
348
|
+
max_value
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @param {Av1FrameContext} ctx
|
|
356
|
+
* @param {number} x
|
|
357
|
+
* @param {number} y
|
|
358
|
+
* @param {number} w
|
|
359
|
+
* @param {number} h
|
|
360
|
+
* @param {number} set
|
|
361
|
+
* @param {number} pass
|
|
362
|
+
* @param {Int32Array} out
|
|
363
|
+
* @see AV1 spec 7.17.3 "Box filter process"
|
|
364
|
+
*/
|
|
365
|
+
function box_filter(ctx, x, y, w, h, set, pass, out) {
|
|
366
|
+
const r = SGR_PARAMS[set * 4 + pass * 2];
|
|
367
|
+
|
|
368
|
+
if (r === 0) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const eps = SGR_PARAMS[set * 4 + pass * 2 + 1];
|
|
373
|
+
|
|
374
|
+
const bit_depth = ctx.bit_depth;
|
|
375
|
+
|
|
376
|
+
const n = (2 * r + 1) * (2 * r + 1);
|
|
377
|
+
const n2e = n * n * eps;
|
|
378
|
+
const s = Math.floor(((1 << SGRPROJ_MTABLE_BITS) + Math.floor(n2e / 2)) / n2e);
|
|
379
|
+
|
|
380
|
+
const one_over_n = Math.floor(((1 << SGRPROJ_RECIP_BITS) + Math.floor(n / 2)) / n);
|
|
381
|
+
|
|
382
|
+
for (let i = -1; i < h + 1; i++) {
|
|
383
|
+
for (let j = -1; j < w + 1; j++) {
|
|
384
|
+
let a = 0;
|
|
385
|
+
let b = 0;
|
|
386
|
+
|
|
387
|
+
for (let dy = -r; dy <= r; dy++) {
|
|
388
|
+
for (let dx = -r; dx <= r; dx++) {
|
|
389
|
+
const c = source_sample(x + j + dx, y + i + dy);
|
|
390
|
+
|
|
391
|
+
a += c * c;
|
|
392
|
+
b += c;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
a = round2(a, 2 * (bit_depth - 8));
|
|
397
|
+
|
|
398
|
+
const d = round2(b, bit_depth - 8);
|
|
399
|
+
|
|
400
|
+
const p = Math.max(0, a * n - d * d);
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* `round2_wide`, not `round2`: `p` reaches n^2 times the window variance and `s` is up to
|
|
404
|
+
* 3236, so `p * s` runs past 2^31 on ordinary content — a 3x3 window across a hard
|
|
405
|
+
* black/white edge at 8 bit gives 3,787,576,200, where the shifted form returns -484
|
|
406
|
+
* against an exact 3612. `a2` then comes out at 257 instead of the saturated 256 and the
|
|
407
|
+
* pixel is filtered with an inverted correction, exactly where restoration is meant to
|
|
408
|
+
* help. A `z` of -1 would divide by zero and black the pixel out outright.
|
|
409
|
+
*/
|
|
410
|
+
const z = round2_wide(p * s, SGRPROJ_MTABLE_BITS);
|
|
411
|
+
|
|
412
|
+
let a2;
|
|
413
|
+
|
|
414
|
+
if (z >= 255) {
|
|
415
|
+
a2 = 256;
|
|
416
|
+
} else if (z === 0) {
|
|
417
|
+
a2 = 1;
|
|
418
|
+
} else {
|
|
419
|
+
a2 = Math.floor(((z << SGRPROJ_SGR_BITS) + Math.floor(z / 2)) / (z + 1));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const b2 = ((1 << SGRPROJ_SGR_BITS) - a2) * b * one_over_n;
|
|
423
|
+
|
|
424
|
+
const index = (i + 1) * BOX_STRIDE + j + 1;
|
|
425
|
+
|
|
426
|
+
BOX_A[index] = a2;
|
|
427
|
+
//same overflow as `z` above: at 12 bit a flat near-white region gives b2 = 4,281,322,500
|
|
428
|
+
BOX_B[index] = round2_wide(b2, SGRPROJ_RECIP_BITS);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
for (let i = 0; i < h; i++) {
|
|
433
|
+
const shift = pass === 0 && (i & 1) !== 0 ? 4 : 5;
|
|
434
|
+
|
|
435
|
+
for (let j = 0; j < w; j++) {
|
|
436
|
+
let a = 0;
|
|
437
|
+
let b = 0;
|
|
438
|
+
|
|
439
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
440
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
441
|
+
let weight;
|
|
442
|
+
|
|
443
|
+
if (pass === 0) {
|
|
444
|
+
weight = ((i + dy) & 1) !== 0 ? (dx === 0 ? 6 : 5) : 0;
|
|
445
|
+
} else {
|
|
446
|
+
weight = dx === 0 || dy === 0 ? 4 : 3;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const index = (i + dy + 1) * BOX_STRIDE + j + dx + 1;
|
|
450
|
+
|
|
451
|
+
a += weight * BOX_A[index];
|
|
452
|
+
b += weight * BOX_B[index];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const v = a * SOURCE.cdef[(y + i) * SOURCE.stride + x + j] + b;
|
|
457
|
+
|
|
458
|
+
out[i * MI_SIZE + j] = round2(v, SGRPROJ_SGR_BITS + shift - SGRPROJ_RST_BITS);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|