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,388 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ Import "RasterBuffer.rgr"
4
+ Import "Deflate.rgr"
5
+ Import "Buffer.rgr"
6
+
7
+ ; PNG Encoder - Lossless image output
8
+ ; Implements basic PNG format with uncompressed DEFLATE
9
+
10
+ class PNGEncoder {
11
+ def output:GrowableBuffer
12
+ def crcTable:[int]
13
+ def crcTableInit:boolean false
14
+ ; Emit uncompressed deflate blocks instead of compressing. What this file
15
+ ; used to do always.
16
+ def stored:boolean false
17
+
18
+ Constructor () {
19
+ output = (new GrowableBuffer())
20
+ }
21
+
22
+ ; Initialize CRC32 lookup table
23
+ fn initCRCTable:void () {
24
+ if crcTableInit {
25
+ return
26
+ }
27
+ def n:int 0
28
+ while (n < 256) {
29
+ def c:int n
30
+ def k:int 0
31
+ while (k < 8) {
32
+ if ((bit_and c 1) != 0) {
33
+ ; EDB88320 in decimal = 3988292384
34
+ c = (bit_xor (bit_ushr c 1) 3988292384)
35
+ } {
36
+ c = (bit_ushr c 1)
37
+ }
38
+ k = k + 1
39
+ }
40
+ push crcTable c
41
+ n = n + 1
42
+ }
43
+ crcTableInit = true
44
+ }
45
+
46
+ ; Calculate CRC32 of a GrowableBuffer section
47
+ fn crc32Buffer:int (data:GrowableBuffer) {
48
+ this.initCRCTable()
49
+ ; FFFFFFFF in decimal = 4294967295
50
+ def crc:int 4294967295
51
+ def buf:buffer (data.toBuffer())
52
+ def len:int (data.size())
53
+ def i:int 0
54
+ while (i < len) {
55
+ def byte:int (buffer_get buf i)
56
+ def idx:int (bit_and (bit_xor crc byte) 255)
57
+ crc = (bit_xor (bit_ushr crc 8) (itemAt crcTable idx))
58
+ i = i + 1
59
+ }
60
+ return (bit_xor crc 4294967295)
61
+ }
62
+
63
+ ; Write PNG signature
64
+ fn writeSignature:void () {
65
+ output.writeByte(137) ; 0x89
66
+ output.writeByte(80) ; P
67
+ output.writeByte(78) ; N
68
+ output.writeByte(71) ; G
69
+ output.writeByte(13) ; 0x0D
70
+ output.writeByte(10) ; 0x0A
71
+ output.writeByte(26) ; 0x1A
72
+ output.writeByte(10) ; 0x0A
73
+ }
74
+
75
+ ; Write 32-bit big-endian integer
76
+ fn writeUInt32:void (value:int) {
77
+ output.writeByte((bit_and (bit_shr value 24) 255))
78
+ output.writeByte((bit_and (bit_shr value 16) 255))
79
+ output.writeByte((bit_and (bit_shr value 8) 255))
80
+ output.writeByte((bit_and value 255))
81
+ }
82
+
83
+ ; Write 32-bit big-endian to a GrowableBuffer
84
+ fn writeUInt32To:void (buf:GrowableBuffer value:int) {
85
+ buf.writeByte((bit_and (bit_shr value 24) 255))
86
+ buf.writeByte((bit_and (bit_shr value 16) 255))
87
+ buf.writeByte((bit_and (bit_shr value 8) 255))
88
+ buf.writeByte((bit_and value 255))
89
+ }
90
+
91
+ ; Write a PNG chunk
92
+ fn writeChunk:void (chunkType:string data:GrowableBuffer) {
93
+ def dataLen:int (data.size())
94
+
95
+ ; Length
96
+ this.writeUInt32(dataLen)
97
+
98
+ ; Build chunk type + data for CRC
99
+ def crcData:GrowableBuffer (new GrowableBuffer())
100
+ def i:int 0
101
+ while (i < 4) {
102
+ def ch:int (charAt chunkType i)
103
+ crcData.writeByte(ch)
104
+ output.writeByte(ch)
105
+ i = i + 1
106
+ }
107
+
108
+ ; Data
109
+ def dataBuf:buffer (data.toBuffer())
110
+ i = 0
111
+ while (i < dataLen) {
112
+ def b:int (buffer_get dataBuf i)
113
+ crcData.writeByte(b)
114
+ output.writeByte(b)
115
+ i = i + 1
116
+ }
117
+
118
+ ; CRC
119
+ def crc:int (this.crc32Buffer(crcData))
120
+ this.writeUInt32(crc)
121
+ }
122
+
123
+ ; Write IHDR chunk (colorType 2 = RGB).
124
+ fn writeIHDR:void (width:int height:int) {
125
+ this.writeIHDRColor(width height 2)
126
+ }
127
+
128
+ fn writeIHDRColor:void (width:int height:int colorType:int) {
129
+ def data:GrowableBuffer (new GrowableBuffer())
130
+
131
+ ; Width (4 bytes)
132
+ this.writeUInt32To(data width)
133
+
134
+ ; Height (4 bytes)
135
+ this.writeUInt32To(data height)
136
+
137
+ ; Bit depth: 8
138
+ data.writeByte(8)
139
+ data.writeByte(colorType)
140
+ ; Compression method: 0 = deflate
141
+ data.writeByte(0)
142
+ ; Filter method: 0
143
+ data.writeByte(0)
144
+ ; Interlace method: 0 = none
145
+ data.writeByte(0)
146
+
147
+ this.writeChunk("IHDR" data)
148
+ }
149
+
150
+ ; Adler32 checksum for zlib
151
+ fn adler32:int (data:GrowableBuffer) {
152
+ def a:int 1
153
+ def b:int 0
154
+ def buf:buffer (data.toBuffer())
155
+ def len:int (data.size())
156
+ def i:int 0
157
+ while (i < len) {
158
+ a = ((a + (buffer_get buf i)) % 65521)
159
+ b = ((b + a) % 65521)
160
+ i = i + 1
161
+ }
162
+ return (bit_or (bit_shl b 16) a)
163
+ }
164
+
165
+ ; Create uncompressed DEFLATE blocks (store mode)
166
+ ; Compressed with fixed-Huffman deflate. Stored blocks are the fallback,
167
+ ; kept because they are the one encoding that cannot be wrong — a bug here
168
+ ; would be a file nothing opens, and `stored` is one flag away.
169
+ fn createDeflateData:void (rawData:GrowableBuffer compressed:GrowableBuffer) {
170
+ if (stored == false) {
171
+ def dz (new Deflate)
172
+ def w (new DeflateWriter)
173
+ dz.compress((rawData.toBuffer()) (rawData.size()) w)
174
+ def packed:buffer (w.out.toBuffer())
175
+ def pi:int 0
176
+ def pn:int (w.out.size())
177
+ while (pi < pn) {
178
+ compressed.writeByte((buffer_get packed pi))
179
+ pi = (pi + 1)
180
+ }
181
+ return
182
+ }
183
+ def rawBuf:buffer (rawData.toBuffer())
184
+ def dataLen:int (rawData.size())
185
+ def blockSize:int 65535 ; Max block size
186
+ def offset:int 0
187
+
188
+ while (offset < dataLen) {
189
+ def remaining:int (dataLen - offset)
190
+ def len:int blockSize
191
+ if (remaining < len) {
192
+ len = remaining
193
+ }
194
+
195
+ def isFinal:int 0
196
+ if ((offset + len) >= dataLen) {
197
+ isFinal = 1
198
+ }
199
+
200
+ ; Block header: BFINAL=1/0, BTYPE=00 (stored)
201
+ compressed.writeByte(isFinal)
202
+
203
+ ; LEN (2 bytes, little-endian)
204
+ compressed.writeByte((bit_and len 255))
205
+ compressed.writeByte((bit_and (bit_shr len 8) 255))
206
+
207
+ ; NLEN (one's complement of LEN)
208
+ def nlen:int (bit_xor len 65535)
209
+ compressed.writeByte((bit_and nlen 255))
210
+ compressed.writeByte((bit_and (bit_shr nlen 8) 255))
211
+
212
+ ; Data
213
+ def i:int 0
214
+ while (i < len) {
215
+ compressed.writeByte((buffer_get rawBuf (offset + i)))
216
+ i = i + 1
217
+ }
218
+
219
+ offset = offset + len
220
+ }
221
+ }
222
+
223
+ ; Write IDAT chunk with image data
224
+ fn writeIDAT:void (buf:RasterBuffer) {
225
+ ; Build raw scanline data with filter bytes
226
+ def rawData:GrowableBuffer (new GrowableBuffer())
227
+
228
+ def y:int 0
229
+ while (y < buf.height) {
230
+ ; Filter type: 0 = None
231
+ rawData.writeByte(0)
232
+
233
+ def x:int 0
234
+ while (x < buf.width) {
235
+ def idx:int ((y * buf.width + x) * 4)
236
+ def r:int (buffer_get buf.pixels idx)
237
+ def g:int (buffer_get buf.pixels (idx + 1))
238
+ def b:int (buffer_get buf.pixels (idx + 2))
239
+ def a:int (buffer_get buf.pixels (idx + 3))
240
+ ; A three-channel PNG cannot carry alpha, so a translucent
241
+ ; pixel has to be composited against something. White: this is
242
+ ; the format a picture is pasted into a document as, and a
243
+ ; document's page is white. It used to premultiply against
244
+ ; black with `>> 8`, which is a divide by 256 rather than 255 —
245
+ ; so a pixel the text renderer left at alpha 254, which every
246
+ ; antialiased glyph edge is, came out two levels darker than it
247
+ ; was drawn, and a round trip through our own decoder could not
248
+ ; get back what was painted.
249
+ if (a < 255) {
250
+ def inv:int (255 - a)
251
+ r = (idiv (((r * a) + (255 * inv)) + 127) 255)
252
+ g = (idiv (((g * a) + (255 * inv)) + 127) 255)
253
+ b = (idiv (((b * a) + (255 * inv)) + 127) 255)
254
+ }
255
+ rawData.writeByte(r)
256
+ rawData.writeByte(g)
257
+ rawData.writeByte(b)
258
+ x = x + 1
259
+ }
260
+ y = y + 1
261
+ }
262
+
263
+ ; Wrap in zlib format
264
+ def zlibData:GrowableBuffer (new GrowableBuffer())
265
+
266
+ ; Zlib header (CMF, FLG)
267
+ zlibData.writeByte(120) ; 0x78 CMF: CM=8 (deflate), CINFO=7 (32K window)
268
+ zlibData.writeByte(1) ; 0x01 FLG: no dict, level 0
269
+
270
+ ; DEFLATE data
271
+ this.createDeflateData(rawData zlibData)
272
+
273
+ ; Adler32 checksum (big-endian)
274
+ def adler:int (this.adler32(rawData))
275
+ zlibData.writeByte((bit_and (bit_shr adler 24) 255))
276
+ zlibData.writeByte((bit_and (bit_shr adler 16) 255))
277
+ zlibData.writeByte((bit_and (bit_shr adler 8) 255))
278
+ zlibData.writeByte((bit_and adler 255))
279
+
280
+ this.writeChunk("IDAT" zlibData)
281
+ }
282
+
283
+ ; Write IDAT chunk with RGBA scanlines (color type 6).
284
+ fn writeIDATRgba:void (buf:RasterBuffer) {
285
+ def rawData:GrowableBuffer (new GrowableBuffer())
286
+
287
+ def y:int 0
288
+ while (y < buf.height) {
289
+ rawData.writeByte(0)
290
+ def x:int 0
291
+ while (x < buf.width) {
292
+ def idx:int ((y * buf.width + x) * 4)
293
+ rawData.writeByte((buffer_get buf.pixels idx))
294
+ rawData.writeByte((buffer_get buf.pixels (idx + 1)))
295
+ rawData.writeByte((buffer_get buf.pixels (idx + 2)))
296
+ rawData.writeByte((buffer_get buf.pixels (idx + 3)))
297
+ x = (x + 1)
298
+ }
299
+ y = (y + 1)
300
+ }
301
+
302
+ def zlibData:GrowableBuffer (new GrowableBuffer())
303
+ zlibData.writeByte(120)
304
+ zlibData.writeByte(1)
305
+ this.createDeflateData(rawData zlibData)
306
+ def adler:int (this.adler32(rawData))
307
+ zlibData.writeByte((bit_and (bit_shr adler 24) 255))
308
+ zlibData.writeByte((bit_and (bit_shr adler 16) 255))
309
+ zlibData.writeByte((bit_and (bit_shr adler 8) 255))
310
+ zlibData.writeByte((bit_and adler 255))
311
+ this.writeChunk("IDAT" zlibData)
312
+ }
313
+ fn writeIEND:void () {
314
+ def data:GrowableBuffer (new GrowableBuffer())
315
+ this.writeChunk("IEND" data)
316
+ }
317
+
318
+ ; The same, without the running commentary. A chart copied to the clipboard
319
+ ; encodes a PNG on every Ctrl+C, and three lines of log per keystroke is
320
+ ; noise in a server that is otherwise quiet.
321
+ fn encodeQuiet:buffer (buf:RasterBuffer) {
322
+ output = (new GrowableBuffer())
323
+ this.writeSignature()
324
+ this.writeIHDR(buf.width buf.height)
325
+ this.writeIDAT(buf)
326
+ this.writeIEND()
327
+ return (output.toBuffer())
328
+ }
329
+
330
+ ; Encode buffer to PNG and return as buffer (for HTTP responses)
331
+ fn encodeToBuffer:buffer (buf:RasterBuffer) {
332
+ print ("Encoding PNG to buffer")
333
+ print (" Image size: " + (to_string buf.width) + "x" + (to_string buf.height))
334
+
335
+ ; Reset output
336
+ output = (new GrowableBuffer())
337
+
338
+ this.writeSignature()
339
+ this.writeIHDR(buf.width buf.height)
340
+ this.writeIDAT(buf)
341
+ this.writeIEND()
342
+
343
+ def finalSize:int (output.size())
344
+ print (" Encoded size: " + (to_string finalSize) + " bytes")
345
+
346
+ return (output.toBuffer())
347
+ }
348
+
349
+ ; Encode buffer to PNG and save to file
350
+ fn encode:void (buf:RasterBuffer dirPath:string fileName:string) {
351
+ print ("Encoding PNG: " + fileName)
352
+ print (" Image size: " + (to_string buf.width) + "x" + (to_string buf.height))
353
+
354
+ ; Reset output
355
+ output = (new GrowableBuffer())
356
+
357
+ this.writeSignature()
358
+ this.writeIHDR(buf.width buf.height)
359
+ this.writeIDAT(buf)
360
+ this.writeIEND()
361
+
362
+ def finalSize:int (output.size())
363
+ print (" Encoded size: " + (to_string finalSize) + " bytes")
364
+
365
+ ; Write to file
366
+ def outBuf:buffer (output.toBuffer())
367
+ buffer_write_file dirPath fileName outBuf
368
+ print (" Saved: " + dirPath + "/" + fileName)
369
+ }
370
+
371
+ ; Encode RGBA PNG (preserves alpha — use for LPC compose sheets).
372
+ fn encodeRgba:void (buf:RasterBuffer dirPath:string fileName:string) {
373
+ print ("Encoding RGBA PNG: " + fileName)
374
+ print (" Image size: " + (to_string buf.width) + "x" + (to_string buf.height))
375
+
376
+ output = (new GrowableBuffer())
377
+ this.writeSignature()
378
+ this.writeIHDRColor(buf.width buf.height 6)
379
+ this.writeIDATRgba(buf)
380
+ this.writeIEND()
381
+
382
+ def finalSize:int (output.size())
383
+ print (" Encoded size: " + (to_string finalSize) + " bytes")
384
+ def outBuf:buffer (output.toBuffer())
385
+ buffer_write_file dirPath fileName outBuf
386
+ print (" Saved: " + dirPath + "/" + fileName)
387
+ }
388
+ }
@@ -0,0 +1,224 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; PPMImage.rgr - PPM (Portable Pixmap) image reader/writer
4
+ ; PPM is a simple uncompressed image format - easy to read/write
5
+ ;
6
+ ; Format: P6 (binary RGB) or P3 (ASCII RGB)
7
+ ; Header: P6\n<width> <height>\n<maxval>\n<binary RGB data>
8
+
9
+ Import "Buffer.rgr"
10
+ Import "ImageBuffer.rgr"
11
+
12
+ class PPMImage {
13
+
14
+ Constructor () {
15
+ }
16
+
17
+ fn parseNumber:int (data:buffer startPos:int endPos:[int]) {
18
+ ; Parse ASCII number, skip whitespace, return value and update endPos
19
+ def len:int (buffer_length data)
20
+ def pos:int startPos
21
+
22
+ ; Skip whitespace
23
+ def skipping:boolean true
24
+ while (skipping && (pos < len)) {
25
+ def ch:int (buffer_get data pos)
26
+ if ((ch == 32) || (ch == 10) || (ch == 13) || (ch == 9)) {
27
+ pos = pos + 1
28
+ } {
29
+ skipping = false
30
+ }
31
+ }
32
+
33
+ ; Parse digits
34
+ def value:int 0
35
+ def parsing:boolean true
36
+ while (parsing && (pos < len)) {
37
+ def ch:int (buffer_get data pos)
38
+ if ((ch >= 48) && (ch <= 57)) {
39
+ value = (value * 10) + (ch - 48)
40
+ pos = pos + 1
41
+ } {
42
+ parsing = false
43
+ }
44
+ }
45
+
46
+ set endPos 0 pos
47
+ return value
48
+ }
49
+
50
+ fn skipToNextLine:int (data:buffer pos:int) {
51
+ def len:int (buffer_length data)
52
+ while (pos < len) {
53
+ def ch:int (buffer_get data pos)
54
+ pos = pos + 1
55
+ if (ch == 10) {
56
+ return pos
57
+ }
58
+ }
59
+ return pos
60
+ }
61
+
62
+ fn load:ImageBuffer (dirPath:string fileName:string) {
63
+ def data:buffer (buffer_read_file dirPath fileName)
64
+ def len:int (buffer_length data)
65
+
66
+ if (len < 10) {
67
+ print ("Error: File too small: " + fileName)
68
+ def errImg (new ImageBuffer())
69
+ errImg.init(1 1)
70
+ return errImg
71
+ }
72
+
73
+ ; Check magic number (P6 for binary PPM)
74
+ def m1:int (buffer_get data 0)
75
+ def m2:int (buffer_get data 1)
76
+
77
+ if ((m1 != 80) || ((m2 != 54) && (m2 != 51))) {
78
+ ; Not P6 or P3
79
+ print ("Error: Not a PPM file (P3 or P6): " + fileName)
80
+ def errImg (new ImageBuffer())
81
+ errImg.init(1 1)
82
+ return errImg
83
+ }
84
+
85
+ def isBinary:boolean (m2 == 54)
86
+ def pos:int 2
87
+
88
+ ; Skip comments and whitespace, parse width, height, maxval
89
+ def endPos:[int]
90
+ push endPos 0
91
+
92
+ ; Skip any comments
93
+ def skippingComments:boolean true
94
+ while (skippingComments && (pos < len)) {
95
+ def ch:int (buffer_get data pos)
96
+ if ((ch == 32) || (ch == 10) || (ch == 13) || (ch == 9)) {
97
+ pos = pos + 1
98
+ } {
99
+ if (ch == 35) {
100
+ ; Comment line starting with #
101
+ pos = (this.skipToNextLine(data pos))
102
+ } {
103
+ skippingComments = false
104
+ }
105
+ }
106
+ }
107
+
108
+ def width:int (this.parseNumber(data pos endPos))
109
+ pos = (at endPos 0)
110
+
111
+ def height:int (this.parseNumber(data pos endPos))
112
+ pos = (at endPos 0)
113
+
114
+ def maxVal:int (this.parseNumber(data pos endPos))
115
+ pos = (at endPos 0)
116
+
117
+ ; Skip single whitespace after maxval
118
+ if (pos < len) {
119
+ pos = pos + 1
120
+ }
121
+
122
+ print ("Loading PPM: " + (to_string width) + "x" + (to_string height) + ", maxval=" + (to_string maxVal))
123
+
124
+ def img (new ImageBuffer())
125
+ img.init(width height)
126
+
127
+ if isBinary {
128
+ ; P6: Binary RGB data
129
+ def y 0
130
+ while (y < height) {
131
+ def x 0
132
+ while (x < width) {
133
+ if ((pos + 2) < len) {
134
+ def r:int (buffer_get data pos)
135
+ def g:int (buffer_get data (pos + 1))
136
+ def b:int (buffer_get data (pos + 2))
137
+ img.setPixelRGB(x y r g b)
138
+ pos = pos + 3
139
+ }
140
+ x = x + 1
141
+ }
142
+ y = y + 1
143
+ }
144
+ } {
145
+ ; P3: ASCII RGB data
146
+ def y 0
147
+ while (y < height) {
148
+ def x 0
149
+ while (x < width) {
150
+ def r:int (this.parseNumber(data pos endPos))
151
+ pos = (at endPos 0)
152
+ def g:int (this.parseNumber(data pos endPos))
153
+ pos = (at endPos 0)
154
+ def b:int (this.parseNumber(data pos endPos))
155
+ pos = (at endPos 0)
156
+ img.setPixelRGB(x y r g b)
157
+ x = x + 1
158
+ }
159
+ y = y + 1
160
+ }
161
+ }
162
+
163
+ return img
164
+ }
165
+
166
+ fn save:void (img:ImageBuffer dirPath:string fileName:string) {
167
+ ; Save as P6 (binary PPM)
168
+ def buf (new GrowableBuffer())
169
+
170
+ ; Header
171
+ buf.writeString("P6\n")
172
+ buf.writeString((to_string img.width) + " " + (to_string img.height) + "\n")
173
+ buf.writeString("255\n")
174
+
175
+ ; Pixel data (RGB only, no alpha)
176
+ def y 0
177
+ while (y < img.height) {
178
+ def x 0
179
+ while (x < img.width) {
180
+ def c:Color (img.getPixel(x y))
181
+ buf.writeByte(c.r)
182
+ buf.writeByte(c.g)
183
+ buf.writeByte(c.b)
184
+ x = x + 1
185
+ }
186
+ y = y + 1
187
+ }
188
+
189
+ def data:buffer (buf.toBuffer())
190
+ buffer_write_file dirPath fileName data
191
+ print ("Saved PPM: " + dirPath + "/" + fileName)
192
+ }
193
+
194
+ fn saveP3:void (img:ImageBuffer dirPath:string fileName:string) {
195
+ ; Save as P3 (ASCII PPM) - larger but human-readable
196
+ def buf (new GrowableBuffer())
197
+
198
+ ; Header
199
+ buf.writeString("P3\n")
200
+ buf.writeString("# Created by Ranger ImageEditor\n")
201
+ buf.writeString((to_string img.width) + " " + (to_string img.height) + "\n")
202
+ buf.writeString("255\n")
203
+
204
+ ; Pixel data as ASCII
205
+ def y 0
206
+ while (y < img.height) {
207
+ def x 0
208
+ while (x < img.width) {
209
+ def c:Color (img.getPixel(x y))
210
+ buf.writeString((to_string c.r) + " " + (to_string c.g) + " " + (to_string c.b))
211
+ if (x < (img.width - 1)) {
212
+ buf.writeString(" ")
213
+ }
214
+ x = x + 1
215
+ }
216
+ buf.writeString("\n")
217
+ y = y + 1
218
+ }
219
+
220
+ def data:buffer (buf.toBuffer())
221
+ buffer_write_file dirPath fileName data
222
+ print ("Saved PPM (ASCII): " + dirPath + "/" + fileName)
223
+ }
224
+ }