supervision 0.2.8 → 0.2.9
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/dist/index.js +283 -32
- package/dist/index.js.map +1 -1
- package/dist/renderers/media-clahe.d.ts +6 -0
- package/dist/renderers/media-clahe.d.ts.map +1 -0
- package/dist/renderers/pixi-media-inspection-filter.d.ts +8 -0
- package/dist/renderers/pixi-media-inspection-filter.d.ts.map +1 -0
- package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +4 -0
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10650,6 +10650,260 @@ function matches(left, right) {
|
|
|
10650
10650
|
left.every((value, index) => Object.is(value, right[index])));
|
|
10651
10651
|
}
|
|
10652
10652
|
|
|
10653
|
+
const BINS = 256;
|
|
10654
|
+
const TILE_COUNT = 8;
|
|
10655
|
+
const CLIP_LIMIT = 2;
|
|
10656
|
+
function computeMediaClahe(rgba, width, height) {
|
|
10657
|
+
const tilesX = Math.min(TILE_COUNT, width);
|
|
10658
|
+
const tilesY = Math.min(TILE_COUNT, height);
|
|
10659
|
+
const luts = new Uint8Array(tilesX * tilesY * BINS);
|
|
10660
|
+
const histogram = new Uint32Array(BINS);
|
|
10661
|
+
for (let ty = 0; ty < tilesY; ty++) {
|
|
10662
|
+
const y0 = Math.floor((ty * height) / tilesY);
|
|
10663
|
+
const y1 = Math.floor(((ty + 1) * height) / tilesY);
|
|
10664
|
+
for (let tx = 0; tx < tilesX; tx++) {
|
|
10665
|
+
const x0 = Math.floor((tx * width) / tilesX);
|
|
10666
|
+
const x1 = Math.floor(((tx + 1) * width) / tilesX);
|
|
10667
|
+
histogram.fill(0);
|
|
10668
|
+
for (let y = y0; y < y1; y++) {
|
|
10669
|
+
for (let x = x0; x < x1; x++) {
|
|
10670
|
+
const pixel = (y * width + x) * 4;
|
|
10671
|
+
const luma = Math.round(0.299 * rgba[pixel] +
|
|
10672
|
+
0.587 * rgba[pixel + 1] +
|
|
10673
|
+
0.114 * rgba[pixel + 2]);
|
|
10674
|
+
histogram[luma]++;
|
|
10675
|
+
}
|
|
10676
|
+
}
|
|
10677
|
+
const area = (x1 - x0) * (y1 - y0);
|
|
10678
|
+
const limit = Math.max(1, Math.floor((CLIP_LIMIT * area) / BINS));
|
|
10679
|
+
let excess = 0;
|
|
10680
|
+
for (let i = 0; i < BINS; i++) {
|
|
10681
|
+
if (histogram[i] > limit) {
|
|
10682
|
+
excess += histogram[i] - limit;
|
|
10683
|
+
histogram[i] = limit;
|
|
10684
|
+
}
|
|
10685
|
+
}
|
|
10686
|
+
const perBin = Math.floor(excess / BINS);
|
|
10687
|
+
let residual = excess - perBin * BINS;
|
|
10688
|
+
for (let i = 0; i < BINS; i++)
|
|
10689
|
+
histogram[i] += perBin;
|
|
10690
|
+
const residualStep = Math.max(Math.floor(BINS / Math.max(residual, 1)), 1);
|
|
10691
|
+
for (let i = 0; i < BINS && residual > 0; i += residualStep, residual--) {
|
|
10692
|
+
histogram[i]++;
|
|
10693
|
+
}
|
|
10694
|
+
const offset = (ty * tilesX + tx) * BINS;
|
|
10695
|
+
let cumulative = 0;
|
|
10696
|
+
for (let i = 0; i < BINS; i++) {
|
|
10697
|
+
cumulative += histogram[i];
|
|
10698
|
+
luts[offset + i] = Math.round((cumulative * (BINS - 1)) / area);
|
|
10699
|
+
}
|
|
10700
|
+
}
|
|
10701
|
+
}
|
|
10702
|
+
return { luts, tilesX, tilesY };
|
|
10703
|
+
}
|
|
10704
|
+
|
|
10705
|
+
/**
|
|
10706
|
+
* Pixi keys its program cache by source, so one program is shared by every
|
|
10707
|
+
* shader built from it. `destroy(true)` takes that program with it, leaving
|
|
10708
|
+
* both the peers still rendering through it and the next shader built from the
|
|
10709
|
+
* same source bound to nothing.
|
|
10710
|
+
*/
|
|
10711
|
+
function destroyShaderKeepingProgram(shader) {
|
|
10712
|
+
try {
|
|
10713
|
+
shader.destroy();
|
|
10714
|
+
}
|
|
10715
|
+
catch {
|
|
10716
|
+
// Pixi has already invalidated this shader's resource group.
|
|
10717
|
+
}
|
|
10718
|
+
}
|
|
10719
|
+
/**
|
|
10720
|
+
* Pixi uploads a placeholder while it builds a shader's first bind group, and
|
|
10721
|
+
* WebGPU rejects a canvas that was never given a rendering context.
|
|
10722
|
+
*/
|
|
10723
|
+
function createShaderPlaceholderCanvas() {
|
|
10724
|
+
if (typeof document === "undefined") {
|
|
10725
|
+
return { height: 1, width: 1 };
|
|
10726
|
+
}
|
|
10727
|
+
const canvas = document.createElement("canvas");
|
|
10728
|
+
canvas.height = 1;
|
|
10729
|
+
canvas.width = 1;
|
|
10730
|
+
canvas.getContext("2d");
|
|
10731
|
+
return canvas;
|
|
10732
|
+
}
|
|
10733
|
+
|
|
10734
|
+
const HISTOGRAM_MAX_DIMENSION = 1024;
|
|
10735
|
+
const vertex = `
|
|
10736
|
+
in vec2 aPosition;
|
|
10737
|
+
out vec2 vTextureCoord;
|
|
10738
|
+
out vec2 vImageCoord;
|
|
10739
|
+
uniform vec4 uInputSize;
|
|
10740
|
+
uniform vec4 uOutputFrame;
|
|
10741
|
+
uniform vec4 uOutputTexture;
|
|
10742
|
+
uniform mat3 uImageMatrix;
|
|
10743
|
+
void main() {
|
|
10744
|
+
vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
|
|
10745
|
+
position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
|
|
10746
|
+
position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
|
|
10747
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
10748
|
+
vTextureCoord = aPosition * (uOutputFrame.zw * uInputSize.zw);
|
|
10749
|
+
vImageCoord = (uImageMatrix * vec3(vTextureCoord, 1.0)).xy;
|
|
10750
|
+
}
|
|
10751
|
+
`;
|
|
10752
|
+
const fragment = `
|
|
10753
|
+
in vec2 vTextureCoord;
|
|
10754
|
+
in vec2 vImageCoord;
|
|
10755
|
+
out vec4 finalColor;
|
|
10756
|
+
uniform sampler2D uTexture;
|
|
10757
|
+
uniform sampler2D uLut;
|
|
10758
|
+
uniform vec2 uTiles;
|
|
10759
|
+
uniform float uGamma;
|
|
10760
|
+
uniform float uAuto;
|
|
10761
|
+
float lookup(vec2 tile, float luma) {
|
|
10762
|
+
float row = tile.y * uTiles.x + tile.x;
|
|
10763
|
+
return texture(uLut, vec2((luma * 255.0 + 0.5) / 256.0,
|
|
10764
|
+
(row + 0.5) / (uTiles.x * uTiles.y))).r;
|
|
10765
|
+
}
|
|
10766
|
+
void main() {
|
|
10767
|
+
vec4 color = texture(uTexture, vTextureCoord);
|
|
10768
|
+
if (color.a == 0.0) { finalColor = color; return; }
|
|
10769
|
+
vec3 rgb = color.rgb / color.a;
|
|
10770
|
+
if (uAuto > 0.5) {
|
|
10771
|
+
float luma = dot(rgb, vec3(0.299, 0.587, 0.114));
|
|
10772
|
+
vec2 tilePosition = clamp(vImageCoord, 0.0, 1.0) * uTiles - 0.5;
|
|
10773
|
+
vec2 t0 = floor(tilePosition);
|
|
10774
|
+
vec2 f = tilePosition - t0;
|
|
10775
|
+
vec2 t1 = clamp(t0 + 1.0, vec2(0.0), uTiles - 1.0);
|
|
10776
|
+
t0 = clamp(t0, vec2(0.0), uTiles - 1.0);
|
|
10777
|
+
float top = mix(lookup(t0, luma), lookup(vec2(t1.x, t0.y), luma), f.x);
|
|
10778
|
+
float bottom = mix(lookup(vec2(t0.x, t1.y), luma), lookup(t1, luma), f.x);
|
|
10779
|
+
rgb = clamp(rgb + (mix(top, bottom, f.y) - luma), 0.0, 1.0);
|
|
10780
|
+
}
|
|
10781
|
+
finalColor = vec4(pow(rgb, vec3(1.0 / uGamma)) * color.a, color.a);
|
|
10782
|
+
}
|
|
10783
|
+
`;
|
|
10784
|
+
const gpuSource = `
|
|
10785
|
+
struct GlobalFilterUniforms {
|
|
10786
|
+
uInputSize: vec4<f32>, uInputPixel: vec4<f32>, uInputClamp: vec4<f32>,
|
|
10787
|
+
uOutputFrame: vec4<f32>, uGlobalFrame: vec4<f32>, uOutputTexture: vec4<f32>,
|
|
10788
|
+
};
|
|
10789
|
+
struct InspectionUniforms {
|
|
10790
|
+
uImageMatrix: mat3x3<f32>, uTiles: vec2<f32>, uGamma: f32, uAuto: f32,
|
|
10791
|
+
};
|
|
10792
|
+
@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
|
|
10793
|
+
@group(0) @binding(1) var uTexture: texture_2d<f32>;
|
|
10794
|
+
@group(0) @binding(2) var uSampler: sampler;
|
|
10795
|
+
@group(1) @binding(0) var<uniform> inspectionUniforms: InspectionUniforms;
|
|
10796
|
+
@group(1) @binding(1) var uLut: texture_2d<f32>;
|
|
10797
|
+
@group(1) @binding(2) var uLutSampler: sampler;
|
|
10798
|
+
struct VertexOutput {
|
|
10799
|
+
@builtin(position) position: vec4<f32>,
|
|
10800
|
+
@location(0) uv: vec2<f32>, @location(1) image: vec2<f32>,
|
|
10801
|
+
};
|
|
10802
|
+
@vertex fn mainVertex(@location(0) aPosition: vec2<f32>) -> VertexOutput {
|
|
10803
|
+
var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
|
|
10804
|
+
position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
|
|
10805
|
+
position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
|
|
10806
|
+
let uv = aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
|
|
10807
|
+
return VertexOutput(vec4(position, 0.0, 1.0), uv,
|
|
10808
|
+
(inspectionUniforms.uImageMatrix * vec3(uv, 1.0)).xy);
|
|
10809
|
+
}
|
|
10810
|
+
fn lookup(tile: vec2<f32>, luma: f32) -> f32 {
|
|
10811
|
+
let tiles = inspectionUniforms.uTiles;
|
|
10812
|
+
let row = tile.y * tiles.x + tile.x;
|
|
10813
|
+
return textureSampleLevel(uLut, uLutSampler,
|
|
10814
|
+
vec2((luma * 255.0 + 0.5) / 256.0, (row + 0.5) / (tiles.x * tiles.y)), 0.0).r;
|
|
10815
|
+
}
|
|
10816
|
+
@fragment fn mainFragment(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
10817
|
+
let color = textureSample(uTexture, uSampler, input.uv);
|
|
10818
|
+
if (color.a == 0.0) { return color; }
|
|
10819
|
+
var rgb = color.rgb / color.a;
|
|
10820
|
+
if (inspectionUniforms.uAuto > 0.5) {
|
|
10821
|
+
let luma = dot(rgb, vec3(0.299, 0.587, 0.114));
|
|
10822
|
+
let tiles = inspectionUniforms.uTiles;
|
|
10823
|
+
let tilePosition = clamp(input.image, vec2(0.0), vec2(1.0)) * tiles - 0.5;
|
|
10824
|
+
let unbounded = floor(tilePosition);
|
|
10825
|
+
let f = tilePosition - unbounded;
|
|
10826
|
+
let t0 = clamp(unbounded, vec2(0.0), tiles - 1.0);
|
|
10827
|
+
let t1 = clamp(unbounded + 1.0, vec2(0.0), tiles - 1.0);
|
|
10828
|
+
let top = mix(lookup(t0, luma), lookup(vec2(t1.x, t0.y), luma), f.x);
|
|
10829
|
+
let bottom = mix(lookup(vec2(t0.x, t1.y), luma), lookup(t1, luma), f.x);
|
|
10830
|
+
rgb = clamp(rgb + (mix(top, bottom, f.y) - luma), vec3(0.0), vec3(1.0));
|
|
10831
|
+
}
|
|
10832
|
+
return vec4(pow(rgb, vec3(1.0 / inspectionUniforms.uGamma)) * color.a, color.a);
|
|
10833
|
+
}
|
|
10834
|
+
`;
|
|
10835
|
+
function createPixiMediaInspectionFilter(sprite, pixi) {
|
|
10836
|
+
const { BufferImageSource, Filter, Matrix } = pixi;
|
|
10837
|
+
const pixels = new Uint8Array(256 * 64 * 4);
|
|
10838
|
+
const lutSource = new BufferImageSource({
|
|
10839
|
+
resource: pixels,
|
|
10840
|
+
width: 256,
|
|
10841
|
+
height: 64,
|
|
10842
|
+
scaleMode: "linear",
|
|
10843
|
+
alphaMode: "no-premultiply-alpha",
|
|
10844
|
+
});
|
|
10845
|
+
const filter = Filter.from({
|
|
10846
|
+
gl: { vertex: vertex, fragment: fragment },
|
|
10847
|
+
gpu: {
|
|
10848
|
+
vertex: { source: gpuSource, entryPoint: "mainVertex" },
|
|
10849
|
+
fragment: { source: gpuSource, entryPoint: "mainFragment" },
|
|
10850
|
+
},
|
|
10851
|
+
resolution: "inherit",
|
|
10852
|
+
resources: {
|
|
10853
|
+
inspectionUniforms: {
|
|
10854
|
+
uImageMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
10855
|
+
uTiles: { value: new Float32Array([8, 8]), type: "vec2<f32>" },
|
|
10856
|
+
uGamma: { value: 1, type: "f32" },
|
|
10857
|
+
uAuto: { value: 0, type: "f32" },
|
|
10858
|
+
},
|
|
10859
|
+
uLut: lutSource,
|
|
10860
|
+
uLutSampler: lutSource.style,
|
|
10861
|
+
},
|
|
10862
|
+
});
|
|
10863
|
+
const uniforms = filter.resources.inspectionUniforms.uniforms;
|
|
10864
|
+
let hasHistogram = false;
|
|
10865
|
+
filter.apply = (manager, input, output, clear) => {
|
|
10866
|
+
// The filter input is clipped by the viewport; lookup tiles stay anchored
|
|
10867
|
+
// to the full image when the user zooms or pans.
|
|
10868
|
+
manager.calculateSpriteMatrix(uniforms.uImageMatrix, sprite);
|
|
10869
|
+
manager.applyFilter(filter, input, output, clear);
|
|
10870
|
+
};
|
|
10871
|
+
return {
|
|
10872
|
+
filter,
|
|
10873
|
+
invalidateImage() {
|
|
10874
|
+
hasHistogram = false;
|
|
10875
|
+
},
|
|
10876
|
+
update(gamma, autoContrast, image) {
|
|
10877
|
+
uniforms.uGamma = Math.max(gamma, 0.0001);
|
|
10878
|
+
if (autoContrast && image && !hasHistogram) {
|
|
10879
|
+
const scale = Math.min(1, HISTOGRAM_MAX_DIMENSION / Math.max(image.width, image.height));
|
|
10880
|
+
const sample = document.createElement("canvas");
|
|
10881
|
+
sample.width = Math.max(1, Math.round(image.width * scale));
|
|
10882
|
+
sample.height = Math.max(1, Math.round(image.height * scale));
|
|
10883
|
+
const context = sample.getContext("2d", { willReadFrequently: true });
|
|
10884
|
+
context.drawImage(image, 0, 0, sample.width, sample.height);
|
|
10885
|
+
const { luts, tilesX, tilesY } = computeMediaClahe(context.getImageData(0, 0, sample.width, sample.height).data, sample.width, sample.height);
|
|
10886
|
+
for (let i = 0; i < luts.length; i++) {
|
|
10887
|
+
pixels[i * 4] = luts[i];
|
|
10888
|
+
pixels[i * 4 + 1] = luts[i];
|
|
10889
|
+
pixels[i * 4 + 2] = luts[i];
|
|
10890
|
+
pixels[i * 4 + 3] = 255;
|
|
10891
|
+
}
|
|
10892
|
+
uniforms.uTiles.set([tilesX, tilesY]);
|
|
10893
|
+
lutSource.resource = pixels.subarray(0, luts.length * 4);
|
|
10894
|
+
lutSource.resize(256, tilesX * tilesY);
|
|
10895
|
+
lutSource.update();
|
|
10896
|
+
hasHistogram = true;
|
|
10897
|
+
}
|
|
10898
|
+
uniforms.uAuto = autoContrast && hasHistogram ? 1 : 0;
|
|
10899
|
+
},
|
|
10900
|
+
destroy() {
|
|
10901
|
+
destroyShaderKeepingProgram(filter);
|
|
10902
|
+
lutSource.destroy();
|
|
10903
|
+
},
|
|
10904
|
+
};
|
|
10905
|
+
}
|
|
10906
|
+
|
|
10653
10907
|
/**
|
|
10654
10908
|
* Which frame a media time draws and how much of it is cooked, as two separate
|
|
10655
10909
|
* facts. Reporting readiness is what the snapshot and the token are for; a
|
|
@@ -10789,35 +11043,6 @@ void main(void) {
|
|
|
10789
11043
|
}
|
|
10790
11044
|
`;
|
|
10791
11045
|
|
|
10792
|
-
/**
|
|
10793
|
-
* Pixi keys its program cache by source, so one program is shared by every
|
|
10794
|
-
* shader built from it. `destroy(true)` takes that program with it, leaving
|
|
10795
|
-
* both the peers still rendering through it and the next shader built from the
|
|
10796
|
-
* same source bound to nothing.
|
|
10797
|
-
*/
|
|
10798
|
-
function destroyShaderKeepingProgram(shader) {
|
|
10799
|
-
try {
|
|
10800
|
-
shader.destroy();
|
|
10801
|
-
}
|
|
10802
|
-
catch {
|
|
10803
|
-
// Pixi has already invalidated this shader's resource group.
|
|
10804
|
-
}
|
|
10805
|
-
}
|
|
10806
|
-
/**
|
|
10807
|
-
* Pixi uploads a placeholder while it builds a shader's first bind group, and
|
|
10808
|
-
* WebGPU rejects a canvas that was never given a rendering context.
|
|
10809
|
-
*/
|
|
10810
|
-
function createShaderPlaceholderCanvas() {
|
|
10811
|
-
if (typeof document === "undefined") {
|
|
10812
|
-
return { height: 1, width: 1 };
|
|
10813
|
-
}
|
|
10814
|
-
const canvas = document.createElement("canvas");
|
|
10815
|
-
canvas.height = 1;
|
|
10816
|
-
canvas.width = 1;
|
|
10817
|
-
canvas.getContext("2d");
|
|
10818
|
-
return canvas;
|
|
10819
|
-
}
|
|
10820
|
-
|
|
10821
11046
|
/**
|
|
10822
11047
|
* The halo a detection actually paints, or `undefined`. A style may answer an
|
|
10823
11048
|
* instruction that draws nothing, and both the coverage the halo prepares and
|
|
@@ -18572,6 +18797,9 @@ async function createPixiMediaScene(options) {
|
|
|
18572
18797
|
let isScrubbing = false;
|
|
18573
18798
|
let displayBrightness = 1;
|
|
18574
18799
|
let displayContrast = 1;
|
|
18800
|
+
let displayGamma = 1;
|
|
18801
|
+
let displayAutoContrast = false;
|
|
18802
|
+
let inspectionFilter;
|
|
18575
18803
|
let viewportScale = 1;
|
|
18576
18804
|
let hasPresentedSample = false;
|
|
18577
18805
|
let mediaHeight = 0;
|
|
@@ -19429,6 +19657,7 @@ async function createPixiMediaScene(options) {
|
|
|
19429
19657
|
if (!collectFrameTimings) {
|
|
19430
19658
|
sample.draw(stagingContext, 0, 0, mediaWidth, mediaHeight);
|
|
19431
19659
|
presentedSampleTimestamp = sample.timestamp;
|
|
19660
|
+
refreshInspectionImage();
|
|
19432
19661
|
stagingTextureSource?.update();
|
|
19433
19662
|
stagingTexture?.update();
|
|
19434
19663
|
const { boxState, regionState } = drawFramePresentLayers(annotationDrawLayers, sample.timestamp);
|
|
@@ -19444,6 +19673,7 @@ async function createPixiMediaScene(options) {
|
|
|
19444
19673
|
const mediaUploadMs = measure(() => {
|
|
19445
19674
|
sample.draw(stagingContext, 0, 0, mediaWidth, mediaHeight);
|
|
19446
19675
|
presentedSampleTimestamp = sample.timestamp;
|
|
19676
|
+
refreshInspectionImage();
|
|
19447
19677
|
stagingTextureSource?.update();
|
|
19448
19678
|
stagingTexture?.update();
|
|
19449
19679
|
});
|
|
@@ -19507,15 +19737,27 @@ async function createPixiMediaScene(options) {
|
|
|
19507
19737
|
},
|
|
19508
19738
|
setDisplayAdjustments(adjustments) {
|
|
19509
19739
|
void import('pixi.js').then(({ ColorMatrixFilter }) => {
|
|
19740
|
+
if (isDestroyed)
|
|
19741
|
+
return;
|
|
19510
19742
|
const brightness = adjustments.brightness ?? 1;
|
|
19511
19743
|
const contrast = adjustments.contrast ?? 1;
|
|
19512
19744
|
const filter = new ColorMatrixFilter();
|
|
19513
19745
|
// Preserve the renderer's pixel grid through the adjustment pass.
|
|
19514
19746
|
filter.resolution = "inherit";
|
|
19515
19747
|
filter.brightness(brightness, false);
|
|
19516
|
-
|
|
19517
|
-
|
|
19518
|
-
|
|
19748
|
+
displayGamma = adjustments.gamma ?? 1;
|
|
19749
|
+
displayAutoContrast = stillFrame && adjustments.autoContrast === true;
|
|
19750
|
+
filter.contrast(displayAutoContrast ? 0 : contrast - 1, true);
|
|
19751
|
+
if (mediaSprite) {
|
|
19752
|
+
if (displayGamma !== 1 || displayAutoContrast) {
|
|
19753
|
+
inspectionFilter ??= createPixiMediaInspectionFilter(mediaSprite, pixi);
|
|
19754
|
+
inspectionFilter.update(displayGamma, displayAutoContrast, hasPresentedSample ? stagingCanvas : null);
|
|
19755
|
+
mediaSprite.filters = [inspectionFilter.filter, filter];
|
|
19756
|
+
}
|
|
19757
|
+
else {
|
|
19758
|
+
mediaSprite.filters = [filter];
|
|
19759
|
+
}
|
|
19760
|
+
}
|
|
19519
19761
|
displayBrightness = brightness;
|
|
19520
19762
|
displayContrast = contrast;
|
|
19521
19763
|
renderOnChange();
|
|
@@ -19822,6 +20064,7 @@ async function createPixiMediaScene(options) {
|
|
|
19822
20064
|
// a decoder buffer in a producer that outlives the scene.
|
|
19823
20065
|
frameChannel?.onPresentedFrame((presented) => presented.frame.close());
|
|
19824
20066
|
destroyBatchTextureBindings();
|
|
20067
|
+
inspectionFilter?.destroy();
|
|
19825
20068
|
mediaCompositor?.destroy();
|
|
19826
20069
|
interactionLayer?.destroy();
|
|
19827
20070
|
interactionPresentationLayer?.destroy();
|
|
@@ -20532,6 +20775,12 @@ async function createPixiMediaScene(options) {
|
|
|
20532
20775
|
app.ticker.remove(drawAnnotationOverlayNow);
|
|
20533
20776
|
}
|
|
20534
20777
|
}
|
|
20778
|
+
function refreshInspectionImage() {
|
|
20779
|
+
if (!stillFrame || !inspectionFilter)
|
|
20780
|
+
return;
|
|
20781
|
+
inspectionFilter.invalidateImage();
|
|
20782
|
+
inspectionFilter.update(displayGamma, displayAutoContrast, stagingCanvas);
|
|
20783
|
+
}
|
|
20535
20784
|
function hasLoadingDetections() {
|
|
20536
20785
|
const loading = currentVisibility?.loadingDetectionIds;
|
|
20537
20786
|
if (!loading)
|
|
@@ -20563,6 +20812,8 @@ async function createPixiMediaScene(options) {
|
|
|
20563
20812
|
app.renderer.resolution,
|
|
20564
20813
|
displayBrightness,
|
|
20565
20814
|
displayContrast,
|
|
20815
|
+
displayGamma,
|
|
20816
|
+
displayAutoContrast,
|
|
20566
20817
|
...RENDERED_PRESENTATION_FIELDS.map((field) => appliedPresentation?.[field]),
|
|
20567
20818
|
// Styles above compare by identity; mask opacity is applied after
|
|
20568
20819
|
// preparation, so a host can move it inside a style object it keeps.
|