ranger-compiler 3.5.0 → 3.5.2
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/CHANGELOG.md +1160 -19
- package/LICENSE +3 -1
- package/README.md +124 -29
- package/dist/Lang.rgr +1188 -290
- package/dist/api.d.ts +2343 -879
- package/dist/api.js +79924 -56922
- package/dist/lib/JSON.rgr +102 -91
- package/dist/lib/Shell.rgr +4 -4
- package/dist/lib/apple/AppleToolchain.rgr +4 -4
- package/dist/lib/apple/README.md +5 -4
- package/dist/lib/apple/apple_test.rgr +1 -1
- package/dist/lib/core/README.md +1 -1
- package/dist/lib/evg/EVG.rgr +12 -0
- package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
- package/dist/lib/evg/EVGA11yTree.rgr +894 -0
- package/dist/lib/evg/EVGBox.rgr +267 -0
- package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
- package/dist/lib/evg/EVGCodepoint.rgr +316 -0
- package/dist/lib/evg/EVGColor.rgr +700 -0
- package/dist/lib/evg/EVGCommands.rgr +177 -0
- package/dist/lib/evg/EVGComponent.rgr +331 -0
- package/dist/lib/evg/EVGComponentTest.rgr +387 -0
- package/dist/lib/evg/EVGConnector.rgr +541 -0
- package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
- package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
- package/dist/lib/evg/EVGEasing.rgr +370 -0
- package/dist/lib/evg/EVGEffectTest.rgr +253 -0
- package/dist/lib/evg/EVGElement.rgr +4914 -0
- package/dist/lib/evg/EVGFixedTest.rgr +289 -0
- package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
- package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
- package/dist/lib/evg/EVGFling.rgr +244 -0
- package/dist/lib/evg/EVGFocus.rgr +400 -0
- package/dist/lib/evg/EVGFocusTest.rgr +399 -0
- package/dist/lib/evg/EVGGradient.rgr +322 -0
- package/dist/lib/evg/EVGGrapheme.rgr +190 -0
- package/dist/lib/evg/EVGGrid.rgr +975 -0
- package/dist/lib/evg/EVGHitTest.rgr +208 -0
- package/dist/lib/evg/EVGHoles.rgr +294 -0
- package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
- package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
- package/dist/lib/evg/EVGHostTree.rgr +834 -0
- package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
- package/dist/lib/evg/EVGImageDecode.rgr +117 -0
- package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
- package/dist/lib/evg/EVGInspect.rgr +883 -0
- package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
- package/dist/lib/evg/EVGJsonTest.rgr +535 -0
- package/dist/lib/evg/EVGLayout.rgr +4220 -0
- package/dist/lib/evg/EVGMeasure.rgr +1267 -0
- package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
- package/dist/lib/evg/EVGPatch.rgr +1721 -0
- package/dist/lib/evg/EVGPatchTest.rgr +692 -0
- package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
- package/dist/lib/evg/EVGReconcile.rgr +226 -0
- package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
- package/dist/lib/evg/EVGReject.rgr +126 -0
- package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
- package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
- package/dist/lib/evg/EVGRuler.rgr +232 -0
- package/dist/lib/evg/EVGRulerTest.rgr +172 -0
- package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
- package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
- package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
- package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
- package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
- package/dist/lib/evg/EVGText.rgr +105 -0
- package/dist/lib/evg/EVGTextEngine.rgr +536 -0
- package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
- package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
- package/dist/lib/evg/EVGToolbar.rgr +1369 -0
- package/dist/lib/evg/EVGTransition.rgr +728 -0
- package/dist/lib/evg/EVGTreeJson.rgr +721 -0
- package/dist/lib/evg/EVGUnit.rgr +554 -0
- package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
- package/dist/lib/evg/EvgApp.rgr +189 -0
- package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
- package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
- package/dist/lib/evg/EvgHost.rgr +451 -0
- package/dist/lib/evg/EvgTest.rgr +91 -0
- package/dist/lib/evg/EvgTraceColor.rgr +236 -0
- package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
- package/dist/lib/evg/EvgTraceFit.rgr +767 -0
- package/dist/lib/evg/EvgTracePath.rgr +435 -0
- package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
- package/dist/lib/evg/EvgViewport.rgr +313 -0
- package/dist/lib/evg/FxDemoDoc.rgr +185 -0
- package/dist/lib/evg/HOSTS.md +227 -0
- package/dist/lib/evg/ISSUES.md +835 -0
- package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
- package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
- package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
- package/dist/lib/evg/PLAN_EVG.md +518 -0
- package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
- package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
- package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
- package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
- package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
- package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
- package/dist/lib/evg/PathBuilder.rgr +333 -0
- package/dist/lib/evg/README.md +1236 -0
- package/dist/lib/evg/SPEC.md +1252 -0
- package/dist/lib/evg/SVGPathParser.rgr +1199 -0
- package/dist/lib/evg/SvgParser.rgr +1736 -0
- package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
- package/dist/lib/evg/VectorShapes.rgr +331 -0
- package/dist/lib/evg/VectorStroke.rgr +107 -0
- package/dist/lib/evg/VectorViewBox.rgr +380 -0
- package/dist/lib/evg/agent/README.md +290 -0
- package/dist/lib/evg/agent/evg_agent.rgr +899 -0
- package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
- package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
- package/dist/lib/evg/agent/fixtures/connector.css +63 -0
- package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
- package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
- package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
- package/dist/lib/evg/agent/fixtures/popover.css +99 -0
- package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
- package/dist/lib/evg/agent/roundtrip.sh +74 -0
- package/dist/lib/evg/agent/smoke.sh +230 -0
- package/dist/lib/evg/android/README.md +97 -0
- package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
- package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
- package/dist/lib/evg/android/androidstubs/App.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
- package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
- package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
- package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
- package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
- package/dist/lib/evg/android/androidstubs/View.kt +113 -0
- package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
- package/dist/lib/evg/apple/README.md +128 -0
- package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
- package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
- package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
- package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
- package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
- package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
- package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
- package/dist/lib/evg/bench/README.md +64 -0
- package/dist/lib/evg/bench/layout-bench.mjs +316 -0
- package/dist/lib/evg/bench/layout-cases.mjs +510 -0
- package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
- package/dist/lib/evg/bin/.gitignore +17 -0
- package/dist/lib/evg/evg_test.rgr +313 -0
- package/dist/lib/evg/gl/README.md +198 -0
- package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
- package/dist/lib/evg/gl/blur-check.mjs +399 -0
- package/dist/lib/evg/gl/boxmodel.json +1 -0
- package/dist/lib/evg/gl/demo.html +46 -0
- package/dist/lib/evg/gl/effect-presets.css +285 -0
- package/dist/lib/evg/gl/effect-presets.js +63 -0
- package/dist/lib/evg/gl/effect-shots.mjs +135 -0
- package/dist/lib/evg/gl/evg-a11y.js +566 -0
- package/dist/lib/evg/gl/evg-binary.js +160 -0
- package/dist/lib/evg/gl/evg-engine.js +296 -0
- package/dist/lib/evg/gl/evg-fx.js +237 -0
- package/dist/lib/evg/gl/evg-gestures.js +209 -0
- package/dist/lib/evg/gl/evg-list.js +167 -0
- package/dist/lib/evg/gl/evg-measure.js +186 -0
- package/dist/lib/evg/gl/evg-textinput.js +303 -0
- package/dist/lib/evg/gl/evg-view.js +144 -0
- package/dist/lib/evg/gl/evg-webgl.js +3942 -0
- package/dist/lib/evg/gl/fx-check.mjs +998 -0
- package/dist/lib/evg/gl/fx-demo.html +166 -0
- package/dist/lib/evg/gl/fx-demo.js +2 -0
- package/dist/lib/evg/gl/fx-serve.mjs +47 -0
- package/dist/lib/evg/gl/gestures-check.mjs +189 -0
- package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
- package/dist/lib/evg/gl/measure-check.mjs +135 -0
- package/dist/lib/evg/gl/rotation-check.mjs +207 -0
- package/dist/lib/evg/gl/shift-check.mjs +113 -0
- package/dist/lib/evg/gl/stroke-check.mjs +168 -0
- package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
- package/dist/lib/evg/gl/view-check.mjs +343 -0
- package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
- package/dist/lib/evg/html/evg-dom.js +318 -0
- package/dist/lib/evg/html/evg-html.js +600 -0
- package/dist/lib/evg/inspect/README.md +415 -0
- package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
- package/dist/lib/evg/inspect/evg-inspect.js +947 -0
- package/dist/lib/evg/inspect/shots/css.png +0 -0
- package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
- package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
- package/dist/lib/evg/inspect/shots/state.png +0 -0
- package/dist/lib/evg/inspect/shots.mjs +226 -0
- package/dist/lib/evg/oracle/css-blur.json +576 -0
- package/dist/lib/evg/oracle/css-box.json +157 -0
- package/dist/lib/evg/oracle/css-timing.json +709 -0
- package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
- package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
- package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
- package/dist/lib/evg/original/EVGColor.clj +310 -0
- package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
- package/dist/lib/evg/original/SVGPath.rgr +627 -0
- package/dist/lib/evg/original/Vec2.crgr +103 -0
- package/dist/lib/evg/ranger.json +9 -0
- package/dist/lib/evg/showcase/README.md +246 -0
- package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
- package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
- package/dist/lib/evg/showcase/build.mjs +651 -0
- package/dist/lib/evg/showcase/pages/album.tsx +30 -0
- package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
- package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
- package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
- package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
- package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
- package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
- package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
- package/dist/lib/evg/showcase/pages/more.tsx +332 -0
- package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
- package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
- package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
- package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
- package/dist/lib/evg/showcase/pages/units.tsx +35 -0
- package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
- package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
- package/dist/lib/evg/showcase/pages/views.tsx +223 -0
- package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
- package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
- package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
- package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
- package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
- package/dist/lib/evg/showcase/themes/more-default.css +128 -0
- package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
- package/dist/lib/evg/showcase/themes/showcase.css +508 -0
- package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
- package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
- package/dist/lib/evg/showcase/themes/views-default.css +15 -0
- package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
- package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
- package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
- package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
- package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
- package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
- package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
- package/dist/lib/evg/web/responsive/README.md +97 -0
- package/dist/lib/evg/web/responsive/build.mjs +90 -0
- package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
- package/dist/lib/evg/web/responsive/index.html +177 -0
- package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
- package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
- package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
- package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
- package/dist/lib/evg/web/tools/minify.mjs +60 -0
- package/dist/lib/evg/web/tracer/build.mjs +87 -0
- package/dist/lib/evg/web/tracer/index.html +2805 -0
- package/dist/lib/evg/web/tracer/sample.png +0 -0
- package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
- package/dist/lib/evgr/Cargo.lock +21 -0
- package/dist/lib/evgr/Cargo.toml +19 -0
- package/dist/lib/evgr/README.md +140 -0
- package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
- package/dist/lib/evgr/bench/compare.mjs +252 -0
- package/dist/lib/evgr/bench/speed.mjs +277 -0
- package/dist/lib/evgr/bin/.gitignore +3 -0
- package/dist/lib/evgr/src/bin/bench.rs +106 -0
- package/dist/lib/evgr/src/bin/smoke.rs +13 -0
- package/dist/lib/evgr/src/grid.rs +730 -0
- package/dist/lib/evgr/src/lib.rs +1003 -0
- package/dist/lib/evgr/src/style.rs +468 -0
- package/dist/lib/evgr/src/text.rs +83 -0
- package/dist/lib/image/BitReader.rgr +171 -0
- package/dist/lib/image/Buffer.rgr +173 -0
- package/dist/lib/image/DCT.rgr +283 -0
- package/dist/lib/image/Deflate.rgr +339 -0
- package/dist/lib/image/HuffmanDecoder.rgr +181 -0
- package/dist/lib/image/ImageBuffer.rgr +706 -0
- package/dist/lib/image/JPEGDecoder.rgr +808 -0
- package/dist/lib/image/PNGDecoder.rgr +544 -0
- package/dist/lib/image/PNGEncoder.rgr +388 -0
- package/dist/lib/image/PPMImage.rgr +224 -0
- package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
- package/dist/lib/image/README.md +33 -0
- package/dist/lib/image/RasterBuffer.rgr +294 -0
- package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
- package/dist/lib/image/WebPDecoder.rgr +433 -0
- package/dist/lib/image/WebPLossless.rgr +1035 -0
- package/dist/lib/image/WebPLossy.rgr +1929 -0
- package/dist/lib/image/ranger.json +9 -0
- package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
- package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
- package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
- package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
- package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
- package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
- package/dist/lib/image/tests/run_webp_tests.sh +39 -0
- package/dist/lib/rust/RsJson.rgr +468 -0
- package/dist/lib/rust/RsPrelude.rgr +1663 -0
- package/dist/lib/shell_test.rgr +7 -7
- package/dist/lib/stdlib.rgr +21 -5
- package/dist/lib/zip/ranger.json +6 -0
- package/dist/rgrc.js +87318 -67610
- package/package.json +1010 -1040
- package/dist/README.md +0 -117
- package/dist/package.json +0 -47
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; JPEGDecoder.rgr - Full JPEG decoder (baseline DCT)
|
|
4
|
+
; Decodes JPEG to raw RGB pixels
|
|
5
|
+
|
|
6
|
+
Import "Buffer.rgr"
|
|
7
|
+
Import "BitReader.rgr"
|
|
8
|
+
Import "HuffmanDecoder.rgr"
|
|
9
|
+
Import "DCT.rgr"
|
|
10
|
+
Import "ImageBuffer.rgr"
|
|
11
|
+
Import "PPMImage.rgr"
|
|
12
|
+
|
|
13
|
+
class JPEGComponent {
|
|
14
|
+
def id:int 0
|
|
15
|
+
def hSamp:int 1 ; Horizontal sampling factor
|
|
16
|
+
def vSamp:int 1 ; Vertical sampling factor
|
|
17
|
+
def quantTableId:int 0
|
|
18
|
+
def dcTableId:int 0
|
|
19
|
+
def acTableId:int 0
|
|
20
|
+
def prevDC:int 0 ; Previous DC value for differential coding
|
|
21
|
+
|
|
22
|
+
Constructor () {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class QuantizationTable {
|
|
27
|
+
def values:[int]
|
|
28
|
+
def id:int 0
|
|
29
|
+
|
|
30
|
+
Constructor () {
|
|
31
|
+
def i 0
|
|
32
|
+
while (i < 64) {
|
|
33
|
+
push values 1
|
|
34
|
+
i = i + 1
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class JPEGDecoder {
|
|
40
|
+
; Say nothing while decoding.
|
|
41
|
+
;
|
|
42
|
+
; This decoder narrates - twenty-five lines per photograph, every
|
|
43
|
+
; marker and every tenth row - which is useful when you are debugging a
|
|
44
|
+
; JPEG and is noise everywhere else. In a browser it goes to a console
|
|
45
|
+
; nobody has open; in a native window it goes to the terminal, so a book
|
|
46
|
+
; of forty pictures opens behind a thousand lines of scrolling.
|
|
47
|
+
;
|
|
48
|
+
; Off by default, so every existing caller is unchanged. A caller that
|
|
49
|
+
; decodes pictures as a side effect of doing something else turns it on.
|
|
50
|
+
def quiet:boolean false
|
|
51
|
+
|
|
52
|
+
fn say:void (msg:string) {
|
|
53
|
+
if quiet {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
print msg
|
|
57
|
+
}
|
|
58
|
+
def data:buffer (buffer_alloc 0)
|
|
59
|
+
def dataLen:int 0
|
|
60
|
+
|
|
61
|
+
; Image properties
|
|
62
|
+
def width:int 0
|
|
63
|
+
def height:int 0
|
|
64
|
+
def numComponents:int 0
|
|
65
|
+
def precision:int 8
|
|
66
|
+
|
|
67
|
+
; Components
|
|
68
|
+
def components:[JPEGComponent]
|
|
69
|
+
|
|
70
|
+
; Quantization tables (up to 4)
|
|
71
|
+
def quantTables:[QuantizationTable]
|
|
72
|
+
|
|
73
|
+
; Huffman decoder
|
|
74
|
+
def huffman:HuffmanDecoder
|
|
75
|
+
|
|
76
|
+
; DCT transformer
|
|
77
|
+
def idct:IDCT
|
|
78
|
+
|
|
79
|
+
; Scan data position
|
|
80
|
+
def scanDataStart:int 0
|
|
81
|
+
def scanDataLen:int 0
|
|
82
|
+
|
|
83
|
+
; MCU dimensions
|
|
84
|
+
def mcuWidth:int 8
|
|
85
|
+
def mcuHeight:int 8
|
|
86
|
+
def mcusPerRow:int 0
|
|
87
|
+
def mcusPerCol:int 0
|
|
88
|
+
def maxHSamp:int 1
|
|
89
|
+
def maxVSamp:int 1
|
|
90
|
+
|
|
91
|
+
; Restart interval (0 = no restart markers)
|
|
92
|
+
def restartInterval:int 0
|
|
93
|
+
|
|
94
|
+
Constructor () {
|
|
95
|
+
huffman = (new HuffmanDecoder())
|
|
96
|
+
idct = (new IDCT())
|
|
97
|
+
|
|
98
|
+
def i 0
|
|
99
|
+
while (i < 4) {
|
|
100
|
+
push quantTables (new QuantizationTable())
|
|
101
|
+
i = i + 1
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn reset:void () {
|
|
106
|
+
; Reset all state for decoding a new image
|
|
107
|
+
; This is important when reusing the same decoder instance!
|
|
108
|
+
width = 0
|
|
109
|
+
height = 0
|
|
110
|
+
numComponents = 0
|
|
111
|
+
precision = 8
|
|
112
|
+
scanDataStart = 0
|
|
113
|
+
scanDataLen = 0
|
|
114
|
+
mcuWidth = 8
|
|
115
|
+
mcuHeight = 8
|
|
116
|
+
mcusPerRow = 0
|
|
117
|
+
mcusPerCol = 0
|
|
118
|
+
maxHSamp = 1
|
|
119
|
+
maxVSamp = 1
|
|
120
|
+
restartInterval = 0
|
|
121
|
+
|
|
122
|
+
; Clear components
|
|
123
|
+
clear components
|
|
124
|
+
|
|
125
|
+
; Reset Huffman tables
|
|
126
|
+
huffman.dcTable0.resetArrays()
|
|
127
|
+
huffman.dcTable1.resetArrays()
|
|
128
|
+
huffman.acTable0.resetArrays()
|
|
129
|
+
huffman.acTable1.resetArrays()
|
|
130
|
+
|
|
131
|
+
; Reset quantization tables
|
|
132
|
+
def i 0
|
|
133
|
+
while (i < 4) {
|
|
134
|
+
def qt:QuantizationTable (at quantTables i)
|
|
135
|
+
clear qt.values
|
|
136
|
+
def j 0
|
|
137
|
+
while (j < 64) {
|
|
138
|
+
push qt.values 1
|
|
139
|
+
j = j + 1
|
|
140
|
+
}
|
|
141
|
+
i = i + 1
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
fn readUint16BE:int (pos:int) {
|
|
146
|
+
def high:int (buffer_get data pos)
|
|
147
|
+
def low:int (buffer_get data (pos + 1))
|
|
148
|
+
return ((high * 256) + low)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn parseSOF:void (pos:int length:int) {
|
|
152
|
+
; Start of Frame - image dimensions and components
|
|
153
|
+
precision = (buffer_get data pos)
|
|
154
|
+
height = (this.readUint16BE(pos + 1))
|
|
155
|
+
width = (this.readUint16BE(pos + 3))
|
|
156
|
+
numComponents = (buffer_get data (pos + 5))
|
|
157
|
+
|
|
158
|
+
this.say((" Image: " + (to_string width) + "x" + (to_string height) + ", " + (to_string numComponents) + " components"))
|
|
159
|
+
|
|
160
|
+
clear components
|
|
161
|
+
maxHSamp = 1
|
|
162
|
+
maxVSamp = 1
|
|
163
|
+
|
|
164
|
+
def i 0
|
|
165
|
+
def offset:int (pos + 6)
|
|
166
|
+
while (i < numComponents) {
|
|
167
|
+
def comp (new JPEGComponent())
|
|
168
|
+
comp.id = (buffer_get data offset)
|
|
169
|
+
def sampling:int (buffer_get data (offset + 1))
|
|
170
|
+
comp.hSamp = (bit_shr sampling 4)
|
|
171
|
+
comp.vSamp = (bit_and sampling 15)
|
|
172
|
+
comp.quantTableId = (buffer_get data (offset + 2))
|
|
173
|
+
|
|
174
|
+
if (comp.hSamp > maxHSamp) { maxHSamp = comp.hSamp }
|
|
175
|
+
if (comp.vSamp > maxVSamp) { maxVSamp = comp.vSamp }
|
|
176
|
+
|
|
177
|
+
push components comp
|
|
178
|
+
this.say((" Component " + (to_string comp.id) + ": " + (to_string comp.hSamp) + "x" + (to_string comp.vSamp) + " sampling, quant table " + (to_string comp.quantTableId)))
|
|
179
|
+
|
|
180
|
+
offset = offset + 3
|
|
181
|
+
i = i + 1
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
; Calculate MCU dimensions
|
|
185
|
+
mcuWidth = maxHSamp * 8
|
|
186
|
+
mcuHeight = maxVSamp * 8
|
|
187
|
+
|
|
188
|
+
; Calculate MCUs needed (ceiling division)
|
|
189
|
+
; mcusPerRow = ceil(width / mcuWidth)
|
|
190
|
+
mcusPerRow = (to_int ((width + mcuWidth - 1) / mcuWidth))
|
|
191
|
+
mcusPerCol = (to_int ((height + mcuHeight - 1) / mcuHeight))
|
|
192
|
+
|
|
193
|
+
this.say((" MCU size: " + (to_string mcuWidth) + "x" + (to_string mcuHeight) + ", grid: " + (to_string mcusPerRow) + "x" + (to_string mcusPerCol)))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
fn parseDQT:void (pos:int length:int) {
|
|
197
|
+
; Define Quantization Table
|
|
198
|
+
def endPos:int (pos + length)
|
|
199
|
+
|
|
200
|
+
while (pos < endPos) {
|
|
201
|
+
def info:int (buffer_get data pos)
|
|
202
|
+
pos = pos + 1
|
|
203
|
+
|
|
204
|
+
def precision:int (bit_shr info 4) ; 0 = 8-bit, 1 = 16-bit
|
|
205
|
+
def tableId:int (bit_and info 15)
|
|
206
|
+
|
|
207
|
+
def table:QuantizationTable (at quantTables tableId)
|
|
208
|
+
table.id = tableId
|
|
209
|
+
clear table.values
|
|
210
|
+
|
|
211
|
+
def i 0
|
|
212
|
+
while (i < 64) {
|
|
213
|
+
if (precision == 0) {
|
|
214
|
+
push table.values (buffer_get data pos)
|
|
215
|
+
pos = pos + 1
|
|
216
|
+
} {
|
|
217
|
+
push table.values (this.readUint16BE(pos))
|
|
218
|
+
pos = pos + 2
|
|
219
|
+
}
|
|
220
|
+
i = i + 1
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
this.say((" Quantization table " + (to_string tableId) + " (" + (to_string (precision + 1)) + "-byte values)"))
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
fn parseSOS:void (pos:int length:int) {
|
|
228
|
+
; Start of Scan
|
|
229
|
+
def numScanComponents:int (buffer_get data pos)
|
|
230
|
+
pos = pos + 1
|
|
231
|
+
|
|
232
|
+
def i 0
|
|
233
|
+
while (i < numScanComponents) {
|
|
234
|
+
def compId:int (buffer_get data pos)
|
|
235
|
+
def tableSelect:int (buffer_get data (pos + 1))
|
|
236
|
+
pos = pos + 2
|
|
237
|
+
|
|
238
|
+
; Find matching component and set table IDs
|
|
239
|
+
def j 0
|
|
240
|
+
while (j < numComponents) {
|
|
241
|
+
def comp:JPEGComponent (at components j)
|
|
242
|
+
if (comp.id == compId) {
|
|
243
|
+
comp.dcTableId = (bit_shr tableSelect 4)
|
|
244
|
+
comp.acTableId = (bit_and tableSelect 15)
|
|
245
|
+
this.say((" Component " + (to_string compId) + ": DC table " + (to_string comp.dcTableId) + ", AC table " + (to_string comp.acTableId)))
|
|
246
|
+
}
|
|
247
|
+
j = j + 1
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
i = i + 1
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
; Skip spectral selection and successive approximation (baseline ignores these)
|
|
254
|
+
pos = pos + 3
|
|
255
|
+
|
|
256
|
+
; Scan data starts here
|
|
257
|
+
scanDataStart = pos
|
|
258
|
+
|
|
259
|
+
; Find end of scan data (next marker or EOI)
|
|
260
|
+
; Note: RST markers (FFD0-FFD7) are part of scan data and should not stop the search
|
|
261
|
+
def searchPos:int pos
|
|
262
|
+
while (searchPos < (dataLen - 1)) {
|
|
263
|
+
def b:int (buffer_get data searchPos)
|
|
264
|
+
if (b == 255) {
|
|
265
|
+
def nextB:int (buffer_get data (searchPos + 1))
|
|
266
|
+
if ((nextB != 0) && (nextB != 255)) {
|
|
267
|
+
; Check if it's a RST marker (D0-D7 = 208-215)
|
|
268
|
+
if ((nextB >= 208) && (nextB <= 215)) {
|
|
269
|
+
; RST marker - skip it and continue searching
|
|
270
|
+
searchPos = searchPos + 2
|
|
271
|
+
continue
|
|
272
|
+
}
|
|
273
|
+
; Found a non-RST marker - end of scan data
|
|
274
|
+
scanDataLen = searchPos - scanDataStart
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
searchPos = searchPos + 1
|
|
279
|
+
}
|
|
280
|
+
scanDataLen = dataLen - scanDataStart
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fn parseMarkers:boolean () {
|
|
284
|
+
; Parse all JPEG markers
|
|
285
|
+
def pos 0
|
|
286
|
+
|
|
287
|
+
; Check SOI
|
|
288
|
+
if (dataLen < 2) {
|
|
289
|
+
this.say("Error: File too small")
|
|
290
|
+
return false
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
def m1:int (buffer_get data 0)
|
|
294
|
+
def m2:int (buffer_get data 1)
|
|
295
|
+
if ((m1 != 255) || (m2 != 216)) {
|
|
296
|
+
this.say("Error: Not a JPEG file (missing SOI)")
|
|
297
|
+
return false
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pos = 2
|
|
301
|
+
this.say("Parsing JPEG markers...")
|
|
302
|
+
|
|
303
|
+
while (pos < (dataLen - 1)) {
|
|
304
|
+
def marker1:int (buffer_get data pos)
|
|
305
|
+
if (marker1 != 255) {
|
|
306
|
+
pos = pos + 1
|
|
307
|
+
continue
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
def marker2:int (buffer_get data (pos + 1))
|
|
311
|
+
|
|
312
|
+
; Skip padding FF bytes
|
|
313
|
+
if (marker2 == 255) {
|
|
314
|
+
pos = pos + 1
|
|
315
|
+
continue
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (marker2 == 0) {
|
|
319
|
+
; FF00 = escaped FF in data
|
|
320
|
+
pos = pos + 2
|
|
321
|
+
continue
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
; Check for markers without length
|
|
325
|
+
if (marker2 == 216) {
|
|
326
|
+
; SOI
|
|
327
|
+
pos = pos + 2
|
|
328
|
+
continue
|
|
329
|
+
}
|
|
330
|
+
if (marker2 == 217) {
|
|
331
|
+
; EOI
|
|
332
|
+
this.say(" End of Image")
|
|
333
|
+
return true
|
|
334
|
+
}
|
|
335
|
+
if ((marker2 >= 208) && (marker2 <= 215)) {
|
|
336
|
+
; RST markers
|
|
337
|
+
pos = pos + 2
|
|
338
|
+
continue
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
; Read marker length
|
|
342
|
+
if ((pos + 4) > dataLen) {
|
|
343
|
+
return true
|
|
344
|
+
}
|
|
345
|
+
def markerLen:int (this.readUint16BE(pos + 2))
|
|
346
|
+
def dataStart:int (pos + 4)
|
|
347
|
+
def markerDataLen:int (markerLen - 2)
|
|
348
|
+
|
|
349
|
+
; Process marker
|
|
350
|
+
if (marker2 == 192) {
|
|
351
|
+
this.say(" SOF0 (Baseline DCT)")
|
|
352
|
+
this.parseSOF(dataStart markerDataLen)
|
|
353
|
+
}
|
|
354
|
+
if (marker2 == 193) {
|
|
355
|
+
this.say(" SOF1 (Extended Sequential DCT)")
|
|
356
|
+
this.parseSOF(dataStart markerDataLen)
|
|
357
|
+
}
|
|
358
|
+
if (marker2 == 194) {
|
|
359
|
+
this.say(" SOF2 (Progressive DCT) - NOT SUPPORTED")
|
|
360
|
+
return false
|
|
361
|
+
}
|
|
362
|
+
if (marker2 == 196) {
|
|
363
|
+
this.say(" DHT (Huffman Tables)")
|
|
364
|
+
huffman.parseDHT(data dataStart markerDataLen)
|
|
365
|
+
}
|
|
366
|
+
if (marker2 == 219) {
|
|
367
|
+
this.say(" DQT (Quantization Tables)")
|
|
368
|
+
this.parseDQT(dataStart markerDataLen)
|
|
369
|
+
}
|
|
370
|
+
if (marker2 == 221) {
|
|
371
|
+
; DRI - Define Restart Interval
|
|
372
|
+
restartInterval = (this.readUint16BE(dataStart))
|
|
373
|
+
this.say((" DRI (Restart Interval: " + (to_string restartInterval) + ")"))
|
|
374
|
+
}
|
|
375
|
+
if (marker2 == 218) {
|
|
376
|
+
this.say(" SOS (Start of Scan)")
|
|
377
|
+
this.parseSOS(dataStart markerDataLen)
|
|
378
|
+
; After SOS, entropy-coded data follows
|
|
379
|
+
; Skip to end of scan data
|
|
380
|
+
pos = scanDataStart + scanDataLen
|
|
381
|
+
continue
|
|
382
|
+
}
|
|
383
|
+
if (marker2 == 224) {
|
|
384
|
+
this.say(" APP0 (JFIF)")
|
|
385
|
+
}
|
|
386
|
+
if (marker2 == 225) {
|
|
387
|
+
this.say(" APP1 (EXIF)")
|
|
388
|
+
}
|
|
389
|
+
if (marker2 == 254) {
|
|
390
|
+
this.say(" COM (Comment)")
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
pos = pos + 2 + markerLen
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return true
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
fn decodeBlock:int_buffer (reader:BitReader comp:JPEGComponent quantTable:QuantizationTable) {
|
|
400
|
+
; Decode one 8x8 block
|
|
401
|
+
; Returns 64 dequantized DCT coefficients in zigzag order
|
|
402
|
+
|
|
403
|
+
def coeffs:int_buffer (int_buffer_alloc 64)
|
|
404
|
+
int_buffer_fill coeffs 0 0 64
|
|
405
|
+
|
|
406
|
+
; Decode DC coefficient
|
|
407
|
+
def dcTable:HuffmanTable (huffman.getDCTable(comp.dcTableId))
|
|
408
|
+
def dcCategory:int (dcTable.decode(reader))
|
|
409
|
+
def dcDiff:int (reader.receiveExtend(dcCategory))
|
|
410
|
+
def dcValue:int (comp.prevDC + dcDiff)
|
|
411
|
+
comp.prevDC = dcValue
|
|
412
|
+
|
|
413
|
+
; Dequantize DC
|
|
414
|
+
def dcQuant:int (at quantTable.values 0)
|
|
415
|
+
int_buffer_set coeffs 0 (dcValue * dcQuant)
|
|
416
|
+
|
|
417
|
+
; Decode AC coefficients
|
|
418
|
+
def acTable:HuffmanTable (huffman.getACTable(comp.acTableId))
|
|
419
|
+
def k:int 1
|
|
420
|
+
|
|
421
|
+
while (k < 64) {
|
|
422
|
+
def acSymbol:int (acTable.decode(reader))
|
|
423
|
+
|
|
424
|
+
if (acSymbol == 0) {
|
|
425
|
+
; EOB - all remaining coefficients are zero
|
|
426
|
+
k = 64
|
|
427
|
+
} {
|
|
428
|
+
def runLength:int (bit_shr acSymbol 4)
|
|
429
|
+
def acCategory:int (bit_and acSymbol 15)
|
|
430
|
+
|
|
431
|
+
if (acSymbol == 240) {
|
|
432
|
+
; ZRL - skip 16 zeros
|
|
433
|
+
k = k + 16
|
|
434
|
+
} {
|
|
435
|
+
; Skip run of zeros
|
|
436
|
+
k = k + runLength
|
|
437
|
+
|
|
438
|
+
if (k < 64) {
|
|
439
|
+
; Decode AC value
|
|
440
|
+
def acValue:int (reader.receiveExtend(acCategory))
|
|
441
|
+
|
|
442
|
+
; Dequantize
|
|
443
|
+
def acQuant:int (at quantTable.values k)
|
|
444
|
+
int_buffer_set coeffs k (acValue * acQuant)
|
|
445
|
+
|
|
446
|
+
k = k + 1
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return coeffs
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
fn decode:ImageBuffer (dirPath:string fileName:string) {
|
|
455
|
+
def bytes:buffer (buffer_read_file dirPath fileName)
|
|
456
|
+
return (this.decodeBytes(bytes))
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
; decode a JPEG already held in memory (e.g. a glTF embedded image slice).
|
|
460
|
+
fn decodeBytes:ImageBuffer (bytes:buffer) {
|
|
461
|
+
; Main decode function
|
|
462
|
+
; Reset all state before decoding new image
|
|
463
|
+
this.reset()
|
|
464
|
+
; The table builder narrates too. Passed here rather than in the
|
|
465
|
+
; constructor, because a caller sets `quiet` on the decoder AFTER
|
|
466
|
+
; constructing it - which is the only moment it could.
|
|
467
|
+
huffman.quiet = quiet
|
|
468
|
+
|
|
469
|
+
data = bytes
|
|
470
|
+
dataLen = (buffer_length data)
|
|
471
|
+
|
|
472
|
+
this.say(("Decoding JPEG in-memory (" + (to_string dataLen) + " bytes)"))
|
|
473
|
+
|
|
474
|
+
; Parse markers
|
|
475
|
+
def ok:boolean (this.parseMarkers())
|
|
476
|
+
if (ok == false) {
|
|
477
|
+
this.say("Error parsing JPEG markers")
|
|
478
|
+
def errImg (new ImageBuffer())
|
|
479
|
+
errImg.init(1 1)
|
|
480
|
+
return errImg
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if ((width == 0) || (height == 0)) {
|
|
484
|
+
this.say("Error: Invalid image dimensions")
|
|
485
|
+
def errImg (new ImageBuffer())
|
|
486
|
+
errImg.init(1 1)
|
|
487
|
+
return errImg
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
this.say(("Decoding " + (to_string scanDataLen) + " bytes of scan data..."))
|
|
491
|
+
|
|
492
|
+
; Create output image
|
|
493
|
+
def img (new ImageBuffer())
|
|
494
|
+
img.init(width height)
|
|
495
|
+
|
|
496
|
+
; Create bit reader for scan data
|
|
497
|
+
def reader (new BitReader())
|
|
498
|
+
reader.init(data scanDataStart scanDataLen)
|
|
499
|
+
|
|
500
|
+
; Reset DC predictors
|
|
501
|
+
def c 0
|
|
502
|
+
while (c < numComponents) {
|
|
503
|
+
def comp:JPEGComponent (at components c)
|
|
504
|
+
comp.prevDC = 0
|
|
505
|
+
c = c + 1
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
; Component block buffers - use flat arrays, 64 values per block
|
|
509
|
+
; For 2x2 subsampling max, we need 4 Y blocks = 256 values
|
|
510
|
+
def yBlocksData:[int] ; Up to 4 Y blocks * 64 = 256 values
|
|
511
|
+
def yBlockCount:int 0
|
|
512
|
+
def cbBlock:[int]
|
|
513
|
+
def crBlock:[int]
|
|
514
|
+
|
|
515
|
+
; MCU counter for restart interval
|
|
516
|
+
def mcuCount:int 0
|
|
517
|
+
|
|
518
|
+
; Decode MCU by MCU
|
|
519
|
+
def mcuY 0
|
|
520
|
+
while (mcuY < mcusPerCol) {
|
|
521
|
+
def mcuX 0
|
|
522
|
+
while (mcuX < mcusPerRow) {
|
|
523
|
+
; Check for restart interval - reset DC predictions
|
|
524
|
+
if ((restartInterval > 0) && (mcuCount > 0) && ((mcuCount % restartInterval) == 0)) {
|
|
525
|
+
; Reset DC predictions after restart marker
|
|
526
|
+
c = 0
|
|
527
|
+
while (c < numComponents) {
|
|
528
|
+
def compRst:JPEGComponent (at components c)
|
|
529
|
+
compRst.prevDC = 0
|
|
530
|
+
c = c + 1
|
|
531
|
+
}
|
|
532
|
+
; Align bit reader to byte boundary and skip RST marker
|
|
533
|
+
reader.alignToByte()
|
|
534
|
+
reader.skipRestartMarker()
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
; Decode all blocks in this MCU
|
|
538
|
+
clear yBlocksData
|
|
539
|
+
yBlockCount = 0
|
|
540
|
+
|
|
541
|
+
; For each component
|
|
542
|
+
def compIdx 0
|
|
543
|
+
while (compIdx < numComponents) {
|
|
544
|
+
def comp:JPEGComponent (at components compIdx)
|
|
545
|
+
def quantTable:QuantizationTable (at quantTables comp.quantTableId)
|
|
546
|
+
|
|
547
|
+
; Decode blocks for this component
|
|
548
|
+
def blockV 0
|
|
549
|
+
while (blockV < comp.vSamp) {
|
|
550
|
+
def blockH 0
|
|
551
|
+
while (blockH < comp.hSamp) {
|
|
552
|
+
; Decode block
|
|
553
|
+
def coeffs:int_buffer (this.decodeBlock(reader comp quantTable))
|
|
554
|
+
|
|
555
|
+
; IDCT to get pixels
|
|
556
|
+
def blockPixels:int_buffer (int_buffer_alloc 64)
|
|
557
|
+
int_buffer_fill blockPixels 0 0 64
|
|
558
|
+
|
|
559
|
+
; Dezigzag and transform
|
|
560
|
+
def tempBlock:int_buffer (idct.dezigzag(coeffs))
|
|
561
|
+
idct.transform(tempBlock blockPixels)
|
|
562
|
+
|
|
563
|
+
; Store block
|
|
564
|
+
if (compIdx == 0) {
|
|
565
|
+
; Y component - append to flat array
|
|
566
|
+
def bi 0
|
|
567
|
+
while (bi < 64) {
|
|
568
|
+
push yBlocksData (int_buffer_get blockPixels bi)
|
|
569
|
+
bi = bi + 1
|
|
570
|
+
}
|
|
571
|
+
yBlockCount = yBlockCount + 1
|
|
572
|
+
}
|
|
573
|
+
if (compIdx == 1) {
|
|
574
|
+
; Cb component
|
|
575
|
+
clear cbBlock
|
|
576
|
+
def bi 0
|
|
577
|
+
while (bi < 64) {
|
|
578
|
+
push cbBlock (int_buffer_get blockPixels bi)
|
|
579
|
+
bi = bi + 1
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (compIdx == 2) {
|
|
583
|
+
; Cr component
|
|
584
|
+
clear crBlock
|
|
585
|
+
def bi 0
|
|
586
|
+
while (bi < 64) {
|
|
587
|
+
push crBlock (int_buffer_get blockPixels bi)
|
|
588
|
+
bi = bi + 1
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
blockH = blockH + 1
|
|
593
|
+
}
|
|
594
|
+
blockV = blockV + 1
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
compIdx = compIdx + 1
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
; Convert MCU to RGB and write to image
|
|
601
|
+
this.writeMCU(img mcuX mcuY yBlocksData yBlockCount cbBlock crBlock)
|
|
602
|
+
|
|
603
|
+
mcuX = mcuX + 1
|
|
604
|
+
mcuCount = mcuCount + 1
|
|
605
|
+
}
|
|
606
|
+
mcuY = mcuY + 1
|
|
607
|
+
|
|
608
|
+
; Progress indicator
|
|
609
|
+
if ((mcuY % 10) == 0) {
|
|
610
|
+
this.say((" Row " + (to_string mcuY) + "/" + (to_string mcusPerCol)))
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
this.say("Decode complete!")
|
|
615
|
+
return img
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
fn writeMCU:void (img:ImageBuffer mcuX:int mcuY:int yBlocksData:[int] yBlockCount:int cbBlock:[int] crBlock:[int]) {
|
|
619
|
+
; Write decoded MCU to image, handling YCbCr to RGB conversion
|
|
620
|
+
; yBlocksData is a flat array: block0[64], block1[64], ...
|
|
621
|
+
|
|
622
|
+
def baseX:int (mcuX * mcuWidth)
|
|
623
|
+
def baseY:int (mcuY * mcuHeight)
|
|
624
|
+
|
|
625
|
+
; Get Y component from first component for dimensions
|
|
626
|
+
def comp0:JPEGComponent (at components 0)
|
|
627
|
+
|
|
628
|
+
; Simple case: 1x1 sampling (no subsampling)
|
|
629
|
+
if ((maxHSamp == 1) && (maxVSamp == 1)) {
|
|
630
|
+
def py 0
|
|
631
|
+
while (py < 8) {
|
|
632
|
+
def px 0
|
|
633
|
+
while (px < 8) {
|
|
634
|
+
def imgX:int (baseX + px)
|
|
635
|
+
def imgY:int (baseY + py)
|
|
636
|
+
|
|
637
|
+
if ((imgX < width) && (imgY < height)) {
|
|
638
|
+
def idx:int ((py * 8) + px)
|
|
639
|
+
def y:int (at yBlocksData idx)
|
|
640
|
+
|
|
641
|
+
def cb:int 128
|
|
642
|
+
def cr:int 128
|
|
643
|
+
if (numComponents >= 3) {
|
|
644
|
+
cb = (at cbBlock idx)
|
|
645
|
+
cr = (at crBlock idx)
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
; YCbCr to RGB
|
|
649
|
+
def r:int (y + (bit_shr (359 * (cr - 128)) 8))
|
|
650
|
+
def g:int (y - (bit_shr (88 * (cb - 128)) 8) - (bit_shr (183 * (cr - 128)) 8))
|
|
651
|
+
def b:int (y + (bit_shr (454 * (cb - 128)) 8))
|
|
652
|
+
|
|
653
|
+
if (r < 0) { r = 0 }
|
|
654
|
+
if (r > 255) { r = 255 }
|
|
655
|
+
if (g < 0) { g = 0 }
|
|
656
|
+
if (g > 255) { g = 255 }
|
|
657
|
+
if (b < 0) { b = 0 }
|
|
658
|
+
if (b > 255) { b = 255 }
|
|
659
|
+
|
|
660
|
+
img.setPixelRGB(imgX imgY r g b)
|
|
661
|
+
}
|
|
662
|
+
px = px + 1
|
|
663
|
+
}
|
|
664
|
+
py = py + 1
|
|
665
|
+
}
|
|
666
|
+
return
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
; 2x2 subsampling (4:2:0) - most common
|
|
670
|
+
if ((maxHSamp == 2) && (maxVSamp == 2)) {
|
|
671
|
+
; 4 Y blocks, 1 Cb, 1 Cr
|
|
672
|
+
; Y blocks are stored sequentially: block0 at offset 0, block1 at 64, etc.
|
|
673
|
+
def blockIdx 0
|
|
674
|
+
def blockY 0
|
|
675
|
+
while (blockY < 2) {
|
|
676
|
+
def blockX 0
|
|
677
|
+
while (blockX < 2) {
|
|
678
|
+
def yBlockOffset:int (blockIdx * 64)
|
|
679
|
+
|
|
680
|
+
def py 0
|
|
681
|
+
while (py < 8) {
|
|
682
|
+
def px 0
|
|
683
|
+
while (px < 8) {
|
|
684
|
+
def imgX:int (baseX + (blockX * 8) + px)
|
|
685
|
+
def imgY:int (baseY + (blockY * 8) + py)
|
|
686
|
+
|
|
687
|
+
if ((imgX < width) && (imgY < height)) {
|
|
688
|
+
def yIdx:int (yBlockOffset + (py * 8) + px)
|
|
689
|
+
def y:int (at yBlocksData yIdx)
|
|
690
|
+
|
|
691
|
+
; Cb/Cr are subsampled - calculate index
|
|
692
|
+
def chromaX:int ((blockX * 4) + (bit_shr px 1))
|
|
693
|
+
def chromaY:int ((blockY * 4) + (bit_shr py 1))
|
|
694
|
+
def chromaIdx:int ((chromaY * 8) + chromaX)
|
|
695
|
+
|
|
696
|
+
def cb:int 128
|
|
697
|
+
def cr:int 128
|
|
698
|
+
if (numComponents >= 3) {
|
|
699
|
+
cb = (at cbBlock chromaIdx)
|
|
700
|
+
cr = (at crBlock chromaIdx)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
; YCbCr to RGB
|
|
704
|
+
def r:int (y + (bit_shr (359 * (cr - 128)) 8))
|
|
705
|
+
def g:int (y - (bit_shr (88 * (cb - 128)) 8) - (bit_shr (183 * (cr - 128)) 8))
|
|
706
|
+
def b:int (y + (bit_shr (454 * (cb - 128)) 8))
|
|
707
|
+
|
|
708
|
+
if (r < 0) { r = 0 }
|
|
709
|
+
if (r > 255) { r = 255 }
|
|
710
|
+
if (g < 0) { g = 0 }
|
|
711
|
+
if (g > 255) { g = 255 }
|
|
712
|
+
if (b < 0) { b = 0 }
|
|
713
|
+
if (b > 255) { b = 255 }
|
|
714
|
+
|
|
715
|
+
img.setPixelRGB(imgX imgY r g b)
|
|
716
|
+
}
|
|
717
|
+
px = px + 1
|
|
718
|
+
}
|
|
719
|
+
py = py + 1
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
blockIdx = blockIdx + 1
|
|
723
|
+
blockX = blockX + 1
|
|
724
|
+
}
|
|
725
|
+
blockY = blockY + 1
|
|
726
|
+
}
|
|
727
|
+
return
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
; 2x1 subsampling (4:2:2) - horizontal subsampling only
|
|
731
|
+
if ((maxHSamp == 2) && (maxVSamp == 1)) {
|
|
732
|
+
; 2 Y blocks side by side, 1 Cb, 1 Cr
|
|
733
|
+
; MCU is 16x8 pixels
|
|
734
|
+
; Y block 0 covers pixels 0-7 horizontally
|
|
735
|
+
; Y block 1 covers pixels 8-15 horizontally
|
|
736
|
+
; Cb/Cr are subsampled 2:1 horizontally (each chroma sample covers 2 Y samples)
|
|
737
|
+
|
|
738
|
+
def blockX 0
|
|
739
|
+
while (blockX < 2) {
|
|
740
|
+
def yBlockOffset:int (blockX * 64)
|
|
741
|
+
|
|
742
|
+
def py 0
|
|
743
|
+
while (py < 8) {
|
|
744
|
+
def px 0
|
|
745
|
+
while (px < 8) {
|
|
746
|
+
def imgX:int (baseX + (blockX * 8) + px)
|
|
747
|
+
def imgY:int (baseY + py)
|
|
748
|
+
|
|
749
|
+
if ((imgX < width) && (imgY < height)) {
|
|
750
|
+
def yIdx:int (yBlockOffset + (py * 8) + px)
|
|
751
|
+
def y:int (at yBlocksData yIdx)
|
|
752
|
+
|
|
753
|
+
; Cb/Cr are subsampled horizontally only
|
|
754
|
+
; chromaX = (blockX * 4) + (px / 2), maps 16 px to 8
|
|
755
|
+
; chromaY = py (no vertical subsampling)
|
|
756
|
+
def chromaX:int ((blockX * 4) + (bit_shr px 1))
|
|
757
|
+
def chromaY:int py
|
|
758
|
+
def chromaIdx:int ((chromaY * 8) + chromaX)
|
|
759
|
+
|
|
760
|
+
def cb:int 128
|
|
761
|
+
def cr:int 128
|
|
762
|
+
if (numComponents >= 3) {
|
|
763
|
+
cb = (at cbBlock chromaIdx)
|
|
764
|
+
cr = (at crBlock chromaIdx)
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
; YCbCr to RGB
|
|
768
|
+
def r:int (y + (bit_shr (359 * (cr - 128)) 8))
|
|
769
|
+
def g:int (y - (bit_shr (88 * (cb - 128)) 8) - (bit_shr (183 * (cr - 128)) 8))
|
|
770
|
+
def b:int (y + (bit_shr (454 * (cb - 128)) 8))
|
|
771
|
+
|
|
772
|
+
if (r < 0) { r = 0 }
|
|
773
|
+
if (r > 255) { r = 255 }
|
|
774
|
+
if (g < 0) { g = 0 }
|
|
775
|
+
if (g > 255) { g = 255 }
|
|
776
|
+
if (b < 0) { b = 0 }
|
|
777
|
+
if (b > 255) { b = 255 }
|
|
778
|
+
|
|
779
|
+
img.setPixelRGB(imgX imgY r g b)
|
|
780
|
+
}
|
|
781
|
+
px = px + 1
|
|
782
|
+
}
|
|
783
|
+
py = py + 1
|
|
784
|
+
}
|
|
785
|
+
blockX = blockX + 1
|
|
786
|
+
}
|
|
787
|
+
return
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
; Fallback: treat as grayscale
|
|
791
|
+
if (yBlockCount > 0) {
|
|
792
|
+
def py 0
|
|
793
|
+
while (py < 8) {
|
|
794
|
+
def px 0
|
|
795
|
+
while (px < 8) {
|
|
796
|
+
def imgX:int (baseX + px)
|
|
797
|
+
def imgY:int (baseY + py)
|
|
798
|
+
if ((imgX < width) && (imgY < height)) {
|
|
799
|
+
def y:int (at yBlocksData ((py * 8) + px))
|
|
800
|
+
img.setPixelRGB(imgX imgY y y y)
|
|
801
|
+
}
|
|
802
|
+
px = px + 1
|
|
803
|
+
}
|
|
804
|
+
py = py + 1
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|