@kurot/core 1.0.13 → 1.0.15

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 (294) hide show
  1. package/README.md +63 -1
  2. package/dist/kurot/display/Bitmap.d.ts +0 -1
  3. package/dist/kurot/display/Bitmap.d.ts.map +1 -1
  4. package/dist/kurot/display/Bitmap.js +0 -3
  5. package/dist/kurot/display/Bitmap.js.map +1 -1
  6. package/dist/kurot/display/DisplayObject.d.ts +16 -35
  7. package/dist/kurot/display/DisplayObject.d.ts.map +1 -1
  8. package/dist/kurot/display/DisplayObject.js +6 -41
  9. package/dist/kurot/display/DisplayObject.js.map +1 -1
  10. package/dist/kurot/display/DisplayObjectContainer.d.ts +0 -10
  11. package/dist/kurot/display/DisplayObjectContainer.d.ts.map +1 -1
  12. package/dist/kurot/display/DisplayObjectContainer.js +0 -14
  13. package/dist/kurot/display/DisplayObjectContainer.js.map +1 -1
  14. package/dist/kurot/display/Graphics.d.ts +0 -1
  15. package/dist/kurot/display/Graphics.d.ts.map +1 -1
  16. package/dist/kurot/display/Graphics.js +0 -3
  17. package/dist/kurot/display/Graphics.js.map +1 -1
  18. package/dist/kurot/display/Mesh.d.ts +9 -3
  19. package/dist/kurot/display/Mesh.d.ts.map +1 -1
  20. package/dist/kurot/display/Mesh.js +9 -3
  21. package/dist/kurot/display/Mesh.js.map +1 -1
  22. package/dist/kurot/display/Stage.d.ts +0 -1
  23. package/dist/kurot/display/Stage.d.ts.map +1 -1
  24. package/dist/kurot/display/Stage.js +0 -2
  25. package/dist/kurot/display/Stage.js.map +1 -1
  26. package/dist/kurot/display/enums/BlendMode.d.ts.map +1 -1
  27. package/dist/kurot/display/enums/BlendMode.js +0 -2
  28. package/dist/kurot/display/enums/BlendMode.js.map +1 -1
  29. package/dist/kurot/display/texture/BitmapData.d.ts +1 -0
  30. package/dist/kurot/display/texture/BitmapData.d.ts.map +1 -1
  31. package/dist/kurot/display/texture/BitmapData.js +2 -0
  32. package/dist/kurot/display/texture/BitmapData.js.map +1 -1
  33. package/dist/kurot/display/texture/RenderTexture.d.ts +0 -1
  34. package/dist/kurot/display/texture/RenderTexture.d.ts.map +1 -1
  35. package/dist/kurot/display/texture/RenderTexture.js +0 -4
  36. package/dist/kurot/display/texture/RenderTexture.js.map +1 -1
  37. package/dist/kurot/display/texture/Texture.d.ts +9 -3
  38. package/dist/kurot/display/texture/Texture.d.ts.map +1 -1
  39. package/dist/kurot/display/texture/Texture.js +9 -3
  40. package/dist/kurot/display/texture/Texture.js.map +1 -1
  41. package/dist/kurot/events/Event.d.ts +3 -1
  42. package/dist/kurot/events/Event.d.ts.map +1 -1
  43. package/dist/kurot/events/Event.js +0 -2
  44. package/dist/kurot/events/Event.js.map +1 -1
  45. package/dist/kurot/events/EventDispatcher.d.ts.map +1 -1
  46. package/dist/kurot/events/EventDispatcher.js +0 -2
  47. package/dist/kurot/events/EventDispatcher.js.map +1 -1
  48. package/dist/kurot/events/TimerEvent.d.ts +0 -1
  49. package/dist/kurot/events/TimerEvent.d.ts.map +1 -1
  50. package/dist/kurot/events/TimerEvent.js +0 -1
  51. package/dist/kurot/events/TimerEvent.js.map +1 -1
  52. package/dist/kurot/events/TouchEvent.d.ts +0 -1
  53. package/dist/kurot/events/TouchEvent.d.ts.map +1 -1
  54. package/dist/kurot/events/TouchEvent.js +0 -1
  55. package/dist/kurot/events/TouchEvent.js.map +1 -1
  56. package/dist/kurot/filters/BlurFilter.d.ts.map +1 -1
  57. package/dist/kurot/filters/BlurFilter.js +0 -3
  58. package/dist/kurot/filters/BlurFilter.js.map +1 -1
  59. package/dist/kurot/filters/DropShadowFilter.d.ts.map +1 -1
  60. package/dist/kurot/filters/DropShadowFilter.js +0 -2
  61. package/dist/kurot/filters/DropShadowFilter.js.map +1 -1
  62. package/dist/kurot/geom/Matrix.d.ts +3 -9
  63. package/dist/kurot/geom/Matrix.d.ts.map +1 -1
  64. package/dist/kurot/geom/Matrix.js +3 -9
  65. package/dist/kurot/geom/Matrix.js.map +1 -1
  66. package/dist/kurot/geom/Point.d.ts +3 -1
  67. package/dist/kurot/geom/Point.d.ts.map +1 -1
  68. package/dist/kurot/geom/Point.js +3 -1
  69. package/dist/kurot/geom/Point.js.map +1 -1
  70. package/dist/kurot/geom/Rectangle.d.ts +3 -3
  71. package/dist/kurot/geom/Rectangle.d.ts.map +1 -1
  72. package/dist/kurot/geom/Rectangle.js +3 -3
  73. package/dist/kurot/geom/Rectangle.js.map +1 -1
  74. package/dist/kurot/media/Sound.d.ts.map +1 -1
  75. package/dist/kurot/media/Sound.js +1 -8
  76. package/dist/kurot/media/Sound.js.map +1 -1
  77. package/dist/kurot/media/SoundChannel.d.ts +0 -1
  78. package/dist/kurot/media/SoundChannel.d.ts.map +1 -1
  79. package/dist/kurot/media/SoundChannel.js +0 -3
  80. package/dist/kurot/media/SoundChannel.js.map +1 -1
  81. package/dist/kurot/media/Video.d.ts +21 -17
  82. package/dist/kurot/media/Video.d.ts.map +1 -1
  83. package/dist/kurot/media/Video.js +137 -106
  84. package/dist/kurot/media/Video.js.map +1 -1
  85. package/dist/kurot/player/InstructionSet.d.ts +4 -17
  86. package/dist/kurot/player/InstructionSet.d.ts.map +1 -1
  87. package/dist/kurot/player/InstructionSet.js +3 -20
  88. package/dist/kurot/player/InstructionSet.js.map +1 -1
  89. package/dist/kurot/player/KurotOptions.d.ts +24 -9
  90. package/dist/kurot/player/KurotOptions.d.ts.map +1 -1
  91. package/dist/kurot/player/Player.d.ts +15 -9
  92. package/dist/kurot/player/Player.d.ts.map +1 -1
  93. package/dist/kurot/player/Player.js +20 -22
  94. package/dist/kurot/player/Player.js.map +1 -1
  95. package/dist/kurot/player/RenderBuffer.d.ts +4 -14
  96. package/dist/kurot/player/RenderBuffer.d.ts.map +1 -1
  97. package/dist/kurot/player/RenderContext.d.ts +11 -27
  98. package/dist/kurot/player/RenderContext.d.ts.map +1 -1
  99. package/dist/kurot/player/RenderPipe.d.ts +3 -13
  100. package/dist/kurot/player/RenderPipe.d.ts.map +1 -1
  101. package/dist/kurot/player/ScreenAdapter.d.ts +6 -5
  102. package/dist/kurot/player/ScreenAdapter.d.ts.map +1 -1
  103. package/dist/kurot/player/ScreenAdapter.js +6 -15
  104. package/dist/kurot/player/ScreenAdapter.js.map +1 -1
  105. package/dist/kurot/player/SystemTicker.d.ts +13 -9
  106. package/dist/kurot/player/SystemTicker.d.ts.map +1 -1
  107. package/dist/kurot/player/SystemTicker.js +10 -13
  108. package/dist/kurot/player/SystemTicker.js.map +1 -1
  109. package/dist/kurot/player/TouchHandler.d.ts +3 -4
  110. package/dist/kurot/player/TouchHandler.d.ts.map +1 -1
  111. package/dist/kurot/player/TouchHandler.js +3 -12
  112. package/dist/kurot/player/TouchHandler.js.map +1 -1
  113. package/dist/kurot/player/canvas/CanvasBuffer.d.ts.map +1 -1
  114. package/dist/kurot/player/canvas/CanvasBuffer.js +0 -3
  115. package/dist/kurot/player/canvas/CanvasBuffer.js.map +1 -1
  116. package/dist/kurot/player/canvas/CanvasRenderer.d.ts +9 -17
  117. package/dist/kurot/player/canvas/CanvasRenderer.d.ts.map +1 -1
  118. package/dist/kurot/player/canvas/CanvasRenderer.js +182 -74
  119. package/dist/kurot/player/canvas/CanvasRenderer.js.map +1 -1
  120. package/dist/kurot/player/canvas/DisplayList.d.ts +1 -5
  121. package/dist/kurot/player/canvas/DisplayList.d.ts.map +1 -1
  122. package/dist/kurot/player/canvas/DisplayList.js +1 -5
  123. package/dist/kurot/player/canvas/DisplayList.js.map +1 -1
  124. package/dist/kurot/player/createPlayer.d.ts.map +1 -1
  125. package/dist/kurot/player/createPlayer.js +0 -2
  126. package/dist/kurot/player/createPlayer.js.map +1 -1
  127. package/dist/kurot/player/pipes/BitmapPipe.d.ts +3 -9
  128. package/dist/kurot/player/pipes/BitmapPipe.d.ts.map +1 -1
  129. package/dist/kurot/player/pipes/BitmapPipe.js +19 -33
  130. package/dist/kurot/player/pipes/BitmapPipe.js.map +1 -1
  131. package/dist/kurot/player/pipes/FilterPipe.d.ts +11 -28
  132. package/dist/kurot/player/pipes/FilterPipe.d.ts.map +1 -1
  133. package/dist/kurot/player/pipes/FilterPipe.js +8 -51
  134. package/dist/kurot/player/pipes/FilterPipe.js.map +1 -1
  135. package/dist/kurot/player/pipes/GraphicsPipe.d.ts +2 -7
  136. package/dist/kurot/player/pipes/GraphicsPipe.d.ts.map +1 -1
  137. package/dist/kurot/player/pipes/GraphicsPipe.js +28 -38
  138. package/dist/kurot/player/pipes/GraphicsPipe.js.map +1 -1
  139. package/dist/kurot/player/pipes/MaskPipe.d.ts +9 -14
  140. package/dist/kurot/player/pipes/MaskPipe.d.ts.map +1 -1
  141. package/dist/kurot/player/pipes/MaskPipe.js +24 -46
  142. package/dist/kurot/player/pipes/MaskPipe.js.map +1 -1
  143. package/dist/kurot/player/pipes/MeshPipe.d.ts +4 -1
  144. package/dist/kurot/player/pipes/MeshPipe.d.ts.map +1 -1
  145. package/dist/kurot/player/pipes/MeshPipe.js +20 -22
  146. package/dist/kurot/player/pipes/MeshPipe.js.map +1 -1
  147. package/dist/kurot/player/pipes/ParticlePipe.d.ts +1 -2
  148. package/dist/kurot/player/pipes/ParticlePipe.d.ts.map +1 -1
  149. package/dist/kurot/player/pipes/ParticlePipe.js +6 -14
  150. package/dist/kurot/player/pipes/ParticlePipe.js.map +1 -1
  151. package/dist/kurot/player/pipes/TextPipe.d.ts +3 -23
  152. package/dist/kurot/player/pipes/TextPipe.d.ts.map +1 -1
  153. package/dist/kurot/player/pipes/TextPipe.js +31 -74
  154. package/dist/kurot/player/pipes/TextPipe.js.map +1 -1
  155. package/dist/kurot/player/webgl/MultiTextureBatcher.d.ts +1 -12
  156. package/dist/kurot/player/webgl/MultiTextureBatcher.d.ts.map +1 -1
  157. package/dist/kurot/player/webgl/MultiTextureBatcher.js +2 -11
  158. package/dist/kurot/player/webgl/MultiTextureBatcher.js.map +1 -1
  159. package/dist/kurot/player/webgl/WebGLDrawCmdManager.d.ts +4 -4
  160. package/dist/kurot/player/webgl/WebGLDrawCmdManager.d.ts.map +1 -1
  161. package/dist/kurot/player/webgl/WebGLDrawCmdManager.js +11 -10
  162. package/dist/kurot/player/webgl/WebGLDrawCmdManager.js.map +1 -1
  163. package/dist/kurot/player/webgl/WebGLProgram.d.ts.map +1 -1
  164. package/dist/kurot/player/webgl/WebGLProgram.js.map +1 -1
  165. package/dist/kurot/player/webgl/WebGLRenderBuffer.d.ts +2 -0
  166. package/dist/kurot/player/webgl/WebGLRenderBuffer.d.ts.map +1 -1
  167. package/dist/kurot/player/webgl/WebGLRenderBuffer.js +9 -6
  168. package/dist/kurot/player/webgl/WebGLRenderBuffer.js.map +1 -1
  169. package/dist/kurot/player/webgl/WebGLRenderContext.d.ts +15 -43
  170. package/dist/kurot/player/webgl/WebGLRenderContext.d.ts.map +1 -1
  171. package/dist/kurot/player/webgl/WebGLRenderContext.js +39 -116
  172. package/dist/kurot/player/webgl/WebGLRenderContext.js.map +1 -1
  173. package/dist/kurot/player/webgl/WebGLRenderTarget.d.ts +2 -0
  174. package/dist/kurot/player/webgl/WebGLRenderTarget.d.ts.map +1 -1
  175. package/dist/kurot/player/webgl/WebGLRenderTarget.js +6 -0
  176. package/dist/kurot/player/webgl/WebGLRenderTarget.js.map +1 -1
  177. package/dist/kurot/player/webgl/WebGLRenderer.d.ts +3 -147
  178. package/dist/kurot/player/webgl/WebGLRenderer.d.ts.map +1 -1
  179. package/dist/kurot/player/webgl/WebGLRenderer.js +6 -210
  180. package/dist/kurot/player/webgl/WebGLRenderer.js.map +1 -1
  181. package/dist/kurot/player/webgl/WebGLUtils.d.ts +4 -4
  182. package/dist/kurot/player/webgl/WebGLUtils.d.ts.map +1 -1
  183. package/dist/kurot/player/webgl/WebGLUtils.js +3 -2
  184. package/dist/kurot/player/webgl/WebGLUtils.js.map +1 -1
  185. package/dist/kurot/player/webgl/WebGLVertexArrayObject.d.ts +6 -2
  186. package/dist/kurot/player/webgl/WebGLVertexArrayObject.d.ts.map +1 -1
  187. package/dist/kurot/player/webgl/WebGLVertexArrayObject.js +6 -11
  188. package/dist/kurot/player/webgl/WebGLVertexArrayObject.js.map +1 -1
  189. package/dist/kurot/player/webgl/shaders/ShaderLib.d.ts +1 -2
  190. package/dist/kurot/player/webgl/shaders/ShaderLib.d.ts.map +1 -1
  191. package/dist/kurot/player/webgl/shaders/ShaderLib.js +1 -6
  192. package/dist/kurot/player/webgl/shaders/ShaderLib.js.map +1 -1
  193. package/dist/kurot/player/webgl/shaders/ShaderLib2.d.ts +1 -2
  194. package/dist/kurot/player/webgl/shaders/ShaderLib2.d.ts.map +1 -1
  195. package/dist/kurot/player/webgl/shaders/ShaderLib2.js +1 -13
  196. package/dist/kurot/player/webgl/shaders/ShaderLib2.js.map +1 -1
  197. package/dist/kurot/player/webgl/shaders/index.d.ts.map +1 -1
  198. package/dist/kurot/player/webgl/shaders/index.js +0 -2
  199. package/dist/kurot/player/webgl/shaders/index.js.map +1 -1
  200. package/dist/kurot/resource/Resource.d.ts +1 -1
  201. package/dist/kurot/resource/Resource.d.ts.map +1 -1
  202. package/dist/kurot/resource/Resource.js +1 -12
  203. package/dist/kurot/resource/Resource.js.map +1 -1
  204. package/dist/kurot/resource/ResourceEvent.d.ts +15 -5
  205. package/dist/kurot/resource/ResourceEvent.d.ts.map +1 -1
  206. package/dist/kurot/resource/ResourceLoader.d.ts +15 -24
  207. package/dist/kurot/resource/ResourceLoader.d.ts.map +1 -1
  208. package/dist/kurot/resource/ResourceLoader.js +17 -35
  209. package/dist/kurot/resource/ResourceLoader.js.map +1 -1
  210. package/dist/kurot/resource/analyzers/AnalyzerBase.d.ts +3 -4
  211. package/dist/kurot/resource/analyzers/AnalyzerBase.d.ts.map +1 -1
  212. package/dist/kurot/resource/analyzers/AnalyzerBase.js +3 -5
  213. package/dist/kurot/resource/analyzers/AnalyzerBase.js.map +1 -1
  214. package/dist/kurot/resource/analyzers/ImageAnalyzer.d.ts.map +1 -1
  215. package/dist/kurot/resource/analyzers/ImageAnalyzer.js +0 -1
  216. package/dist/kurot/resource/analyzers/ImageAnalyzer.js.map +1 -1
  217. package/dist/kurot/resource/analyzers/SheetAnalyzer.d.ts +6 -5
  218. package/dist/kurot/resource/analyzers/SheetAnalyzer.d.ts.map +1 -1
  219. package/dist/kurot/resource/analyzers/SheetAnalyzer.js +6 -12
  220. package/dist/kurot/resource/analyzers/SheetAnalyzer.js.map +1 -1
  221. package/dist/kurot/resource/analyzers/SoundAnalyzer.d.ts.map +1 -1
  222. package/dist/kurot/resource/analyzers/SoundAnalyzer.js +0 -1
  223. package/dist/kurot/resource/analyzers/SoundAnalyzer.js.map +1 -1
  224. package/dist/kurot/resource/index.d.ts.map +1 -1
  225. package/dist/kurot/resource/index.js +0 -3
  226. package/dist/kurot/resource/index.js.map +1 -1
  227. package/dist/kurot/system/Capabilities.d.ts +11 -13
  228. package/dist/kurot/system/Capabilities.d.ts.map +1 -1
  229. package/dist/kurot/system/Capabilities.js +12 -18
  230. package/dist/kurot/system/Capabilities.js.map +1 -1
  231. package/dist/kurot/text/BitmapFont.d.ts +0 -1
  232. package/dist/kurot/text/BitmapFont.d.ts.map +1 -1
  233. package/dist/kurot/text/BitmapFont.js +0 -2
  234. package/dist/kurot/text/BitmapFont.js.map +1 -1
  235. package/dist/kurot/text/BitmapText.d.ts +0 -5
  236. package/dist/kurot/text/BitmapText.d.ts.map +1 -1
  237. package/dist/kurot/text/BitmapText.js +0 -6
  238. package/dist/kurot/text/BitmapText.js.map +1 -1
  239. package/dist/kurot/text/HtmlTextParser.d.ts +3 -1
  240. package/dist/kurot/text/HtmlTextParser.d.ts.map +1 -1
  241. package/dist/kurot/text/HtmlTextParser.js +3 -1
  242. package/dist/kurot/text/HtmlTextParser.js.map +1 -1
  243. package/dist/kurot/text/InputController.d.ts.map +1 -1
  244. package/dist/kurot/text/InputController.js +0 -2
  245. package/dist/kurot/text/InputController.js.map +1 -1
  246. package/dist/kurot/text/StageText.d.ts +1 -20
  247. package/dist/kurot/text/StageText.d.ts.map +1 -1
  248. package/dist/kurot/text/StageText.js +2 -46
  249. package/dist/kurot/text/StageText.js.map +1 -1
  250. package/dist/kurot/text/TextField.d.ts +0 -12
  251. package/dist/kurot/text/TextField.d.ts.map +1 -1
  252. package/dist/kurot/text/TextField.js +0 -41
  253. package/dist/kurot/text/TextField.js.map +1 -1
  254. package/dist/kurot/text/WordWrap.d.ts +0 -1
  255. package/dist/kurot/text/WordWrap.d.ts.map +1 -1
  256. package/dist/kurot/text/WordWrap.js +0 -3
  257. package/dist/kurot/text/WordWrap.js.map +1 -1
  258. package/dist/kurot/utils/Base64Util.d.ts +1 -6
  259. package/dist/kurot/utils/Base64Util.d.ts.map +1 -1
  260. package/dist/kurot/utils/Base64Util.js +1 -7
  261. package/dist/kurot/utils/Base64Util.js.map +1 -1
  262. package/dist/kurot/utils/DebugLog.js +0 -1
  263. package/dist/kurot/utils/DebugLog.js.map +1 -1
  264. package/dist/kurot/utils/FontManager.d.ts.map +1 -1
  265. package/dist/kurot/utils/FontManager.js +3 -1
  266. package/dist/kurot/utils/FontManager.js.map +1 -1
  267. package/dist/kurot/utils/NumberUtils.d.ts.map +1 -1
  268. package/dist/kurot/utils/NumberUtils.js +0 -1
  269. package/dist/kurot/utils/NumberUtils.js.map +1 -1
  270. package/package.json +17 -5
  271. package/dist/kurot/benchmark/BenchmarkRunner.d.ts +0 -60
  272. package/dist/kurot/benchmark/BenchmarkRunner.d.ts.map +0 -1
  273. package/dist/kurot/benchmark/BenchmarkRunner.js +0 -112
  274. package/dist/kurot/benchmark/BenchmarkRunner.js.map +0 -1
  275. package/dist/kurot/benchmark/MetricsCollector.d.ts +0 -34
  276. package/dist/kurot/benchmark/MetricsCollector.d.ts.map +0 -1
  277. package/dist/kurot/benchmark/MetricsCollector.js +0 -123
  278. package/dist/kurot/benchmark/MetricsCollector.js.map +0 -1
  279. package/dist/kurot/benchmark/PerfPanel.d.ts +0 -24
  280. package/dist/kurot/benchmark/PerfPanel.d.ts.map +0 -1
  281. package/dist/kurot/benchmark/PerfPanel.js +0 -131
  282. package/dist/kurot/benchmark/PerfPanel.js.map +0 -1
  283. package/dist/kurot/benchmark/ReportExporter.d.ts +0 -8
  284. package/dist/kurot/benchmark/ReportExporter.d.ts.map +0 -1
  285. package/dist/kurot/benchmark/ReportExporter.js +0 -54
  286. package/dist/kurot/benchmark/ReportExporter.js.map +0 -1
  287. package/dist/kurot/benchmark/SceneRegistry.d.ts +0 -16
  288. package/dist/kurot/benchmark/SceneRegistry.d.ts.map +0 -1
  289. package/dist/kurot/benchmark/SceneRegistry.js +0 -233
  290. package/dist/kurot/benchmark/SceneRegistry.js.map +0 -1
  291. package/dist/kurot/benchmark/types.d.ts +0 -47
  292. package/dist/kurot/benchmark/types.d.ts.map +0 -1
  293. package/dist/kurot/benchmark/types.js +0 -2
  294. package/dist/kurot/benchmark/types.js.map +0 -1
@@ -11,7 +11,7 @@ import { TextFieldType } from '../../text/enums/TextFieldType.js';
11
11
  import { getFontString } from '../../text/TextMeasurer.js';
12
12
  import { CanvasBuffer, hitTestBuffer } from './CanvasBuffer.js';
13
13
  const CAPS_MAP = { none: 'butt', square: 'square', round: 'round' };
14
- /** Convert a 0xRRGGBB color number to a CSS rgb() string. */
14
+ const CANVAS_BLUR_RADIUS_SCALE = 0.25;
15
15
  function colorToString(color) {
16
16
  const r = (color >> 16) & 0xff;
17
17
  const g = (color >> 8) & 0xff;
@@ -19,30 +19,22 @@ function colorToString(color) {
19
19
  return `rgb(${r},${g},${b})`;
20
20
  }
21
21
  /**
22
- * Canvas 2D renderer. Traverses the DisplayObject tree and draws each node
23
- * using the Canvas 2D API. Equivalent to Egret's `CanvasRenderer`.
24
- *
25
- * Performance notes:
26
- * - Currently does full-tree traversal and full-canvas redraw every frame.
27
- * Egret used a DisplayList with dirty-region tracking to only redraw changed areas.
28
- * This should be added when performance becomes a concern.
29
- * - Egret used a RenderNode intermediate layer (BitmapNode, GraphicsNode, etc.)
30
- * to cache rendering instructions. Kurot reads DisplayObject data directly,
31
- * which is simpler but skips the caching benefit. If profiling shows the JS-side
32
- * traversal is a bottleneck, consider adding a lightweight render command cache.
33
- * - For high-performance scenarios, a WebGL renderer with batch rendering should
34
- * be implemented as an alternative backend.
22
+ * Traverses a display-object tree and renders it with Canvas 2D.
35
23
  */
36
24
  export class CanvasRenderer {
37
25
  // ── Instance fields ───────────────────────────────────────────────────────
38
26
  _hasFill = false;
39
27
  _hasStroke = false;
28
+ _bitmapTintCache = new WeakMap();
29
+ _graphicsTintCache = new WeakMap();
30
+ _globalTint = 0xffffff;
40
31
  // ── Public methods ────────────────────────────────────────────────────────
41
32
  /**
42
33
  * Renders a display object tree into the given buffer.
43
34
  */
44
35
  render(displayObject, buffer, matrix) {
45
36
  const ctx = buffer.context;
37
+ this._globalTint = 0xffffff;
46
38
  if (matrix) {
47
39
  ctx.save();
48
40
  ctx.transform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
@@ -52,26 +44,21 @@ export class CanvasRenderer {
52
44
  ctx.restore();
53
45
  return drawCalls;
54
46
  }
55
- /** @internal Used by WebGLRenderer for offscreen DisplayList rendering. */
56
47
  renderToContext(displayObject, ctx, offsetX, offsetY) {
57
48
  this.drawDisplayObject(displayObject, ctx, offsetX, offsetY, true);
58
49
  }
59
- /** @internal Used by WebGLRenderer to rasterize a Graphics object into a Canvas 2D context. */
60
50
  renderGraphicsToContext(graphics, ctx, offsetX, offsetY, forHitTest = false, skipCache = false) {
61
51
  this.renderGraphics(graphics, ctx, offsetX, offsetY, forHitTest, skipCache);
62
52
  }
63
- /** @internal Used by WebGL TextPipe to rasterize a TextField into a Canvas 2D context. */
64
53
  renderTextFieldToContext(tf, ctx, offsetX, offsetY) {
65
54
  this.renderTextField(tf, ctx, offsetX, offsetY);
66
55
  }
67
- /** @internal Used by Bitmap pixel hit test. */
68
56
  renderBitmapToContext(bitmap, ctx, offsetX, offsetY) {
69
57
  this.renderBitmap(bitmap, ctx, offsetX, offsetY);
70
58
  }
71
59
  // ── Private: tree traversal ───────────────────────────────────────────────
72
60
  drawDisplayObject(displayObject, ctx, offsetX, offsetY, _isStage = false) {
73
61
  let drawCalls = 0;
74
- // DisplayList cache (cacheAsBitmap)
75
62
  const $displayList = displayObject.$displayList;
76
63
  if ($displayList && !_isStage) {
77
64
  if (displayObject.$cacheDirty || displayObject.$renderDirty) {
@@ -85,7 +72,6 @@ export class CanvasRenderer {
85
72
  displayObject.$cacheDirty = false;
86
73
  displayObject.$renderDirty = false;
87
74
  }
88
- // Draw cached result and skip $children
89
75
  if ($displayList.bitmapData?.source) {
90
76
  const resolution = $displayList.actualResolution;
91
77
  const previousSmoothing = ctx.imageSmoothingEnabled;
@@ -96,9 +82,7 @@ export class CanvasRenderer {
96
82
  }
97
83
  return drawCalls;
98
84
  }
99
- // Draw self
100
85
  drawCalls += this.renderSelf(displayObject, ctx, offsetX, offsetY);
101
- // Draw $children
102
86
  const $children = displayObject.$children;
103
87
  if (!$children)
104
88
  return drawCalls;
@@ -106,6 +90,10 @@ export class CanvasRenderer {
106
90
  const child = $children[i];
107
91
  if (child.$renderMode === 1 /* RenderMode.NONE */)
108
92
  continue;
93
+ const previousTint = this._globalTint;
94
+ if (child.$tintRGB !== 0xffffff) {
95
+ this._globalTint = child.tint;
96
+ }
109
97
  let childOffsetX;
110
98
  let childOffsetY;
111
99
  if (child.$useTranslate) {
@@ -144,6 +132,7 @@ export class CanvasRenderer {
144
132
  else if (prevAlpha !== undefined) {
145
133
  ctx.globalAlpha = prevAlpha;
146
134
  }
135
+ this._globalTint = previousTint;
147
136
  }
148
137
  return drawCalls;
149
138
  }
@@ -170,15 +159,11 @@ export class CanvasRenderer {
170
159
  const bounds = displayObject.$getOriginalBounds();
171
160
  if (bounds.width <= 0 || bounds.height <= 0)
172
161
  return 0;
173
- // Build CSS filter string for GPU-accelerated filters.
174
- // ColorMatrixFilter cannot be expressed as a CSS filter string (arbitrary matrix),
175
- // so it falls back to the CPU pixel path below.
176
162
  const cssFilters = [];
177
163
  let hasCpuFilter = false;
178
164
  for (const filter of filters) {
179
165
  if (filter instanceof BlurFilter) {
180
- // Average blurX/blurY — CSS blur is isotropic.
181
- const radius = (filter.blurX + filter.blurY) / 2;
166
+ const radius = ((filter.blurX + filter.blurY) / 2) * CANVAS_BLUR_RADIUS_SCALE;
182
167
  if (radius > 0)
183
168
  cssFilters.push(`blur(${radius}px)`);
184
169
  }
@@ -194,7 +179,6 @@ export class CanvasRenderer {
194
179
  cssFilters.push(`drop-shadow(${dx}px ${dy}px ${blur}px rgba(${r},${g},${b},${a / 255}))`);
195
180
  }
196
181
  else if (filter instanceof GlowFilter) {
197
- // Approximate glow as a zero-offset drop-shadow.
198
182
  const blur = (filter.blurX + filter.blurY) / 2;
199
183
  const r = (filter.color >> 16) & 0xff;
200
184
  const g = (filter.color >> 8) & 0xff;
@@ -231,7 +215,6 @@ export class CanvasRenderer {
231
215
  const bufferH = Math.ceil(bounds.height);
232
216
  const offscreen = new CanvasBuffer(bufferW, bufferH);
233
217
  const offCtx = offscreen.context;
234
- // Apply CSS-capable filters on the offscreen context before drawing.
235
218
  if (cssFilters.length > 0)
236
219
  offCtx.filter = cssFilters.join(' ');
237
220
  let drawCalls = 0;
@@ -249,7 +232,6 @@ export class CanvasRenderer {
249
232
  return 0;
250
233
  }
251
234
  offCtx.filter = 'none';
252
- // Apply CPU-only filters (ColorMatrixFilter).
253
235
  const imageData = offCtx.getImageData(0, 0, offscreen.width, offscreen.height);
254
236
  const data = imageData.data;
255
237
  for (const filter of filters) {
@@ -279,8 +261,6 @@ export class CanvasRenderer {
279
261
  ctx.rect(scrollRect.x + offsetX, scrollRect.y + offsetY, scrollRect.width, scrollRect.height);
280
262
  ctx.clip();
281
263
  }
282
- // DisplayObject mask: render content and mask to offscreen buffers,
283
- // composite with 'destination-in' to produce the masked result.
284
264
  if (mask) {
285
265
  const bounds = displayObject.$getOriginalBounds();
286
266
  if (bounds.width <= 0 || bounds.height <= 0) {
@@ -294,15 +274,12 @@ export class CanvasRenderer {
294
274
  const bh = Math.ceil(bounds.height);
295
275
  const bx = bounds.x;
296
276
  const by = bounds.y;
297
- // Render content to offscreen buffer
298
277
  const contentBuffer = new CanvasBuffer(bw, bh);
299
278
  const contentCtx = contentBuffer.context;
300
279
  const drawCalls = this.drawDisplayObject(displayObject, contentCtx, -bx, -by);
301
- // Render mask shape to the same buffer using destination-in
302
280
  contentCtx.globalCompositeOperation = 'destination-in';
303
281
  const maskMatrix = mask.$getConcatenatedMatrix();
304
282
  const parentMatrix = displayObject.$getConcatenatedMatrix();
305
- // Transform mask relative to the content's local space
306
283
  contentCtx.save();
307
284
  const invA = parentMatrix.a, invB = parentMatrix.b, invC = parentMatrix.c, invD = parentMatrix.d;
308
285
  const invTx = parentMatrix.tx, invTy = parentMatrix.ty;
@@ -311,7 +288,6 @@ export class CanvasRenderer {
311
288
  const ia = invD / det, ib = -invB / det, ic = -invC / det, id = invA / det;
312
289
  const itx = (invC * invTy - invD * invTx) / det;
313
290
  const ity = (invB * invTx - invA * invTy) / det;
314
- // Combine: inverse(parent) * mask
315
291
  const ra = ia * maskMatrix.a + ic * maskMatrix.b;
316
292
  const rb = ib * maskMatrix.a + id * maskMatrix.b;
317
293
  const rc = ia * maskMatrix.c + ic * maskMatrix.d;
@@ -326,7 +302,6 @@ export class CanvasRenderer {
326
302
  this.drawDisplayObject(mask, contentCtx, 0, 0);
327
303
  contentCtx.restore();
328
304
  contentCtx.globalCompositeOperation = 'source-over';
329
- // Draw the masked result onto the main context
330
305
  ctx.drawImage(contentBuffer.surface, offsetX + bx, offsetY + by);
331
306
  contentBuffer.destroy();
332
307
  if (scrollRect)
@@ -363,13 +338,31 @@ export class CanvasRenderer {
363
338
  }
364
339
  renderMesh(mesh, ctx, offsetX, offsetY) {
365
340
  const bd = mesh.bitmapData;
366
- if (!bd?.source || mesh.vertices.length === 0)
341
+ if (!bd?.source || mesh.vertices.length === 0 || mesh.uvs.length !== mesh.vertices.length)
367
342
  return 0;
368
- // Canvas 2D doesn't natively support mesh rendering.
369
- // Fall back to drawing the full texture as a simple bitmap.
370
- const destW = !isNaN(mesh.width) ? mesh.width : mesh.textureWidth;
371
- const destH = !isNaN(mesh.height) ? mesh.height : mesh.textureHeight;
372
- ctx.drawImage(bd.source, mesh.bitmapX, mesh.bitmapY, mesh.bitmapWidth, mesh.bitmapHeight, offsetX + mesh.bitmapOffsetX, offsetY + mesh.bitmapOffsetY, destW, destH);
343
+ const tinted = this._globalTint !== 0xffffff;
344
+ const source = tinted
345
+ ? this.getTintedBitmapSource(mesh, this._globalTint)
346
+ : bd.source;
347
+ const sourceX = tinted ? 0 : mesh.bitmapX;
348
+ const sourceY = tinted ? 0 : mesh.bitmapY;
349
+ const sourceWidth = mesh.bitmapWidth;
350
+ const sourceHeight = mesh.bitmapHeight;
351
+ ctx.imageSmoothingEnabled = mesh.smoothing;
352
+ for (let i = 0; i + 2 < mesh.indices.length; i += 3) {
353
+ const i0 = mesh.indices[i];
354
+ const i1 = mesh.indices[i + 1];
355
+ const i2 = mesh.indices[i + 2];
356
+ if (i0 < 0 ||
357
+ i1 < 0 ||
358
+ i2 < 0 ||
359
+ i0 * 2 + 1 >= mesh.vertices.length ||
360
+ i1 * 2 + 1 >= mesh.vertices.length ||
361
+ i2 * 2 + 1 >= mesh.vertices.length) {
362
+ continue;
363
+ }
364
+ this.drawTexturedTriangle(ctx, source, sourceX, sourceY, sourceWidth, sourceHeight, mesh.vertices, mesh.uvs, i0, i1, i2, offsetX + mesh.bitmapOffsetX, offsetY + mesh.bitmapOffsetY);
365
+ }
373
366
  return 1;
374
367
  }
375
368
  renderBitmap(bitmap, ctx, offsetX, offsetY) {
@@ -381,21 +374,157 @@ export class CanvasRenderer {
381
374
  if (destW <= 0 || destH <= 0)
382
375
  return 0;
383
376
  ctx.imageSmoothingEnabled = bitmap.smoothing;
384
- ctx.drawImage(bd.source, bitmap.bitmapX, bitmap.bitmapY, bitmap.bitmapWidth, bitmap.bitmapHeight, offsetX + bitmap.bitmapOffsetX, offsetY + bitmap.bitmapOffsetY, destW, destH);
377
+ if (this._globalTint === 0xffffff) {
378
+ ctx.drawImage(bd.source, bitmap.bitmapX, bitmap.bitmapY, bitmap.bitmapWidth, bitmap.bitmapHeight, offsetX + bitmap.bitmapOffsetX, offsetY + bitmap.bitmapOffsetY, destW, destH);
379
+ }
380
+ else {
381
+ ctx.drawImage(this.getTintedBitmapSource(bitmap, this._globalTint), offsetX + bitmap.bitmapOffsetX, offsetY + bitmap.bitmapOffsetY, destW, destH);
382
+ }
385
383
  return 1;
386
384
  }
385
+ getTintedBitmapSource(bitmap, tint) {
386
+ const bitmapData = bitmap.bitmapData;
387
+ let cache = this._bitmapTintCache.get(bitmap);
388
+ const needsUpdate = !cache ||
389
+ cache.tint !== tint ||
390
+ cache.contentVersion !== bitmapData.contentVersion ||
391
+ cache.sourceX !== bitmap.bitmapX ||
392
+ cache.sourceY !== bitmap.bitmapY ||
393
+ cache.sourceWidth !== bitmap.bitmapWidth ||
394
+ cache.sourceHeight !== bitmap.bitmapHeight;
395
+ if (!cache) {
396
+ cache = this.createTintedCanvasCache();
397
+ this._bitmapTintCache.set(bitmap, cache);
398
+ }
399
+ if (needsUpdate) {
400
+ this.updateTintedCanvas(cache, bitmapData.source, bitmap.bitmapX, bitmap.bitmapY, bitmap.bitmapWidth, bitmap.bitmapHeight, tint, bitmapData.contentVersion);
401
+ }
402
+ return cache.buffer.surface;
403
+ }
404
+ getTintedGraphicsSource(graphics, tint, sourceChanged) {
405
+ const source = graphics.offscreenCanvas;
406
+ let cache = this._graphicsTintCache.get(graphics);
407
+ const needsUpdate = sourceChanged ||
408
+ !cache ||
409
+ cache.tint !== tint ||
410
+ cache.sourceWidth !== source.width ||
411
+ cache.sourceHeight !== source.height;
412
+ if (!cache) {
413
+ cache = this.createTintedCanvasCache();
414
+ this._graphicsTintCache.set(graphics, cache);
415
+ }
416
+ if (needsUpdate) {
417
+ this.updateTintedCanvas(cache, source, 0, 0, source.width, source.height, tint, cache.contentVersion + 1);
418
+ }
419
+ return cache.buffer.surface;
420
+ }
421
+ createTintedCanvasCache() {
422
+ return {
423
+ buffer: new CanvasBuffer(),
424
+ tint: 0xffffff,
425
+ contentVersion: -1,
426
+ sourceX: 0,
427
+ sourceY: 0,
428
+ sourceWidth: 0,
429
+ sourceHeight: 0,
430
+ };
431
+ }
432
+ updateTintedCanvas(cache, source, sourceX, sourceY, sourceWidth, sourceHeight, tint, contentVersion) {
433
+ const width = Math.max(1, Math.ceil(sourceWidth));
434
+ const height = Math.max(1, Math.ceil(sourceHeight));
435
+ if (cache.buffer.width !== width || cache.buffer.height !== height) {
436
+ cache.buffer.resize(width, height);
437
+ }
438
+ else {
439
+ cache.buffer.clear();
440
+ }
441
+ const context = cache.buffer.context;
442
+ context.drawImage(source, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, width, height);
443
+ context.globalCompositeOperation = 'multiply';
444
+ context.fillStyle = colorToString(tint);
445
+ context.fillRect(0, 0, width, height);
446
+ context.globalCompositeOperation = 'destination-in';
447
+ context.drawImage(source, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, width, height);
448
+ context.globalCompositeOperation = 'source-over';
449
+ cache.tint = tint;
450
+ cache.contentVersion = contentVersion;
451
+ cache.sourceX = sourceX;
452
+ cache.sourceY = sourceY;
453
+ cache.sourceWidth = sourceWidth;
454
+ cache.sourceHeight = sourceHeight;
455
+ }
456
+ drawTexturedTriangle(ctx, source, sourceX, sourceY, sourceWidth, sourceHeight, vertices, uvs, i0, i1, i2, offsetX, offsetY) {
457
+ const sx0 = sourceX + uvs[i0 * 2] * sourceWidth;
458
+ const sy0 = sourceY + uvs[i0 * 2 + 1] * sourceHeight;
459
+ const sx1 = sourceX + uvs[i1 * 2] * sourceWidth;
460
+ const sy1 = sourceY + uvs[i1 * 2 + 1] * sourceHeight;
461
+ const sx2 = sourceX + uvs[i2 * 2] * sourceWidth;
462
+ const sy2 = sourceY + uvs[i2 * 2 + 1] * sourceHeight;
463
+ const dx0 = offsetX + vertices[i0 * 2];
464
+ const dy0 = offsetY + vertices[i0 * 2 + 1];
465
+ const dx1 = offsetX + vertices[i1 * 2];
466
+ const dy1 = offsetY + vertices[i1 * 2 + 1];
467
+ const dx2 = offsetX + vertices[i2 * 2];
468
+ const dy2 = offsetY + vertices[i2 * 2 + 1];
469
+ const determinant = sx0 * (sy1 - sy2) + sx1 * (sy2 - sy0) + sx2 * (sy0 - sy1);
470
+ if (Math.abs(determinant) <= Number.EPSILON)
471
+ return;
472
+ const a = (dx0 * (sy1 - sy2) + dx1 * (sy2 - sy0) + dx2 * (sy0 - sy1)) / determinant;
473
+ const b = (dy0 * (sy1 - sy2) + dy1 * (sy2 - sy0) + dy2 * (sy0 - sy1)) / determinant;
474
+ const c = (dx0 * (sx2 - sx1) + dx1 * (sx0 - sx2) + dx2 * (sx1 - sx0)) / determinant;
475
+ const d = (dy0 * (sx2 - sx1) + dy1 * (sx0 - sx2) + dy2 * (sx1 - sx0)) / determinant;
476
+ const e = (dx0 * (sx1 * sy2 - sx2 * sy1) +
477
+ dx1 * (sx2 * sy0 - sx0 * sy2) +
478
+ dx2 * (sx0 * sy1 - sx1 * sy0)) /
479
+ determinant;
480
+ const f = (dy0 * (sx1 * sy2 - sx2 * sy1) +
481
+ dy1 * (sx2 * sy0 - sx0 * sy2) +
482
+ dy2 * (sx0 * sy1 - sx1 * sy0)) /
483
+ determinant;
484
+ ctx.save();
485
+ const centerX = (dx0 + dx1 + dx2) / 3;
486
+ const centerY = (dy0 + dy1 + dy2) / 3;
487
+ const vx0 = dx0 - centerX;
488
+ const vy0 = dy0 - centerY;
489
+ const vx1 = dx1 - centerX;
490
+ const vy1 = dy1 - centerY;
491
+ const vx2 = dx2 - centerX;
492
+ const vy2 = dy2 - centerY;
493
+ const length0 = Math.hypot(vx0, vy0);
494
+ const length1 = Math.hypot(vx1, vy1);
495
+ const length2 = Math.hypot(vx2, vy2);
496
+ const scale0 = length0 > 0 ? 1 + 0.5 / length0 : 1;
497
+ const scale1 = length1 > 0 ? 1 + 0.5 / length1 : 1;
498
+ const scale2 = length2 > 0 ? 1 + 0.5 / length2 : 1;
499
+ // Slightly overlapping clips prevent transparent antialiasing seams between adjacent triangles.
500
+ const clipX0 = centerX + vx0 * scale0;
501
+ const clipY0 = centerY + vy0 * scale0;
502
+ const clipX1 = centerX + vx1 * scale1;
503
+ const clipY1 = centerY + vy1 * scale1;
504
+ const clipX2 = centerX + vx2 * scale2;
505
+ const clipY2 = centerY + vy2 * scale2;
506
+ ctx.beginPath();
507
+ ctx.moveTo(clipX0, clipY0);
508
+ ctx.lineTo(clipX1, clipY1);
509
+ ctx.lineTo(clipX2, clipY2);
510
+ ctx.closePath();
511
+ ctx.clip();
512
+ ctx.transform(a, b, c, d, e, f);
513
+ ctx.drawImage(source, 0, 0);
514
+ ctx.restore();
515
+ }
387
516
  renderGraphics(graphics, ctx, offsetX, offsetY, forHitTest = false, skipCache = false) {
388
517
  if (graphics.commands.length === 0)
389
518
  return 0;
390
519
  // ── Offscreen cache (skip for hit-test or when caller manages its own cache) ──
391
520
  if (!forHitTest && !skipCache) {
521
+ let rebuilt = false;
392
522
  if (graphics.canvasCacheDirty || !graphics.offscreenCanvas) {
393
- // Measure bounds to size the offscreen canvas
523
+ rebuilt = true;
394
524
  const bounds = new Rectangle();
395
525
  graphics.$measureContentBounds(bounds);
396
526
  const cw = Math.ceil(bounds.width) || 1;
397
527
  const ch = Math.ceil(bounds.height) || 1;
398
- // Create or resize offscreen canvas
399
528
  if (!graphics.offscreenCanvas) {
400
529
  graphics.offscreenCanvas = document.createElement('canvas');
401
530
  graphics.offscreenCtx = graphics.offscreenCanvas.getContext('2d', { willReadFrequently: true });
@@ -416,15 +545,16 @@ export class CanvasRenderer {
416
545
  for (const cmd of graphics.commands) {
417
546
  this.executeGraphicsCommand(cmd, oc2d, false);
418
547
  }
419
- // Flush any open path that wasn't closed by endFill
420
548
  this.flushOpenPath(oc2d);
421
549
  oc2d.restore();
422
550
  graphics.offscreenBoundsX = bounds.x;
423
551
  graphics.offscreenBoundsY = bounds.y;
424
552
  graphics.canvasCacheDirty = false;
425
553
  }
426
- // Draw cached offscreen canvas
427
- ctx.drawImage(graphics.offscreenCanvas, offsetX + graphics.offscreenBoundsX, offsetY + graphics.offscreenBoundsY);
554
+ const source = this._globalTint === 0xffffff
555
+ ? graphics.offscreenCanvas
556
+ : this.getTintedGraphicsSource(graphics, this._globalTint, rebuilt);
557
+ ctx.drawImage(source, offsetX + graphics.offscreenBoundsX, offsetY + graphics.offscreenBoundsY);
428
558
  return 1;
429
559
  }
430
560
  // ── Hit-test path: direct execution, no cache ─────────────────────────
@@ -435,16 +565,12 @@ export class CanvasRenderer {
435
565
  for (const cmd of graphics.commands) {
436
566
  this.executeGraphicsCommand(cmd, ctx, forHitTest);
437
567
  }
438
- // Flush any open path that wasn't closed by endFill
439
568
  this.flushOpenPath(ctx);
440
569
  ctx.restore();
441
570
  return 1;
442
571
  }
443
572
  renderTextField(tf, ctx, offsetX, offsetY) {
444
- tf.getLinesArr(); // ensure lines are computed
445
- // INPUT mode while focused: HTML input element owns the display.
446
- // Only render background/border so the field is visible, but skip
447
- // text content to avoid the double-text artefact.
573
+ tf.getLinesArr();
448
574
  const inputFocused = tf.type === TextFieldType.INPUT && tf.isTyping;
449
575
  const width = !isNaN(tf.$explicitWidth) ? tf.$explicitWidth : tf.textWidth;
450
576
  const height = !isNaN(tf.$explicitHeight) ? tf.$explicitHeight : tf.textHeight;
@@ -463,7 +589,6 @@ export class CanvasRenderer {
463
589
  ctx.lineWidth = 1;
464
590
  ctx.strokeRect(0, 0, width, height);
465
591
  }
466
- // While the native input is active, skip text/cursor rendering.
467
592
  if (inputFocused) {
468
593
  ctx.restore();
469
594
  return 0;
@@ -490,11 +615,7 @@ export class CanvasRenderer {
490
615
  }
491
616
  // ── ScrollV offset ────────────────────────────────────────────────────
492
617
  const scrollOffset = tf.getScrollYOffset();
493
- // ── Draw lines (Egret-style: textBaseline='middle', y at line-height center) ──
494
- // Following Egret's TextField.drawText():
495
- // drawY advances by h/2 before drawing, then h/2 + lineSpacing after.
496
- // Each element's y = drawY + (lineHeight - elementSize) / 2.
497
- // With textBaseline='middle', the browser handles vertical centering automatically.
618
+ // ── Text lines ───────────────────────────────────────────────────────────
498
619
  let drawY = verticalOffset - scrollOffset;
499
620
  let drawCalls = 0;
500
621
  for (let i = 0; i < lines.length; i++) {
@@ -505,7 +626,6 @@ export class CanvasRenderer {
505
626
  drawY += h / 2 + lineSpacing;
506
627
  continue;
507
628
  }
508
- // Horizontal alignment offset
509
629
  let lineX = 0;
510
630
  if (tf.textAlign === HorizontalAlign.RIGHT) {
511
631
  lineX = width - line.width;
@@ -513,7 +633,6 @@ export class CanvasRenderer {
513
633
  else if (tf.textAlign === HorizontalAlign.CENTER) {
514
634
  lineX = (width - line.width) / 2;
515
635
  }
516
- // Draw each element in the line
517
636
  for (const el of line.elements) {
518
637
  const style = el.style;
519
638
  const fontSize = style?.size ?? tf.size;
@@ -528,7 +647,6 @@ export class CanvasRenderer {
528
647
  ctx.textBaseline = 'middle';
529
648
  ctx.textAlign = 'left';
530
649
  const textY = drawY + (h - fontSize) / 2;
531
- // Stroke
532
650
  if (stroke > 0) {
533
651
  ctx.strokeStyle = colorToString(strokeColor);
534
652
  ctx.lineWidth = stroke * 2;
@@ -536,7 +654,6 @@ export class CanvasRenderer {
536
654
  ctx.strokeText(el.text, lineX, textY);
537
655
  drawCalls++;
538
656
  }
539
- // Fill
540
657
  ctx.fillStyle = colorToString(textColor);
541
658
  ctx.fillText(el.text, lineX, textY);
542
659
  drawCalls++;
@@ -546,12 +663,10 @@ export class CanvasRenderer {
546
663
  }
547
664
  // ── INPUT cursor ──────────────────────────────────────────────────────
548
665
  if (tf.type === TextFieldType.INPUT && tf.isTyping) {
549
- // Draw blinking cursor at caretIndex position
550
666
  const caretIndex = tf.caretIndex;
551
667
  const fontStr = getFontString(tf.size, tf.fontFamily, tf.bold, tf.italic);
552
668
  ctx.font = fontStr;
553
669
  ctx.textBaseline = 'middle';
554
- // Calculate cursor x by measuring text up to caretIndex
555
670
  let cursorX = 0;
556
671
  let charCount = 0;
557
672
  for (const line of lines) {
@@ -568,7 +683,7 @@ export class CanvasRenderer {
568
683
  }
569
684
  if (charCount >= caretIndex)
570
685
  break;
571
- cursorX = 0; // reset x for next line
686
+ cursorX = 0;
572
687
  }
573
688
  const cursorY = verticalOffset - scrollOffset;
574
689
  ctx.fillStyle = colorToString(tf.textColor);
@@ -630,8 +745,6 @@ export class CanvasRenderer {
630
745
  this._hasFill = true;
631
746
  break;
632
747
  }
633
- // createGradientBox uses the Flash/Egret convention where the matrix
634
- // maps gradient space ±819.2 (1638.4/2) to the desired box edges.
635
748
  const GH = 819.2;
636
749
  let gradient;
637
750
  if (cmd.matrix) {
@@ -773,15 +886,11 @@ function applyColorMatrix(data, matrix) {
773
886
  }
774
887
  }
775
888
  // ── Graphics pixel-perfect hit test ──────────────────────────────────────────
776
- // Registered here to avoid circular dependency between display/ and player/.
777
- // Uses the shared 3×3 hitTestBuffer: translate so the test point lands at (1,1),
778
- // render with forHitTest=true (all shapes drawn as opaque black), then read alpha.
779
889
  const _hitRenderer = new CanvasRenderer();
780
890
  setGraphicsHitTest((graphics, localX, localY) => {
781
891
  const buf = hitTestBuffer();
782
892
  buf.clear();
783
893
  const ctx = buf.context;
784
- // Translate so localX/Y maps to pixel (1,1) in the 3×3 buffer
785
894
  ctx.setTransform(1, 0, 0, 1, 1 - localX, 1 - localY);
786
895
  _hitRenderer.renderGraphicsToContext(graphics, ctx, 0, 0, true);
787
896
  ctx.setTransform(1, 0, 0, 1, 0, 0);
@@ -793,7 +902,6 @@ setGraphicsHitTest((graphics, localX, localY) => {
793
902
  }
794
903
  });
795
904
  // ── Bitmap pixel-perfect hit test ────────────────────────────────────────────
796
- // Renders the Bitmap into the shared 3×3 buffer at the test point and reads alpha.
797
905
  setBitmapPixelHitTest((bitmap, localX, localY) => {
798
906
  const buf = hitTestBuffer();
799
907
  buf.clear();