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.
Files changed (338) hide show
  1. package/CHANGELOG.md +1160 -19
  2. package/LICENSE +3 -1
  3. package/README.md +124 -29
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79924 -56922
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,447 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; ==============================================================================
4
+ ; EVGHostTreeTest — does the fifth list say only what changed?
5
+ ; ==============================================================================
6
+ ;
7
+ ; `EVGHostTree` describes a laid-out tree to a host that keeps nodes, as ops
8
+ ; against what it said last time. What has to be true of it, and is asked here:
9
+ ;
10
+ ; * the first build CREATES every node, parents before children, at the
11
+ ; inspector's paths, and every rectangle it reports is a rectangle the
12
+ ; display list drew for the same element;
13
+ ; * a build over a tree that did not change emits NOTHING;
14
+ ; * a colour changes: one UPDATE, PAINT alone; a height changes: the rows
15
+ ; below it get GEOMETRY alone;
16
+ ; * a keyed reorder through `EVGReconcile` MOVES nodes and remakes none;
17
+ ; * a row taken out is one REMOVE; a row added is one CREATE;
18
+ ; * a scroll is one SCROLL bit on the container and no op on its children,
19
+ ; because their geometry is parent-relative at scroll 0;
20
+ ; * text that changes is a TEXT bit, and the lines are the engine's lines;
21
+ ; * the JSON carries what a DOM host reads.
22
+ ;
23
+ ; npm run evg:hosttree:test
24
+ ; ==============================================================================
25
+
26
+ Import "EVGElement.rgr"
27
+ Import "EVGUnit.rgr"
28
+ Import "EVGStyleSheet.rgr"
29
+ Import "EVGLayout.rgr"
30
+ Import "EVGDisplayList.rgr"
31
+ Import "EVGReconcile.rgr"
32
+ Import "EVGHostTree.rgr"
33
+
34
+ class EVGHostTreeTest {
35
+ def passed:int 0
36
+ def failed:int 0
37
+ def sheet:EVGStyleSheet (new EVGStyleSheet())
38
+
39
+ sfn m@(main):void () {
40
+ def t (new EVGHostTreeTest())
41
+ t.run()
42
+ }
43
+
44
+ fn ok:void (what:string cond:boolean) {
45
+ if cond {
46
+ passed = (passed + 1)
47
+ print (" PASS " + what)
48
+ } {
49
+ failed = (failed + 1)
50
+ print (" FAIL " + what)
51
+ }
52
+ }
53
+
54
+ fn eq:void (what:string got:int want:int) {
55
+ this.ok((what + " (" + (to_string got) + " vs " + (to_string want) + ")") (got == want))
56
+ }
57
+
58
+ ; --- the fixture --------------------------------------------------------------
59
+ ; A column of keyed rows with text, and a scroll box with tall content.
60
+
61
+ sfn css:string () {
62
+ return (""
63
+ + ".page { display: flex; flex-direction: column; width: 300px; height: 400px; gap: 4px; padding: 10px; background-color: #ffffff; }"
64
+ + ".row { width: 200px; height: 24px; font-size: 12px; color: #111111; border-radius: 4px; }"
65
+ + ".row-a { background-color: #ff0000; }"
66
+ + ".row-b { background-color: #00ff00; }"
67
+ + ".row-c { background-color: #0000ff; }"
68
+ + ".row-d { background-color: #ffff00; }"
69
+ + ".scroller { width: 200px; height: 60px; overflow: hidden; background-color: #eeeeee; }"
70
+ + ".tall { width: 180px; height: 300px; background-color: #cccccc; }")
71
+ }
72
+
73
+ sfn row:EVGElement (name:string) {
74
+ def el (EVGElement.createDiv())
75
+ el.className = ("row row-" + name)
76
+ el.id = ("row-" + name)
77
+ el.key = name
78
+ el.role = "listitem"
79
+ el.textContent = ("Row " + name)
80
+ return el
81
+ }
82
+
83
+ sfn page:EVGElement (names:string) {
84
+ def el (EVGElement.createDiv())
85
+ el.className = "page"
86
+ el.id = "page"
87
+ def i:int 0
88
+ while (i < (strlen names)) {
89
+ el.addChild((EVGHostTreeTest.row((substring names i (i + 1)))))
90
+ i = (i + 1)
91
+ }
92
+ def sc (EVGElement.createDiv())
93
+ sc.className = "scroller"
94
+ sc.id = "scroller"
95
+ sc.key = "scroller"
96
+ def tall (EVGElement.createDiv())
97
+ tall.className = "tall"
98
+ tall.id = "tall"
99
+ sc.addChild(tall)
100
+ el.addChild(sc)
101
+ return el
102
+ }
103
+
104
+ fn layout:EVGLayout (root:EVGElement) {
105
+ sheet.setViewport(300.0 400.0 false)
106
+ sheet.applyTree(root "")
107
+ def lay (new EVGLayout())
108
+ lay.setPageSize(300.0 400.0)
109
+ lay.layout(root)
110
+ return lay
111
+ }
112
+
113
+ fn opsOfKind:int (t:EVGHostTree kind:int) {
114
+ def n:int 0
115
+ def i:int 0
116
+ while (i < (t.opCount())) {
117
+ if (((t.opAt(i)).kind) == kind) {
118
+ n = (n + 1)
119
+ }
120
+ i = (i + 1)
121
+ }
122
+ return n
123
+ }
124
+
125
+ fn opFor@(optional):EVGHostOp (t:EVGHostTree path:string) {
126
+ def i:int 0
127
+ while (i < (t.opCount())) {
128
+ def op:EVGHostOp (t.opAt(i))
129
+ if (op.path == path) {
130
+ def found@(optional):EVGHostOp op
131
+ return found
132
+ }
133
+ i = (i + 1)
134
+ }
135
+ def none@(optional):EVGHostOp
136
+ return none
137
+ }
138
+
139
+ fn run:void () {
140
+ print "=== EVGHostTree ==="
141
+ sheet.parse((EVGHostTreeTest.css()))
142
+ this.testFirstBuild()
143
+ this.testNothingChanged()
144
+ this.testPaintAlone()
145
+ this.testGeometryBelow()
146
+ this.testKeyedReorder()
147
+ this.testRemoveAndAdd()
148
+ this.testAnotherElementAtTheAddress()
149
+ this.testScrollIsOneBit()
150
+ this.testText()
151
+ this.testJson()
152
+ print ""
153
+ print ("passed = " + (to_string passed) + " failed = " + (to_string failed))
154
+ if (failed > 0) {
155
+ print "FAILURES"
156
+ } {
157
+ print "ALL PASS"
158
+ }
159
+ }
160
+
161
+ fn testFirstBuild:void () {
162
+ print "-- the first build creates everything, at the inspector's paths"
163
+ def root:EVGElement (EVGHostTreeTest.page("abc"))
164
+ def lay:EVGLayout (this.layout(root))
165
+ def t (new EVGHostTree())
166
+ t.setTextEngine((lay.getTextEngine()))
167
+ t.build(root)
168
+ ; page + 3 rows + scroller + tall = 6
169
+ this.eq("six nodes" t.count 6)
170
+ this.eq("six creates" t.created 6)
171
+ this.eq("and nothing else" (t.opCount()) 6)
172
+ this.ok("the root is 0" (((t.opAt(0)).path) == "0"))
173
+ this.ok("a keyed row is k:" (((t.opAt(1)).path) == "0/k:a"))
174
+ this.ok("its parent is the root" (((t.opAt(1)).parentPath) == "0"))
175
+ this.ok("the tall box is under the scroller" (((t.opAt(5)).path) == "0/k:scroller/0"))
176
+ this.ok("a row is a listitem" (((t.opAt(1)).role) == "listitem"))
177
+ ; Every rectangle is one the display list drew.
178
+ def dl (new EVGDisplayList())
179
+ dl.setTextEngine((lay.getTextEngine()))
180
+ dl.build(root)
181
+ def matched:int 0
182
+ def i:int 0
183
+ while (i < (t.opCount())) {
184
+ def op:EVGHostOp (t.opAt(i))
185
+ def j:int 0
186
+ while (j < (array_length dl.cmds)) {
187
+ def c:EVGDrawCmd (itemAt dl.cmds j)
188
+ if (c.kind == 0) {
189
+ if ((c.x == op.pageX) && (c.y == op.pageY)) {
190
+ if ((c.w == op.w) && (c.h == op.h)) {
191
+ matched = (matched + 1)
192
+ j = (array_length dl.cmds)
193
+ }
194
+ }
195
+ }
196
+ j = (j + 1)
197
+ }
198
+ i = (i + 1)
199
+ }
200
+ this.eq("every node's page rectangle is a rectangle the list drew" matched 6)
201
+ ; Parent-relative: the first row sits at the page's padding.
202
+ def a:EVGHostOp (t.opAt(1))
203
+ this.ok("the first row is at the padding, inside its parent" ((a.x == 10.0) && (a.y == 10.0)))
204
+ this.ok("and on the page too, because the page is at 0" ((a.pageX == 10.0) && (a.pageY == 10.0)))
205
+ this.ok("the second row is a row and a gap below" (((t.opAt(2)).y) == 38.0))
206
+ }
207
+
208
+ fn testNothingChanged:void () {
209
+ print "-- a tree that did not change says nothing"
210
+ def root:EVGElement (EVGHostTreeTest.page("abc"))
211
+ def lay:EVGLayout (this.layout(root))
212
+ def t (new EVGHostTree())
213
+ t.setTextEngine((lay.getTextEngine()))
214
+ t.build(root)
215
+ this.layout(root)
216
+ t.build(root)
217
+ this.eq("no ops" (t.opCount()) 0)
218
+ this.eq("still six nodes" t.count 6)
219
+ }
220
+
221
+ fn testPaintAlone:void () {
222
+ print "-- a colour is PAINT and nothing else"
223
+ def root:EVGElement (EVGHostTreeTest.page("abc"))
224
+ def lay:EVGLayout (this.layout(root))
225
+ def t (new EVGHostTree())
226
+ t.setTextEngine((lay.getTextEngine()))
227
+ t.build(root)
228
+ def b:EVGElement (root.getChild(1))
229
+ b.setAttribute("background-color" "#123456")
230
+ this.layout(root)
231
+ t.build(root)
232
+ this.eq("one op" (t.opCount()) 1)
233
+ def op:EVGHostOp (t.opAt(0))
234
+ this.ok("an update" (op.kind == 1))
235
+ this.ok("on row b" (op.path == "0/k:b"))
236
+ this.eq("PAINT alone" op.bits 2)
237
+ this.ok("with the new colour" ((op.bgR == 18) && (op.bgG == 52)))
238
+ }
239
+
240
+ fn testGeometryBelow:void () {
241
+ print "-- a height moves the rows below it: GEOMETRY alone"
242
+ def root:EVGElement (EVGHostTreeTest.page("abc"))
243
+ def lay:EVGLayout (this.layout(root))
244
+ def t (new EVGHostTree())
245
+ t.setTextEngine((lay.getTextEngine()))
246
+ t.build(root)
247
+ def a:EVGElement (root.getChild(0))
248
+ a.setAttribute("height" "40px")
249
+ this.layout(root)
250
+ t.build(root)
251
+ ; a grew; b, c and the scroller moved down; the tall box did not move
252
+ ; inside the scroller. Four ops.
253
+ this.eq("four ops" (t.opCount()) 4)
254
+ def opA@(optional):EVGHostOp (this.opFor(t "0/k:a"))
255
+ def opB@(optional):EVGHostOp (this.opFor(t "0/k:b"))
256
+ def opTall@(optional):EVGHostOp (this.opFor(t "0/k:scroller/0"))
257
+ this.ok("a has an op" (!null? opA))
258
+ this.ok("b has an op" (!null? opB))
259
+ this.ok("the tall box, inside its parent, has none" (null? opTall))
260
+ if opB {
261
+ def ob:EVGHostOp (unwrap opB)
262
+ this.eq("b: GEOMETRY alone" ob.bits 1)
263
+ this.ok("b moved down by 16" (ob.y == 54.0))
264
+ }
265
+ }
266
+
267
+ fn testKeyedReorder:void () {
268
+ print "-- a keyed reorder moves nodes and remakes none"
269
+ def live:EVGElement (EVGHostTreeTest.page("abc"))
270
+ def lay:EVGLayout (this.layout(live))
271
+ def t (new EVGHostTree())
272
+ t.setTextEngine((lay.getTextEngine()))
273
+ t.build(live)
274
+ def next:EVGElement (EVGHostTreeTest.page("cab"))
275
+ def rec (new EVGReconcile())
276
+ rec.reconcile(live next)
277
+ this.layout(live)
278
+ t.build(live)
279
+ this.eq("nothing created" t.created 0)
280
+ this.eq("nothing removed" t.removed 0)
281
+ this.ok("some moved" (t.moved > 0))
282
+ def opC@(optional):EVGHostOp (this.opFor(t "0/k:c"))
283
+ this.ok("c has ops" (!null? opC))
284
+ ; The row that moved to the top also moved on the page: a MOVE and a
285
+ ; GEOMETRY update, both on the same path.
286
+ def moves:int (this.opsOfKind(t 2))
287
+ this.ok("moves are MOVE ops" (moves == t.moved))
288
+ }
289
+
290
+ fn testRemoveAndAdd:void () {
291
+ print "-- a row taken out is one REMOVE; one put in is one CREATE"
292
+ def live:EVGElement (EVGHostTreeTest.page("abc"))
293
+ def lay:EVGLayout (this.layout(live))
294
+ def t (new EVGHostTree())
295
+ t.setTextEngine((lay.getTextEngine()))
296
+ t.build(live)
297
+ def rec (new EVGReconcile())
298
+ rec.reconcile(live (EVGHostTreeTest.page("ac")))
299
+ this.layout(live)
300
+ t.build(live)
301
+ this.eq("one removed" t.removed 1)
302
+ this.eq("none created" t.created 0)
303
+ def gone@(optional):EVGHostOp (this.opFor(t "0/k:b"))
304
+ this.ok("and it is b" (!null? gone))
305
+ if gone {
306
+ this.ok("as a REMOVE" (((unwrap gone).kind) == 3))
307
+ }
308
+ def rec2 (new EVGReconcile())
309
+ rec2.reconcile(live (EVGHostTreeTest.page("acd")))
310
+ this.layout(live)
311
+ t.build(live)
312
+ this.eq("one created" t.created 1)
313
+ this.eq("none removed" t.removed 0)
314
+ def made@(optional):EVGHostOp (this.opFor(t "0/k:d"))
315
+ this.ok("and it is d" (!null? made))
316
+ }
317
+
318
+ fn testAnotherElementAtTheAddress:void () {
319
+ print "-- another element at an address is a REMOVE and a CREATE, its children made with it"
320
+ ; Two pages whose first child sits at the same unkeyed address, `0/0`,
321
+ ; and is a different thing on each: a row on one, a scroller with a
322
+ ; child on the other. The way an app's scenes share a page.
323
+ def one:EVGElement (EVGElement.createDiv())
324
+ one.className = "page"
325
+ one.id = "page"
326
+ def r:EVGElement (EVGHostTreeTest.row("a"))
327
+ r.key = ""
328
+ one.addChild(r)
329
+ def lay:EVGLayout (this.layout(one))
330
+ def t (new EVGHostTree())
331
+ t.setTextEngine((lay.getTextEngine()))
332
+ t.build(one)
333
+ this.eq("the row page: two created" t.created 2)
334
+
335
+ def two:EVGElement (EVGElement.createDiv())
336
+ two.className = "page"
337
+ two.id = "page"
338
+ def sc (EVGElement.createDiv())
339
+ sc.className = "scroller"
340
+ sc.id = "scroller"
341
+ def tall (EVGElement.createDiv())
342
+ tall.className = "tall"
343
+ tall.id = "tall"
344
+ sc.addChild(tall)
345
+ two.addChild(sc)
346
+ this.layout(two)
347
+ t.build(two)
348
+ this.eq("the scroller page: one removed" t.removed 1)
349
+ this.eq("two created" t.created 2)
350
+ this.eq("none updated" t.updated 0)
351
+ ; The order a host needs: the old node gone before the new one is put
352
+ ; at its address, and the child after its parent.
353
+ def kinds:string ""
354
+ def i:int 0
355
+ while (i < (t.opCount())) {
356
+ def op:EVGHostOp (t.opAt(i))
357
+ if (op.path == "0/0") { kinds = (kinds + (to_string op.kind)) }
358
+ if (op.path == "0/0/0") { kinds = (kinds + "k" + (to_string op.kind)) }
359
+ i = (i + 1)
360
+ }
361
+ this.ok(("REMOVE 0/0, CREATE 0/0, CREATE 0/0/0, in that order (" + kinds + ")") (kinds == "30k0"))
362
+ def made@(optional):EVGHostOp (this.opFor(t "0/0/0"))
363
+ if made {
364
+ this.ok("and the child is the tall one" (((unwrap made).id) == "tall"))
365
+ }
366
+ ; And the same tree again says nothing.
367
+ t.build(two)
368
+ this.eq("built again: nothing" (t.opCount()) 0)
369
+ }
370
+
371
+ fn testScrollIsOneBit:void () {
372
+ print "-- a scroll is one SCROLL bit on the container, nothing on its children"
373
+ def root:EVGElement (EVGHostTreeTest.page("abc"))
374
+ def lay:EVGLayout (this.layout(root))
375
+ def t (new EVGHostTree())
376
+ t.setTextEngine((lay.getTextEngine()))
377
+ t.build(root)
378
+ def sc:EVGElement (root.getChild(3))
379
+ this.ok("the scroller clips" (sc.clipsContent()))
380
+ sc.scrollTop = 40.0
381
+ this.layout(root)
382
+ t.build(root)
383
+ this.eq("one op" (t.opCount()) 1)
384
+ def op:EVGHostOp (t.opAt(0))
385
+ this.ok("on the scroller" (op.path == "0/k:scroller"))
386
+ this.eq("SCROLL alone" op.bits 16)
387
+ this.ok("carrying the offset" (op.scrollTop == 40.0))
388
+ ; The tall box did move on the page — by -40 — and the host tree said
389
+ ; nothing about it, because inside its parent it did not.
390
+ def tall:EVGElement (sc.getChild(0))
391
+ this.ok("the child moved on the page" (tall.calculatedY == (sc.calculatedY - 40.0)))
392
+ }
393
+
394
+ fn testText:void () {
395
+ print "-- text is the engine's lines, and a change is TEXT"
396
+ def root:EVGElement (EVGHostTreeTest.page("abc"))
397
+ def lay:EVGLayout (this.layout(root))
398
+ def t (new EVGHostTree())
399
+ t.setTextEngine((lay.getTextEngine()))
400
+ t.build(root)
401
+ def a:EVGHostOp (t.opAt(1))
402
+ this.eq("one line" (array_length a.lines) 1)
403
+ this.ok("the line is the text" (((itemAt a.lines 0)) == "Row a"))
404
+ this.ok("at the padding" (((itemAt a.lineX 0)) == 0.0))
405
+ this.ok("a font and a size" (((strlen a.fontFamily) > 0) && (a.fontSize == 12.0)))
406
+ def b:EVGElement (root.getChild(1))
407
+ b.textContent = "Row b, with a good deal more to say than fits on one line of a two hundred pixel row"
408
+ this.layout(root)
409
+ t.build(root)
410
+ def opB@(optional):EVGHostOp (this.opFor(t "0/k:b"))
411
+ this.ok("b has an op" (!null? opB))
412
+ if opB {
413
+ def ob:EVGHostOp (unwrap opB)
414
+ this.eq("TEXT alone" ob.bits 4)
415
+ def te:EVGTextEngine (lay.getTextEngine())
416
+ def want:int (te.lineCount(b.textContent (b.effectiveFontFamily()) 12.0 200.0))
417
+ this.ok("as many lines as the engine breaks" (((array_length ob.lines) == want) && (want > 1)))
418
+ this.ok("each a line height apart" (((itemAt ob.lineY 1)) == ob.lineH))
419
+ }
420
+ }
421
+
422
+ fn testJson:void () {
423
+ print "-- the JSON carries what a DOM host reads"
424
+ def root:EVGElement (EVGHostTreeTest.page("ab"))
425
+ def lay:EVGLayout (this.layout(root))
426
+ def t (new EVGHostTree())
427
+ t.setTextEngine((lay.getTextEngine()))
428
+ t.build(root)
429
+ def j:string (t.toJson())
430
+ this.ok("a create" ((indexOf j "\"op\":\"create\"") >= 0))
431
+ this.ok("a keyed path" ((indexOf j "\"path\":\"0/k:a\"") >= 0))
432
+ this.ok("a background" ((indexOf j "\"bg\":[255,0,0,1.00]") >= 0))
433
+ this.ok("text lines" ((indexOf j "\"text\":{\"lines\":[[\"Row a\",0.00,0.00]]") >= 0))
434
+ this.ok("a role" ((indexOf j "\"role\":\"listitem\"") >= 0))
435
+ this.ok("a scroll box" ((indexOf j "\"clip\":true") >= 0))
436
+ this.ok("its offset" ((indexOf j "\"scroll\":[0.00,0.00]") >= 0))
437
+ this.ok("the tally" ((indexOf j "\"created\":5") >= 0))
438
+ root.getChild(0).scrollTop = 0.0
439
+ def sc:EVGElement (root.getChild(2))
440
+ sc.scrollTop = 12.0
441
+ this.layout(root)
442
+ t.build(root)
443
+ def j2:string (t.toJson())
444
+ this.ok("an update" ((indexOf j2 "\"op\":\"update\"") >= 0))
445
+ this.ok("with the SCROLL bit" ((indexOf j2 "\"bits\":16") >= 0))
446
+ }
447
+ }
@@ -0,0 +1,117 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; ==============================================================================
4
+ ; EVGImageDecode - PNG/JPEG bytes to a SoftCanvas ImageBuffer
5
+ ; ==============================================================================
6
+ ;
7
+ ; This began as PptxImageDecode, next to the deck viewer, with a note on it
8
+ ; saying it was not PPTX-specific and anything with image bytes could reuse it.
9
+ ; The book editor is the second thing that needed it, which is the moment a
10
+ ; note like that has to become a move: a picture on a page and a picture on a
11
+ ; slide are the same JPEG, and two copies of the sniff-the-first-two-bytes
12
+ ; decision would drift.
13
+ ;
14
+ ; Sniffing rather than trusting a file extension is the point of it - an asset
15
+ ; named .jpg is regularly a PNG, and SoftCanvas blits the same pdf_writer
16
+ ; ImageBuffer either way.
17
+ ; ==============================================================================
18
+
19
+ Import "pkg:image/ImageBuffer.rgr"
20
+ Import "pkg:image/JPEGDecoder.rgr"
21
+ Import "pkg:image/ProgressiveJPEGDecoder.rgr"
22
+ Import "pkg:image/PNGDecoder.rgr"
23
+
24
+ class EVGImageDecode {
25
+ Constructor () {
26
+ }
27
+
28
+ sfn decode@(optional):ImageBuffer (bytes:buffer) {
29
+ def n:int (buffer_length bytes)
30
+ if (n < 8) {
31
+ def __missing@(optional):ImageBuffer
32
+ return __missing
33
+ }
34
+ def b0:int (buffer_get bytes 0)
35
+ def b1:int (buffer_get bytes 1)
36
+ ; JPEG SOI
37
+ if ((b0 == 255) && (b1 == 216)) {
38
+ ; Baseline and progressive are different decoders, and which one a
39
+ ; file needs is in its SOF marker rather than in its name. Sending
40
+ ; a progressive JPEG to the baseline decoder is not an error, it is
41
+ ; a picture that does not arrive — "some of the photos are simply
42
+ ; missing" is a poor way to find out which kind you have.
43
+ if (EVGImageDecode.isProgressive(bytes)) {
44
+ def prog:ProgressiveJPEGDecoder (new ProgressiveJPEGDecoder)
45
+ prog.quiet = true
46
+ def pjim:ImageBuffer (prog.decodeBytes(bytes))
47
+ if ((pjim.width > 0) && (pjim.height > 0)) {
48
+ def hitP@(optional):ImageBuffer pjim
49
+ return hitP
50
+ }
51
+ def __failPJ@(optional):ImageBuffer
52
+ return __failPJ
53
+ }
54
+ def jdec:JPEGDecoder (new JPEGDecoder)
55
+ ; A picture is decoded here because something is being DRAWN, not
56
+ ; because anybody asked about the JPEG, so the decoder's own
57
+ ; narration is noise - and on a native host it is noise on
58
+ ; somebody's terminal, twenty-five lines per photograph.
59
+ jdec.quiet = true
60
+ def jim:ImageBuffer (jdec.decodeBytes(bytes))
61
+ if ((jim.width > 0) && (jim.height > 0)) {
62
+ def hit@(optional):ImageBuffer jim
63
+ return hit
64
+ }
65
+ def __failJ@(optional):ImageBuffer
66
+ return __failJ
67
+ }
68
+ ; PNG signature 89 50 4E 47
69
+ if ((b0 == 137) && (b1 == 80)) {
70
+ def pdec:PNGDecoder (new PNGDecoder)
71
+ def pim:ImageBuffer (pdec.decodeBytes(bytes))
72
+ ; Ask the DECODER what it read, not the buffer it handed back.
73
+ ; `decodeBytes` returns a 1x1 image when it gives up, so judging by
74
+ ; the result made a genuine 1x1 PNG — a spacer, a placeholder, the
75
+ ; thumbnail in a `.fig` — indistinguishable from a failure, and it
76
+ ; was rejected. The header's own dimensions stay 0 when nothing
77
+ ; parsed, and say 1x1 when the file really is that.
78
+ if ((pdec.width > 0) && (pdec.height > 0)) {
79
+ def hit2@(optional):ImageBuffer pim
80
+ return hit2
81
+ }
82
+ def __failP@(optional):ImageBuffer
83
+ return __failP
84
+ }
85
+ def __missing2@(optional):ImageBuffer
86
+ return __missing2
87
+ }
88
+
89
+ ; SOF2 says progressive, SOF0 says baseline, and whichever comes first
90
+ ; decides. The scan stops at that marker rather than walking the file:
91
+ ; a frame header is in the first few hundred bytes, and past it every
92
+ ; 0xFF byte is entropy-coded data that means nothing here.
93
+ sfn isProgressive:boolean (bytes:buffer) {
94
+ def n:int (buffer_length bytes)
95
+ def limit:int n
96
+ if (limit > 65536) {
97
+ limit = 65536
98
+ }
99
+ def i:int 2
100
+ while (i < (limit - 1)) {
101
+ if ((buffer_get bytes i) == 255) {
102
+ def marker:int (buffer_get bytes (i + 1))
103
+ if (marker == 194) {
104
+ return true
105
+ }
106
+ if (marker == 192) {
107
+ return false
108
+ }
109
+ if (marker == 193) {
110
+ return false
111
+ }
112
+ }
113
+ i = (i + 1)
114
+ }
115
+ return false
116
+ }
117
+ }
@@ -0,0 +1,86 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; EVGImageMeasurer.rgr - Abstract image measurement interface for EVG layout
4
+
5
+ class EVGImageDimensions {
6
+ def width:int 0
7
+ def height:int 0
8
+ def aspectRatio:double 1.0
9
+ def isValid:boolean false
10
+
11
+ Constructor () {
12
+ width = 0
13
+ height = 0
14
+ aspectRatio = 1.0
15
+ isValid = false
16
+ }
17
+
18
+ sfn create:EVGImageDimensions (w:int h:int) {
19
+ def d (new EVGImageDimensions())
20
+ d.width = w
21
+ d.height = h
22
+ if (h > 0) {
23
+ d.aspectRatio = ((to_double w) / (to_double h))
24
+ }
25
+ d.isValid = true
26
+ return d
27
+ }
28
+ }
29
+
30
+ ; Abstract interface - implementations must override
31
+ class EVGImageMeasurer {
32
+
33
+ fn getImageDimensions:EVGImageDimensions (src:string) {
34
+ ; Default implementation - returns invalid dimensions
35
+ ; Subclasses should override to provide actual image dimensions
36
+ def dims (new EVGImageDimensions())
37
+ return dims
38
+ }
39
+
40
+ fn calculateHeightForWidth:double (src:string targetWidth:double) {
41
+ ; Calculate height to maintain aspect ratio for given width
42
+ def dims:EVGImageDimensions (this.getImageDimensions(src))
43
+ if dims.isValid {
44
+ return (targetWidth / dims.aspectRatio)
45
+ }
46
+ ; Default to square if we can't determine aspect ratio
47
+ return targetWidth
48
+ }
49
+
50
+ fn calculateWidthForHeight:double (src:string targetHeight:double) {
51
+ ; Calculate width to maintain aspect ratio for given height
52
+ def dims:EVGImageDimensions (this.getImageDimensions(src))
53
+ if dims.isValid {
54
+ return (targetHeight * dims.aspectRatio)
55
+ }
56
+ ; Default to square if we can't determine aspect ratio
57
+ return targetHeight
58
+ }
59
+
60
+ fn calculateFitDimensions:EVGImageDimensions (src:string maxWidth:double maxHeight:double) {
61
+ ; Calculate dimensions to fit within box while maintaining aspect ratio
62
+ def dims:EVGImageDimensions (this.getImageDimensions(src))
63
+ if (dims.isValid == false) {
64
+ ; Default to max size if we can't determine aspect ratio
65
+ return (EVGImageDimensions.create((to_int maxWidth) (to_int maxHeight)))
66
+ }
67
+
68
+ def scaleW:double (maxWidth / (to_double dims.width))
69
+ def scaleH:double (maxHeight / (to_double dims.height))
70
+ def scale:double scaleW
71
+ if (scaleH < scaleW) {
72
+ scale = scaleH
73
+ }
74
+
75
+ def newW:int (to_int ((to_double dims.width) * scale))
76
+ def newH:int (to_int ((to_double dims.height) * scale))
77
+
78
+ return (EVGImageDimensions.create(newW newH))
79
+ }
80
+ }
81
+
82
+ ; Simple implementation that always returns invalid (for testing without images)
83
+ class SimpleImageMeasurer {
84
+ Extends(EVGImageMeasurer)
85
+ ; Uses default implementation - returns invalid dimensions
86
+ }