@bloopjs/toodle 0.0.100

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 (358) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/Toodle.d.ts +304 -0
  4. package/dist/Toodle.d.ts.map +1 -0
  5. package/dist/colors/mod.d.ts +872 -0
  6. package/dist/colors/mod.d.ts.map +1 -0
  7. package/dist/coreTypes/Color.d.ts +7 -0
  8. package/dist/coreTypes/Color.d.ts.map +1 -0
  9. package/dist/coreTypes/Point.d.ts +8 -0
  10. package/dist/coreTypes/Point.d.ts.map +1 -0
  11. package/dist/coreTypes/Size.d.ts +5 -0
  12. package/dist/coreTypes/Size.d.ts.map +1 -0
  13. package/dist/coreTypes/Transform.d.ts +16 -0
  14. package/dist/coreTypes/Transform.d.ts.map +1 -0
  15. package/dist/coreTypes/Vec2.d.ts +8 -0
  16. package/dist/coreTypes/Vec2.d.ts.map +1 -0
  17. package/dist/coreTypes/mod.d.ts +6 -0
  18. package/dist/coreTypes/mod.d.ts.map +1 -0
  19. package/dist/docs/snippets/add-remove-children.d.ts +1 -0
  20. package/dist/docs/snippets/basic-quad.d.ts +1 -0
  21. package/dist/docs/snippets/filter-linear.d.ts +1 -0
  22. package/dist/docs/snippets/filter-nearest.d.ts +1 -0
  23. package/dist/docs/snippets/flipxy.d.ts +1 -0
  24. package/dist/docs/snippets/hello-text.d.ts +1 -0
  25. package/dist/docs/snippets/jumbo-textures.d.ts +1 -0
  26. package/dist/docs/snippets/layer.d.ts +1 -0
  27. package/dist/docs/snippets/layout-edges.d.ts +1 -0
  28. package/dist/docs/snippets/layout-screen-and-world-space.d.ts +1 -0
  29. package/dist/docs/snippets/postprocess.d.ts +1 -0
  30. package/dist/docs/snippets/quad-size-scale.d.ts +1 -0
  31. package/dist/docs/snippets/quickstart.d.ts +1 -0
  32. package/dist/docs/snippets/repeat-texture-loading.d.ts +1 -0
  33. package/dist/docs/snippets/screen-shaders.d.ts +1 -0
  34. package/dist/docs/snippets/shader-color-flash.d.ts +1 -0
  35. package/dist/docs/snippets/shader-default.d.ts +1 -0
  36. package/dist/docs/snippets/shader-fill.d.ts +1 -0
  37. package/dist/docs/snippets/shapes-line.d.ts +1 -0
  38. package/dist/docs/snippets/sprite-region.d.ts +1 -0
  39. package/dist/docs/snippets/text-alignment.d.ts +1 -0
  40. package/dist/docs/snippets/text-shrink-to-fit.d.ts +1 -0
  41. package/dist/docs/snippets/text-word-wrap.d.ts +1 -0
  42. package/dist/docs/snippets/texture-bundles-prebaked.d.ts +1 -0
  43. package/dist/docs/snippets/texture-bundles.d.ts +1 -0
  44. package/dist/docs/snippets/transforms.d.ts +1 -0
  45. package/dist/docs/snippets/transparent-cropping.d.ts +1 -0
  46. package/dist/examples/0-hello.d.ts +1 -0
  47. package/dist/examples/1-hello.d.ts +1 -0
  48. package/dist/examples/1-hello.d.ts.map +1 -0
  49. package/dist/examples/1-quad.d.ts +1 -0
  50. package/dist/examples/10-resize.d.ts +1 -0
  51. package/dist/examples/10-resize.d.ts.map +1 -0
  52. package/dist/examples/11-bundle-test.d.ts +1 -0
  53. package/dist/examples/11-bundle-test.d.ts.map +1 -0
  54. package/dist/examples/12-transparent-pixel-cropping.d.ts +1 -0
  55. package/dist/examples/12-transparent-pixel-cropping.d.ts.map +1 -0
  56. package/dist/examples/13-crop.d.ts +1 -0
  57. package/dist/examples/13-crop.d.ts.map +1 -0
  58. package/dist/examples/14-bundle-bench.d.ts +1 -0
  59. package/dist/examples/14-bundle-bench.d.ts.map +1 -0
  60. package/dist/examples/15-text-layer.d.ts +1 -0
  61. package/dist/examples/15-text-layer.d.ts.map +1 -0
  62. package/dist/examples/16-jumbo-texture.d.ts +1 -0
  63. package/dist/examples/16-jumbo-textures.d.ts +1 -0
  64. package/dist/examples/16-screen-shader.d.ts +1 -0
  65. package/dist/examples/16-screen-shader.d.ts.map +1 -0
  66. package/dist/examples/17-lighting.d.ts +1 -0
  67. package/dist/examples/17-lighting.d.ts.map +1 -0
  68. package/dist/examples/17-translations.d.ts +1 -0
  69. package/dist/examples/18-blur.d.ts +1 -0
  70. package/dist/examples/19-postprocess.d.ts +1 -0
  71. package/dist/examples/19-screenshader.d.ts +1 -0
  72. package/dist/examples/2-shapes.d.ts +1 -0
  73. package/dist/examples/2-shapes.d.ts.map +1 -0
  74. package/dist/examples/3-shader.d.ts +1 -0
  75. package/dist/examples/3-shader.d.ts.map +1 -0
  76. package/dist/examples/4-shader-bench.d.ts +1 -0
  77. package/dist/examples/4-shader-bench.d.ts.map +1 -0
  78. package/dist/examples/5-z.d.ts +1 -0
  79. package/dist/examples/5-z.d.ts.map +1 -0
  80. package/dist/examples/6-atlas.d.ts +1 -0
  81. package/dist/examples/6-atlas.d.ts.map +1 -0
  82. package/dist/examples/7-text.d.ts +1 -0
  83. package/dist/examples/7-text.d.ts.map +1 -0
  84. package/dist/examples/8-text-bench.d.ts +1 -0
  85. package/dist/examples/8-text-bench.d.ts.map +1 -0
  86. package/dist/examples/9-alignment.d.ts +1 -0
  87. package/dist/examples/9-alignment.d.ts.map +1 -0
  88. package/dist/examples/main.d.ts +1 -0
  89. package/dist/examples/main.d.ts.map +1 -0
  90. package/dist/examples/util.d.ts +82 -0
  91. package/dist/examples/util.d.ts.map +1 -0
  92. package/dist/limits.d.ts +23 -0
  93. package/dist/limits.d.ts.map +1 -0
  94. package/dist/math/angle.d.ts +13 -0
  95. package/dist/math/angle.d.ts.map +1 -0
  96. package/dist/math/matrix.d.ts +26 -0
  97. package/dist/math/matrix.d.ts.map +1 -0
  98. package/dist/math/mod.d.ts +3 -0
  99. package/dist/math/mod.d.ts.map +1 -0
  100. package/dist/mod.d.ts +17 -0
  101. package/dist/mod.d.ts.map +1 -0
  102. package/dist/mod.js +19665 -0
  103. package/dist/mod.js.map +41 -0
  104. package/dist/postprocess.d.ts +10 -0
  105. package/dist/postprocess.d.ts.map +1 -0
  106. package/dist/scene/Batcher.d.ts +20 -0
  107. package/dist/scene/Batcher.d.ts.map +1 -0
  108. package/dist/scene/Camera.d.ts +16 -0
  109. package/dist/scene/Camera.d.ts.map +1 -0
  110. package/dist/scene/JumboQuadNode.d.ts +29 -0
  111. package/dist/scene/JumboQuadNode.d.ts.map +1 -0
  112. package/dist/scene/QuadNode.d.ts +159 -0
  113. package/dist/scene/QuadNode.d.ts.map +1 -0
  114. package/dist/scene/RenderComponent.d.ts +11 -0
  115. package/dist/scene/RenderComponent.d.ts.map +1 -0
  116. package/dist/scene/SceneNode.d.ts +300 -0
  117. package/dist/scene/SceneNode.d.ts.map +1 -0
  118. package/dist/scene/mod.d.ts +5 -0
  119. package/dist/scene/mod.d.ts.map +1 -0
  120. package/dist/screen/mod.d.ts +2 -0
  121. package/dist/screen/mod.d.ts.map +1 -0
  122. package/dist/screen/resolution.d.ts +5 -0
  123. package/dist/screen/resolution.d.ts.map +1 -0
  124. package/dist/shaders/EngineUniform.d.ts +9 -0
  125. package/dist/shaders/EngineUniform.d.ts.map +1 -0
  126. package/dist/shaders/IShader.d.ts +15 -0
  127. package/dist/shaders/IShader.d.ts.map +1 -0
  128. package/dist/shaders/QuadShader.d.ts +18 -0
  129. package/dist/shaders/QuadShader.d.ts.map +1 -0
  130. package/dist/shaders/ShaderDescriptor.d.ts +7 -0
  131. package/dist/shaders/ShaderDescriptor.d.ts.map +1 -0
  132. package/dist/shaders/mod.d.ts +6 -0
  133. package/dist/shaders/mod.d.ts.map +1 -0
  134. package/dist/shaders/parser.d.ts +8 -0
  135. package/dist/shaders/parser.d.ts.map +1 -0
  136. package/dist/shaders/postprocess/blur.d.ts +3 -0
  137. package/dist/shaders/postprocess/blur.d.ts.map +1 -0
  138. package/dist/shaders/postprocess/mod.d.ts +17 -0
  139. package/dist/shaders/postprocess/mod.d.ts.map +1 -0
  140. package/dist/shaders/samplers.d.ts +3 -0
  141. package/dist/shaders/samplers.d.ts.map +1 -0
  142. package/dist/shaders/wgsl/example.wgsl.d.ts +3 -0
  143. package/dist/shaders/wgsl/example.wgsl.d.ts.map +1 -0
  144. package/dist/shaders/wgsl/hello.wgsl.d.ts +3 -0
  145. package/dist/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
  146. package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts +3 -0
  147. package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
  148. package/dist/shaders/wgsl/quad.wgsl.d.ts +3 -0
  149. package/dist/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
  150. package/dist/src/Toodle.d.ts +303 -0
  151. package/dist/src/Toodle.d.ts.map +1 -0
  152. package/dist/src/colors/mod.d.ts +871 -0
  153. package/dist/src/coreTypes/Color.d.ts +6 -0
  154. package/dist/src/coreTypes/Color.d.ts.map +1 -0
  155. package/dist/src/coreTypes/Point.d.ts +7 -0
  156. package/dist/src/coreTypes/Point.d.ts.map +1 -0
  157. package/dist/src/coreTypes/Size.d.ts +4 -0
  158. package/dist/src/coreTypes/Size.d.ts.map +1 -0
  159. package/dist/src/coreTypes/Transform.d.ts +15 -0
  160. package/dist/src/coreTypes/Transform.d.ts.map +1 -0
  161. package/dist/src/coreTypes/Vec2.d.ts +7 -0
  162. package/dist/src/coreTypes/Vec2.d.ts.map +1 -0
  163. package/dist/src/coreTypes/mod.d.ts +5 -0
  164. package/dist/src/coreTypes/mod.d.ts.map +1 -0
  165. package/dist/src/limits.d.ts +22 -0
  166. package/dist/src/limits.d.ts.map +1 -0
  167. package/dist/src/math/angle.d.ts +12 -0
  168. package/dist/src/math/angle.d.ts.map +1 -0
  169. package/dist/src/math/matrix.d.ts +25 -0
  170. package/dist/src/math/matrix.d.ts.map +1 -0
  171. package/dist/src/math/mod.d.ts +2 -0
  172. package/dist/src/math/mod.d.ts.map +1 -0
  173. package/dist/src/mod.d.ts +16 -0
  174. package/dist/src/mod.d.ts.map +1 -0
  175. package/dist/src/postprocess.d.ts +10 -0
  176. package/dist/src/postprocess.d.ts.map +1 -0
  177. package/dist/src/scene/Batcher.d.ts +19 -0
  178. package/dist/src/scene/Batcher.d.ts.map +1 -0
  179. package/dist/src/scene/Camera.d.ts +15 -0
  180. package/dist/src/scene/Camera.d.ts.map +1 -0
  181. package/dist/src/scene/JumboQuadNode.d.ts +28 -0
  182. package/dist/src/scene/QuadNode.d.ts +158 -0
  183. package/dist/src/scene/QuadNode.d.ts.map +1 -0
  184. package/dist/src/scene/RenderComponent.d.ts +10 -0
  185. package/dist/src/scene/RenderComponent.d.ts.map +1 -0
  186. package/dist/src/scene/SceneNode.d.ts +299 -0
  187. package/dist/src/scene/SceneNode.d.ts.map +1 -0
  188. package/dist/src/scene/mod.d.ts +4 -0
  189. package/dist/src/scene/mod.d.ts.map +1 -0
  190. package/dist/src/screen/mod.d.ts +1 -0
  191. package/dist/src/screen/mod.d.ts.map +1 -0
  192. package/dist/src/screen/resolution.d.ts +4 -0
  193. package/dist/src/screen/resolution.d.ts.map +1 -0
  194. package/dist/src/shaders/EngineUniform.d.ts +8 -0
  195. package/dist/src/shaders/EngineUniform.d.ts.map +1 -0
  196. package/dist/src/shaders/IShader.d.ts +14 -0
  197. package/dist/src/shaders/IShader.d.ts.map +1 -0
  198. package/dist/src/shaders/QuadShader.d.ts +17 -0
  199. package/dist/src/shaders/QuadShader.d.ts.map +1 -0
  200. package/dist/src/shaders/ShaderDescriptor.d.ts +6 -0
  201. package/dist/src/shaders/ShaderDescriptor.d.ts.map +1 -0
  202. package/dist/src/shaders/mod.d.ts +5 -0
  203. package/dist/src/shaders/mod.d.ts.map +1 -0
  204. package/dist/src/shaders/parser.d.ts +7 -0
  205. package/dist/src/shaders/parser.d.ts.map +1 -0
  206. package/dist/src/shaders/postprocess/blur.d.ts +2 -0
  207. package/dist/src/shaders/postprocess/mod.d.ts +16 -0
  208. package/dist/src/shaders/postprocess/postprocess.d.ts +8 -0
  209. package/dist/src/shaders/postprocess/util.d.ts +2 -0
  210. package/dist/src/shaders/samplers.d.ts +2 -0
  211. package/dist/src/shaders/samplers.d.ts.map +1 -0
  212. package/dist/src/shaders/wgsl/example.wgsl.d.ts +2 -0
  213. package/dist/src/shaders/wgsl/example.wgsl.d.ts.map +1 -0
  214. package/dist/src/shaders/wgsl/hello.wgsl.d.ts +2 -0
  215. package/dist/src/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
  216. package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts +2 -0
  217. package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
  218. package/dist/src/shaders/wgsl/quad.wgsl.d.ts +2 -0
  219. package/dist/src/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
  220. package/dist/src/text/FontPipeline.d.ts +13 -0
  221. package/dist/src/text/FontPipeline.d.ts.map +1 -0
  222. package/dist/src/text/MsdfFont.d.ts +81 -0
  223. package/dist/src/text/MsdfFont.d.ts.map +1 -0
  224. package/dist/src/text/TextFormatting.d.ts +18 -0
  225. package/dist/src/text/TextFormatting.d.ts.map +1 -0
  226. package/dist/src/text/TextNode.d.ts +18 -0
  227. package/dist/src/text/TextNode.d.ts.map +1 -0
  228. package/dist/src/text/TextShader.d.ts +14 -0
  229. package/dist/src/text/TextShader.d.ts.map +1 -0
  230. package/dist/src/text/mod.d.ts +3 -0
  231. package/dist/src/text/mod.d.ts.map +1 -0
  232. package/dist/src/text/shaping.d.ts +38 -0
  233. package/dist/src/text/shaping.d.ts.map +1 -0
  234. package/dist/src/text/text.wgsl.d.ts +2 -0
  235. package/dist/src/text/text.wgsl.d.ts.map +1 -0
  236. package/dist/src/textures/AssetManager.d.ts +181 -0
  237. package/dist/src/textures/AssetManager.d.ts.map +1 -0
  238. package/dist/src/textures/NewTextureComputeShader.d.ts +28 -0
  239. package/dist/src/textures/TextureComputeShader.d.ts +20 -0
  240. package/dist/src/textures/TextureComputeShader.d.ts.map +1 -0
  241. package/dist/src/textures/crop.wgsl.d.ts +2 -0
  242. package/dist/src/textures/mod.d.ts +1 -0
  243. package/dist/src/textures/mod.d.ts.map +1 -0
  244. package/dist/src/textures/pixel-scraping.wgsl.d.ts +2 -0
  245. package/dist/src/textures/pixel-scraping.wgsl.d.ts.map +1 -0
  246. package/dist/src/textures/texture-processing.wgsl.d.ts +2 -0
  247. package/dist/src/textures/types.d.ts +176 -0
  248. package/dist/src/textures/types.d.ts.map +1 -0
  249. package/dist/src/textures/util.d.ts +7 -0
  250. package/dist/src/textures/util.d.ts.map +1 -0
  251. package/dist/src/utils/assert.d.ts +1 -0
  252. package/dist/src/utils/assert.d.ts.map +1 -0
  253. package/dist/src/utils/boilerplate.d.ts +10 -0
  254. package/dist/src/utils/boilerplate.d.ts.map +1 -0
  255. package/dist/src/utils/error.d.ts +7 -0
  256. package/dist/src/utils/error.d.ts.map +1 -0
  257. package/dist/src/utils/mod.d.ts +2 -0
  258. package/dist/src/utils/mod.d.ts.map +1 -0
  259. package/dist/src/utils/pool.d.ts +22 -0
  260. package/dist/src/utils/pool.d.ts.map +1 -0
  261. package/dist/test/math/matrix.test.d.ts +1 -0
  262. package/dist/test/scene/Batcher.test.d.ts +1 -0
  263. package/dist/test/scene/SceneNode.test.d.ts +1 -0
  264. package/dist/test/shader/parser.test.d.ts +1 -0
  265. package/dist/text/FontPipeline.d.ts +14 -0
  266. package/dist/text/FontPipeline.d.ts.map +1 -0
  267. package/dist/text/MsdfFont.d.ts +82 -0
  268. package/dist/text/MsdfFont.d.ts.map +1 -0
  269. package/dist/text/TextFormatting.d.ts +19 -0
  270. package/dist/text/TextFormatting.d.ts.map +1 -0
  271. package/dist/text/TextNode.d.ts +19 -0
  272. package/dist/text/TextNode.d.ts.map +1 -0
  273. package/dist/text/TextShader.d.ts +15 -0
  274. package/dist/text/TextShader.d.ts.map +1 -0
  275. package/dist/text/mod.d.ts +4 -0
  276. package/dist/text/mod.d.ts.map +1 -0
  277. package/dist/text/shaping.d.ts +39 -0
  278. package/dist/text/shaping.d.ts.map +1 -0
  279. package/dist/text/text.wgsl.d.ts +3 -0
  280. package/dist/text/text.wgsl.d.ts.map +1 -0
  281. package/dist/textures/AssetManager.d.ts +182 -0
  282. package/dist/textures/AssetManager.d.ts.map +1 -0
  283. package/dist/textures/TextureComputeShader.d.ts +21 -0
  284. package/dist/textures/TextureComputeShader.d.ts.map +1 -0
  285. package/dist/textures/mod.d.ts +2 -0
  286. package/dist/textures/mod.d.ts.map +1 -0
  287. package/dist/textures/pixel-scraping.wgsl.d.ts +3 -0
  288. package/dist/textures/pixel-scraping.wgsl.d.ts.map +1 -0
  289. package/dist/textures/types.d.ts +177 -0
  290. package/dist/textures/types.d.ts.map +1 -0
  291. package/dist/textures/util.d.ts +8 -0
  292. package/dist/textures/util.d.ts.map +1 -0
  293. package/dist/utils/assert.d.ts +2 -0
  294. package/dist/utils/assert.d.ts.map +1 -0
  295. package/dist/utils/boilerplate.d.ts +11 -0
  296. package/dist/utils/boilerplate.d.ts.map +1 -0
  297. package/dist/utils/error.d.ts +8 -0
  298. package/dist/utils/error.d.ts.map +1 -0
  299. package/dist/utils/mod.d.ts +3 -0
  300. package/dist/utils/mod.d.ts.map +1 -0
  301. package/dist/utils/pool.d.ts +23 -0
  302. package/dist/utils/pool.d.ts.map +1 -0
  303. package/package.json +47 -0
  304. package/src/Toodle.ts +853 -0
  305. package/src/colors/mod.ts +151 -0
  306. package/src/coreTypes/Color.ts +1 -0
  307. package/src/coreTypes/Point.ts +7 -0
  308. package/src/coreTypes/Size.ts +4 -0
  309. package/src/coreTypes/Transform.ts +16 -0
  310. package/src/coreTypes/Vec2.ts +7 -0
  311. package/src/coreTypes/mod.ts +5 -0
  312. package/src/globals.d.ts +4 -0
  313. package/src/limits.ts +23 -0
  314. package/src/math/angle.ts +17 -0
  315. package/src/math/matrix.ts +99 -0
  316. package/src/math/mod.ts +2 -0
  317. package/src/mod.ts +22 -0
  318. package/src/scene/Batcher.ts +61 -0
  319. package/src/scene/Camera.ts +69 -0
  320. package/src/scene/JumboQuadNode.ts +219 -0
  321. package/src/scene/QuadNode.ts +403 -0
  322. package/src/scene/RenderComponent.ts +12 -0
  323. package/src/scene/SceneNode.ts +668 -0
  324. package/src/scene/mod.ts +4 -0
  325. package/src/screen/mod.ts +1 -0
  326. package/src/screen/resolution.ts +1 -0
  327. package/src/shaders/EngineUniform.ts +11 -0
  328. package/src/shaders/IShader.ts +20 -0
  329. package/src/shaders/QuadShader.ts +288 -0
  330. package/src/shaders/ShaderDescriptor.ts +6 -0
  331. package/src/shaders/mod.ts +5 -0
  332. package/src/shaders/parser.ts +221 -0
  333. package/src/shaders/postprocess/blur.ts +245 -0
  334. package/src/shaders/postprocess/mod.ts +71 -0
  335. package/src/shaders/samplers.ts +13 -0
  336. package/src/shaders/wgsl/example.wgsl.ts +24 -0
  337. package/src/shaders/wgsl/hello.wgsl.ts +62 -0
  338. package/src/shaders/wgsl/helloInstanced.wgsl.ts +46 -0
  339. package/src/shaders/wgsl/quad.wgsl.ts +140 -0
  340. package/src/text/FontPipeline.ts +212 -0
  341. package/src/text/MsdfFont.ts +190 -0
  342. package/src/text/TextFormatting.ts +28 -0
  343. package/src/text/TextNode.ts +82 -0
  344. package/src/text/TextShader.ts +223 -0
  345. package/src/text/mod.ts +8 -0
  346. package/src/text/shaping.ts +280 -0
  347. package/src/text/text.wgsl.ts +149 -0
  348. package/src/textures/AssetManager.ts +746 -0
  349. package/src/textures/TextureComputeShader.ts +434 -0
  350. package/src/textures/mod.ts +1 -0
  351. package/src/textures/pixel-scraping.wgsl.ts +131 -0
  352. package/src/textures/types.ts +182 -0
  353. package/src/textures/util.ts +352 -0
  354. package/src/utils/assert.ts +5 -0
  355. package/src/utils/boilerplate.ts +110 -0
  356. package/src/utils/error.ts +14 -0
  357. package/src/utils/mod.ts +2 -0
  358. package/src/utils/pool.ts +42 -0
@@ -0,0 +1,434 @@
1
+ import computeShader from "./pixel-scraping.wgsl";
2
+ import type { TextureWithMetadata } from "./types";
3
+
4
+ // Constants
5
+ const BOUNDING_BOX_SIZE = 4 * Uint32Array.BYTES_PER_ELEMENT;
6
+ const WORKGROUP_SIZE = 8;
7
+ const MAX_BOUND = 0xffffffff;
8
+ const MIN_BOUND = 0x00000000;
9
+ const BYTES_PER_PIXEL = 4;
10
+
11
+ /**
12
+ * The data returned by the compute shader that represents the opaque pixels in a texture.
13
+ * Texel coordinates start at 0,0 in the top-left corner of the texture.
14
+ */
15
+ type OpaqueRect = {
16
+ /** The leftmost texel coordinate of the bounding box. */
17
+ texelX: number;
18
+ /** The topmost texel coordinate of the bounding box. */
19
+ texelY: number;
20
+ /** The width of the bounding box in texels. */
21
+ texelWidth: number;
22
+ /** The height of the bounding box in texels. */
23
+ texelHeight: number;
24
+ };
25
+
26
+ /**
27
+ * A GPU-based texture processor that uses compute shaders to:
28
+ * 1. Find the non-transparent bounding box in a texture.
29
+ * 2. Crop the texture to that bounding box.
30
+ * 3. Create a fallback texture if no non-transparent pixels are found.
31
+ */
32
+ export class TextureComputeShader {
33
+ #device: GPUDevice;
34
+ #boundingBuffer: GPUBuffer;
35
+ #cropPipeline: GPUComputePipeline;
36
+ #boundPipeline: GPUComputePipeline;
37
+ #missingTexturePipeline: GPUComputePipeline;
38
+
39
+ constructor(
40
+ device: GPUDevice,
41
+ cropPipeline: GPUComputePipeline,
42
+ boundPipeline: GPUComputePipeline,
43
+ missingTexturePipeline: GPUComputePipeline,
44
+ ) {
45
+ this.#device = device;
46
+ this.#boundPipeline = boundPipeline;
47
+ this.#cropPipeline = cropPipeline;
48
+ this.#missingTexturePipeline = missingTexturePipeline;
49
+
50
+ // Buffer to store the computed bounding box [minX, minY, maxX, maxY]
51
+ this.#boundingBuffer = this.#device.createBuffer({
52
+ size: BOUNDING_BOX_SIZE,
53
+ usage:
54
+ GPUBufferUsage.STORAGE |
55
+ GPUBufferUsage.COPY_SRC |
56
+ GPUBufferUsage.COPY_DST,
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Factory method to initialize pipelines and return an instance of TextureComputeShader.
62
+ */
63
+ static create(device: GPUDevice) {
64
+ const pipelines = createPipelines(device, "TextureComputeShader");
65
+ return new TextureComputeShader(
66
+ device,
67
+ pipelines.cropPipeline,
68
+ pipelines.boundPipeline,
69
+ pipelines.missingTexturePipeline,
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Main entry point to process a texture.
75
+ * Returns a cropped ImageBitmap and metadata.
76
+ */
77
+ async processTexture(
78
+ textureWrapper: TextureWithMetadata,
79
+ ): Promise<TextureWithMetadata> {
80
+ const boundsBindGroup = this.#boundsBindGroup(textureWrapper.texture);
81
+
82
+ const commandEncoder = this.#device.createCommandEncoder();
83
+ const passEncoder = commandEncoder.beginComputePass();
84
+
85
+ const dispatchX = Math.ceil(textureWrapper.texture.width / WORKGROUP_SIZE);
86
+ const dispatchY = Math.ceil(textureWrapper.texture.height / WORKGROUP_SIZE);
87
+
88
+ // Initialize bounding box with max/min values
89
+ const boundsInit = new Uint32Array([
90
+ MAX_BOUND,
91
+ MAX_BOUND,
92
+ MIN_BOUND,
93
+ MIN_BOUND,
94
+ ]);
95
+ this.#device.queue.writeBuffer(
96
+ this.#boundingBuffer,
97
+ 0,
98
+ boundsInit.buffer,
99
+ 0,
100
+ BOUNDING_BOX_SIZE,
101
+ );
102
+
103
+ // Run bounds detection compute shader
104
+ passEncoder.setPipeline(this.#boundPipeline);
105
+ passEncoder.setBindGroup(0, boundsBindGroup);
106
+ passEncoder.dispatchWorkgroups(dispatchX, dispatchY);
107
+ passEncoder.end();
108
+ this.#device.queue.submit([commandEncoder.finish()]);
109
+
110
+ const { texelX, texelY, texelWidth, texelHeight, computeBuffer } =
111
+ await this.#getBoundingBox();
112
+
113
+ // If no non-transparent pixels were found
114
+ if (texelX === MAX_BOUND || texelY === MAX_BOUND) {
115
+ return await this.#createMissingTexture(textureWrapper.texture);
116
+ }
117
+
118
+ // Crop the texture to the computed bounds
119
+ const croppedTexture = await this.#cropTexture(
120
+ texelWidth,
121
+ texelHeight,
122
+ computeBuffer,
123
+ textureWrapper.texture,
124
+ );
125
+
126
+ const leftCrop = texelX;
127
+ const rightCrop = textureWrapper.originalSize.width - texelX - texelWidth;
128
+ const topCrop = texelY;
129
+ const bottomCrop =
130
+ textureWrapper.originalSize.height - texelY - texelHeight;
131
+
132
+ textureWrapper = {
133
+ texture: croppedTexture,
134
+ cropOffset: { x: leftCrop - rightCrop, y: bottomCrop - topCrop },
135
+ originalSize: textureWrapper.originalSize,
136
+ };
137
+
138
+ return textureWrapper;
139
+ }
140
+
141
+ /**
142
+ * Reads the GPU buffer containing the bounding box.
143
+ */
144
+ async #getBoundingBox(): Promise<
145
+ OpaqueRect & { computeBuffer: Uint32Array }
146
+ > {
147
+ const readBuffer = this.#device.createBuffer({
148
+ label: "AABB Compute Buffer",
149
+ size: BOUNDING_BOX_SIZE,
150
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
151
+ });
152
+
153
+ const copyEncoder = this.#device.createCommandEncoder();
154
+ copyEncoder.copyBufferToBuffer(
155
+ this.#boundingBuffer,
156
+ 0,
157
+ readBuffer,
158
+ 0,
159
+ BOUNDING_BOX_SIZE,
160
+ );
161
+ this.#device.queue.submit([copyEncoder.finish()]);
162
+
163
+ await readBuffer.mapAsync(GPUMapMode.READ);
164
+ const computeBuffer = new Uint32Array(readBuffer.getMappedRange().slice(0));
165
+ readBuffer.unmap();
166
+
167
+ const [minX, minY, maxX, maxY] = computeBuffer;
168
+ return {
169
+ texelX: minX,
170
+ texelY: minY,
171
+ texelWidth: maxX - minX + 1,
172
+ texelHeight: maxY - minY + 1,
173
+
174
+ computeBuffer,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Crops the original texture to the specified bounds using a compute shader.
180
+ */
181
+ async #cropTexture(
182
+ croppedWidth: number,
183
+ croppedHeight: number,
184
+ computeBuffer: Uint32Array,
185
+ inputTexture: GPUTexture,
186
+ ) {
187
+ const boundsUniform = this.#device.createBuffer({
188
+ label: "Cropping Bounds Uniform Buffer",
189
+ size: BOUNDING_BOX_SIZE,
190
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
191
+ });
192
+ this.#device.queue.writeBuffer(boundsUniform, 0, computeBuffer);
193
+
194
+ const outputTexture = this.#device.createTexture({
195
+ label: "Cropped Texture",
196
+ size: [croppedWidth, croppedHeight],
197
+ format: "rgba8unorm",
198
+ usage:
199
+ GPUTextureUsage.STORAGE_BINDING |
200
+ GPUTextureUsage.TEXTURE_BINDING |
201
+ GPUTextureUsage.COPY_SRC,
202
+ });
203
+
204
+ const dimensionsOutBuffer = this.#device.createBuffer({
205
+ label: "Cropping Dimensions Output Buffer",
206
+ size: BOUNDING_BOX_SIZE,
207
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
208
+ });
209
+
210
+ const bindGroup = this.#croppingBindGroup(
211
+ inputTexture,
212
+ outputTexture,
213
+ boundsUniform,
214
+ dimensionsOutBuffer,
215
+ );
216
+
217
+ const encoder = this.#device.createCommandEncoder();
218
+ const pass = encoder.beginComputePass();
219
+ pass.setPipeline(this.#cropPipeline);
220
+ pass.setBindGroup(0, bindGroup);
221
+ pass.dispatchWorkgroups(
222
+ Math.ceil(croppedWidth / WORKGROUP_SIZE),
223
+ Math.ceil(croppedHeight / WORKGROUP_SIZE),
224
+ );
225
+ pass.end();
226
+ this.#device.queue.submit([encoder.finish()]);
227
+
228
+ return outputTexture;
229
+ }
230
+
231
+ /**
232
+ * Creates a fallback placeholder texture if the input is fully transparent.
233
+ */
234
+ async #createMissingTexture(
235
+ inputTexture: GPUTexture,
236
+ ): Promise<TextureWithMetadata> {
237
+ const placeholder = this.#device.createTexture({
238
+ label: "Missing Placeholder Texture",
239
+ size: [inputTexture.width, inputTexture.height],
240
+ format: "rgba8unorm",
241
+ usage:
242
+ GPUTextureUsage.COPY_SRC |
243
+ GPUTextureUsage.COPY_DST |
244
+ GPUTextureUsage.STORAGE_BINDING |
245
+ GPUTextureUsage.TEXTURE_BINDING,
246
+ });
247
+
248
+ const encoder = this.#device.createCommandEncoder();
249
+ const pass = encoder.beginComputePass();
250
+ pass.setPipeline(this.#missingTexturePipeline);
251
+ pass.setBindGroup(0, this.#missingTextureBindGroup(placeholder));
252
+ pass.dispatchWorkgroups(placeholder.width / 8, placeholder.height / 8);
253
+ pass.end();
254
+ this.#device.queue.submit([encoder.finish()]);
255
+
256
+ return {
257
+ texture: placeholder,
258
+ cropOffset: { x: 0, y: 0 },
259
+ originalSize: { width: inputTexture.width, height: inputTexture.height },
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Converts a GPUTexture to an ImageBitmap for display or further use.
265
+ */
266
+ async #textureToBitmap(
267
+ texture: GPUTexture,
268
+ width: number,
269
+ height: number,
270
+ ): Promise<ImageBitmap> {
271
+ const paddedBytesPerRow = Math.ceil((width * BYTES_PER_PIXEL) / 256) * 256;
272
+ const bufferSize = paddedBytesPerRow * height;
273
+
274
+ const readBuffer = this.#device.createBuffer({
275
+ size: bufferSize,
276
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
277
+ });
278
+
279
+ const encoder = this.#device.createCommandEncoder();
280
+ encoder.copyTextureToBuffer(
281
+ { texture },
282
+ { buffer: readBuffer, bytesPerRow: paddedBytesPerRow },
283
+ { width, height, depthOrArrayLayers: 1 },
284
+ );
285
+ this.#device.queue.submit([encoder.finish()]);
286
+
287
+ await readBuffer.mapAsync(GPUMapMode.READ);
288
+ const raw = readBuffer.getMappedRange();
289
+ const rawArray = new Uint8Array(raw);
290
+
291
+ const pixelData = new Uint8ClampedArray(width * height * 4);
292
+ for (let y = 0; y < height; y++) {
293
+ const src = y * paddedBytesPerRow;
294
+ const dst = y * width * 4;
295
+ pixelData.set(rawArray.subarray(src, src + width * 4), dst);
296
+ }
297
+ readBuffer.unmap();
298
+
299
+ const canvas = document.createElement("canvas");
300
+ canvas.width = width;
301
+ canvas.height = height;
302
+ const ctx = canvas.getContext("2d")!;
303
+ ctx.putImageData(new ImageData(pixelData, width, height), 0, 0);
304
+
305
+ return await createImageBitmap(canvas);
306
+ }
307
+
308
+ // Bind group helpers
309
+
310
+ #boundsBindGroup(inputTexture: GPUTexture): GPUBindGroup {
311
+ return this.#device.createBindGroup({
312
+ layout: this.#boundPipeline.getBindGroupLayout(0),
313
+ entries: [
314
+ { binding: 0, resource: inputTexture.createView() },
315
+ { binding: 1, resource: { buffer: this.#boundingBuffer } },
316
+ ],
317
+ });
318
+ }
319
+
320
+ #croppingBindGroup(
321
+ inputTexture: GPUTexture,
322
+ outputTexture: GPUTexture,
323
+ boundsUniform: GPUBuffer,
324
+ dimensionsOutBuffer: GPUBuffer,
325
+ ): GPUBindGroup {
326
+ return this.#device.createBindGroup({
327
+ layout: this.#cropPipeline.getBindGroupLayout(0),
328
+ entries: [
329
+ { binding: 0, resource: inputTexture.createView() },
330
+ { binding: 1, resource: outputTexture.createView() },
331
+ { binding: 2, resource: { buffer: boundsUniform } },
332
+ { binding: 3, resource: { buffer: dimensionsOutBuffer } },
333
+ ],
334
+ });
335
+ }
336
+
337
+ #missingTextureBindGroup(outputTexture: GPUTexture): GPUBindGroup {
338
+ return this.#device.createBindGroup({
339
+ layout: this.#missingTexturePipeline.getBindGroupLayout(0),
340
+ entries: [{ binding: 0, resource: outputTexture.createView() }],
341
+ });
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Creates compute pipelines for bounding box detection, cropping, and fallback texture generation.
347
+ */
348
+ function createPipelines(device: GPUDevice, label: string) {
349
+ const shader = device.createShaderModule({
350
+ label: `${label} Shader`,
351
+ code: computeShader,
352
+ });
353
+
354
+ const findBoundsBindGroupLayout = device.createBindGroupLayout({
355
+ label: "Bounds Detection Layout",
356
+ entries: [
357
+ {
358
+ binding: 0,
359
+ visibility: GPUShaderStage.COMPUTE,
360
+ texture: { sampleType: "float", viewDimension: "2d" },
361
+ },
362
+ {
363
+ binding: 1,
364
+ visibility: GPUShaderStage.COMPUTE,
365
+ buffer: { type: "storage" },
366
+ },
367
+ ],
368
+ });
369
+
370
+ const cropBindGroupLayout = device.createBindGroupLayout({
371
+ label: "Cropping Layout",
372
+ entries: [
373
+ { binding: 0, visibility: GPUShaderStage.COMPUTE, texture: {} },
374
+ {
375
+ binding: 1,
376
+ visibility: GPUShaderStage.COMPUTE,
377
+ storageTexture: {
378
+ access: "write-only",
379
+ format: "rgba8unorm",
380
+ viewDimension: "2d",
381
+ },
382
+ },
383
+ {
384
+ binding: 2,
385
+ visibility: GPUShaderStage.COMPUTE,
386
+ buffer: { type: "uniform" },
387
+ },
388
+ {
389
+ binding: 3,
390
+ visibility: GPUShaderStage.COMPUTE,
391
+ buffer: { type: "storage" },
392
+ },
393
+ ],
394
+ });
395
+
396
+ const missingTextureBindGroupLayout = device.createBindGroupLayout({
397
+ label: "Missing Texture Layout",
398
+ entries: [
399
+ {
400
+ binding: 0,
401
+ visibility: GPUShaderStage.COMPUTE,
402
+ storageTexture: {
403
+ access: "write-only",
404
+ format: "rgba8unorm",
405
+ viewDimension: "2d",
406
+ },
407
+ },
408
+ ],
409
+ });
410
+
411
+ return {
412
+ boundPipeline: device.createComputePipeline({
413
+ label: `${label} - Find Bounds Pipeline`,
414
+ layout: device.createPipelineLayout({
415
+ bindGroupLayouts: [findBoundsBindGroupLayout],
416
+ }),
417
+ compute: { module: shader, entryPoint: "find_bounds" },
418
+ }),
419
+ cropPipeline: device.createComputePipeline({
420
+ label: `${label} - Crop Pipeline`,
421
+ layout: device.createPipelineLayout({
422
+ bindGroupLayouts: [cropBindGroupLayout],
423
+ }),
424
+ compute: { module: shader, entryPoint: "crop_and_output" },
425
+ }),
426
+ missingTexturePipeline: device.createComputePipeline({
427
+ label: `${label} - Missing Texture Pipeline`,
428
+ layout: device.createPipelineLayout({
429
+ bindGroupLayouts: [missingTextureBindGroupLayout],
430
+ }),
431
+ compute: { module: shader, entryPoint: "missing_texture" },
432
+ }),
433
+ };
434
+ }
@@ -0,0 +1 @@
1
+ export type * from "./types";
@@ -0,0 +1,131 @@
1
+ export default /*wgsl*/ `
2
+ // ==============================
3
+ // === BOUNDING BOX PASS =======
4
+ // ==============================
5
+
6
+ // Input texture from which to compute the non-transparent bounding box
7
+ @group(0) @binding(0)
8
+ var input_texture: texture_2d<f32>;
9
+
10
+ // Atomic bounding box storage structure
11
+ struct bounding_box_atomic {
12
+ min_x: atomic<u32>,
13
+ min_y: atomic<u32>,
14
+ max_x: atomic<u32>,
15
+ max_y: atomic<u32>,
16
+ };
17
+
18
+ // Storage buffer to hold atomic bounding box updates
19
+ @group(0) @binding(1)
20
+ var<storage, read_write> bounds: bounding_box_atomic;
21
+
22
+ // Compute shader to find the bounding box of non-transparent pixels
23
+ @compute @workgroup_size(8, 8)
24
+ fn find_bounds(@builtin(global_invocation_id) gid: vec3<u32>) {
25
+ let size = textureDimensions(input_texture).xy;
26
+ if (gid.x >= size.x || gid.y >= size.y) {
27
+ return;
28
+ }
29
+
30
+ let pixel = textureLoad(input_texture, vec2<i32>(gid.xy), 0);
31
+ if (pixel.a > 0.0) {
32
+ atomicMin(&bounds.min_x, gid.x);
33
+ atomicMin(&bounds.min_y, gid.y);
34
+ atomicMax(&bounds.max_x, gid.x);
35
+ atomicMax(&bounds.max_y, gid.y);
36
+ }
37
+ }
38
+
39
+ // ==============================
40
+ // === CROP + OUTPUT PASS ======
41
+ // ==============================
42
+
43
+ // Input texture from which cropped data is read
44
+ @group(0) @binding(0)
45
+ var input_texture_crop: texture_2d<f32>;
46
+
47
+ // Output texture where cropped image is written
48
+ @group(0) @binding(1)
49
+ var output_texture: texture_storage_2d<rgba8unorm, write>;
50
+
51
+ // Bounding box passed in as a uniform (not atomic anymore)
52
+ struct bounding_box_uniform {
53
+ min_x: u32,
54
+ min_y: u32,
55
+ max_x: u32,
56
+ max_y: u32,
57
+ };
58
+
59
+ @group(0) @binding(2)
60
+ var<uniform> bounds_uniform: bounding_box_uniform;
61
+
62
+ // Struct to store both original and cropped texture dimensions
63
+ struct image_dimensions {
64
+ original_width: u32,
65
+ original_height: u32,
66
+ cropped_width: u32,
67
+ cropped_height: u32,
68
+ };
69
+
70
+ // Storage buffer to output the result dimensions
71
+ @group(0) @binding(3)
72
+ var<storage, read_write> dimensions_out: image_dimensions;
73
+
74
+ // Compute shader to crop the input texture to the bounding box and output it
75
+ @compute @workgroup_size(8, 8)
76
+ fn crop_and_output(@builtin(global_invocation_id) gid: vec3<u32>) {
77
+ let size = textureDimensions(input_texture_crop).xy;
78
+
79
+ let crop_width = bounds_uniform.max_x - bounds_uniform.min_x + 1u;
80
+ let crop_height = bounds_uniform.max_y - bounds_uniform.min_y + 1u;
81
+
82
+ if (gid.x >= crop_width || gid.y >= crop_height) {
83
+ return;
84
+ }
85
+
86
+ let src_coord = vec2<i32>(
87
+ i32(bounds_uniform.min_x + gid.x),
88
+ i32(bounds_uniform.min_y + gid.y)
89
+ );
90
+
91
+ let dst_coord = vec2<i32>(i32(gid.x), i32(gid.y));
92
+ let pixel = textureLoad(input_texture_crop, src_coord, 0);
93
+ textureStore(output_texture, dst_coord, pixel);
94
+
95
+ // Output dimensions from workgroup (0,0) only
96
+ if (gid.x == 0u && gid.y == 0u) {
97
+ dimensions_out.original_width = size.x;
98
+ dimensions_out.original_height = size.y;
99
+ dimensions_out.cropped_width = crop_width;
100
+ dimensions_out.cropped_height = crop_height;
101
+ }
102
+ }
103
+
104
+ // ==============================
105
+ // === MISSING TEXTURE FILL ====
106
+ // ==============================
107
+
108
+ // Output texture to draw a fallback checkerboard
109
+ @group(0) @binding(0)
110
+ var checker_texture: texture_storage_2d<rgba8unorm, write>;
111
+
112
+ // Compute shader to fill a texture with a purple & green checkerboard
113
+ @compute @workgroup_size(8, 8)
114
+ fn missing_texture(@builtin(global_invocation_id) id: vec3<u32>) {
115
+ let size = textureDimensions(checker_texture);
116
+ if (id.x >= size.x || id.y >= size.y) {
117
+ return;
118
+ }
119
+
120
+ let checker_size = 25u;
121
+ let on_color = ((id.x / checker_size + id.y / checker_size) % 2u) == 0u;
122
+
123
+ let color = select(
124
+ vec4<f32>(0.5, 0.0, 0.5, 1.0), // Purple
125
+ vec4<f32>(0.0, 1.0, 0.0, 1.0), // Green
126
+ on_color
127
+ );
128
+
129
+ textureStore(checker_texture, vec2<i32>(id.xy), color);
130
+ }
131
+ `;