@@ -33,8 +33,10 @@ class _CanvasPool extends _SaveStackTracking {
3333
3434 html.HtmlElement ? _rootElement;
3535 int _saveContextCount = 0 ;
36+ final double _density;
3637
37- _CanvasPool (this ._widthInBitmapPixels, this ._heightInBitmapPixels);
38+ _CanvasPool (this ._widthInBitmapPixels, this ._heightInBitmapPixels,
39+ this ._density);
3840
3941 html.CanvasRenderingContext2D get context {
4042 html.CanvasRenderingContext2D ? ctx = _context;
@@ -83,7 +85,12 @@ class _CanvasPool extends _SaveStackTracking {
8385 void _createCanvas () {
8486 bool requiresClearRect = false ;
8587 bool reused = false ;
86- html.CanvasElement canvas;
88+ html.CanvasElement ? canvas;
89+ if (_canvas != null ) {
90+ _canvas! .width = 0 ;
91+ _canvas! .height = 0 ;
92+ _canvas = null ;
93+ }
8794 if (_reusablePool != null && _reusablePool! .isNotEmpty) {
8895 canvas = _canvas = _reusablePool! .removeAt (0 );
8996 requiresClearRect = true ;
@@ -99,10 +106,7 @@ class _CanvasPool extends _SaveStackTracking {
99106 _widthInBitmapPixels / EnginePlatformDispatcher .browserDevicePixelRatio;
100107 final double cssHeight =
101108 _heightInBitmapPixels / EnginePlatformDispatcher .browserDevicePixelRatio;
102- canvas = html.CanvasElement (
103- width: _widthInBitmapPixels,
104- height: _heightInBitmapPixels,
105- );
109+ canvas = _allocCanvas (_widthInBitmapPixels, _heightInBitmapPixels);
106110 _canvas = canvas;
107111
108112 // Why is this null check here, even though we just allocated a canvas element above?
@@ -113,12 +117,9 @@ class _CanvasPool extends _SaveStackTracking {
113117 if (_canvas == null ) {
114118 // Evict BitmapCanvas(s) and retry.
115119 _reduceCanvasMemoryUsage ();
116- canvas = html.CanvasElement (
117- width: _widthInBitmapPixels,
118- height: _heightInBitmapPixels,
119- );
120+ canvas = _allocCanvas (_widthInBitmapPixels, _heightInBitmapPixels);
120121 }
121- canvas.style
122+ canvas! .style
122123 ..position = 'absolute'
123124 ..width = '${cssWidth }px'
124125 ..height = '${cssHeight }px' ;
@@ -131,19 +132,55 @@ class _CanvasPool extends _SaveStackTracking {
131132 _rootElement! .append (canvas);
132133 }
133134
134- if (reused) {
135- // If a canvas is the first element we set z-index = -1 in [BitmapCanvas]
136- // endOfPaint to workaround blink compositing bug. To make sure this
137- // does not leak when reused reset z-index.
138- canvas.style.removeProperty ('z-index' );
135+ try {
136+ if (reused) {
137+ // If a canvas is the first element we set z-index = -1 in [BitmapCanvas]
138+ // endOfPaint to workaround blink compositing bug. To make sure this
139+ // does not leak when reused reset z-index.
140+ canvas.style.removeProperty ('z-index' );
141+ }
142+ _context = canvas.context2D;
143+ } catch (e) {
144+ // Handle OOM.
139145 }
140-
141- final html.CanvasRenderingContext2D context = _context = canvas.context2D;
142- _contextHandle = ContextStateHandle (this , context);
146+ if (_context == null ) {
147+ _reduceCanvasMemoryUsage ();
148+ _context = canvas.context2D;
149+ }
150+ if (_context == null ) {
151+ /// Browser ran out of memory, try to recover current allocation
152+ /// and bail.
153+ _canvas? .width = 0 ;
154+ _canvas? .height = 0 ;
155+ _canvas = null ;
156+ return ;
157+ }
158+ _contextHandle = ContextStateHandle (this , _context! , this ._density);
143159 _initializeViewport (requiresClearRect);
144160 _replayClipStack ();
145161 }
146162
163+ html.CanvasElement ? _allocCanvas (int width, int height) {
164+ final dynamic canvas =
165+ js_util.callMethod (html.document, 'createElement' , < dynamic > ['CANVAS' ]);
166+ if (canvas != null ) {
167+ try {
168+ canvas.width = (width * _density).ceil ();
169+ canvas.height = (height * _density).ceil ();
170+ } catch (e) {
171+ return null ;
172+ }
173+ return canvas as html.CanvasElement ;
174+ }
175+ return null ;
176+ // !!! We don't use the code below since NNBD assumes it can never return
177+ // null and optimizes out code.
178+ // return canvas = html.CanvasElement(
179+ // width: _widthInBitmapPixels,
180+ // height: _heightInBitmapPixels,
181+ // );
182+ }
183+
147184 @override
148185 void clear () {
149186 super .clear ();
@@ -188,7 +225,7 @@ class _CanvasPool extends _SaveStackTracking {
188225 clipTimeTransform[5 ] != prevTransform[5 ] ||
189226 clipTimeTransform[12 ] != prevTransform[12 ] ||
190227 clipTimeTransform[13 ] != prevTransform[13 ]) {
191- final double ratio = EnginePlatformDispatcher .browserDevicePixelRatio ;
228+ final double ratio = dpi ;
192229 ctx.setTransform (ratio, 0 , 0 , ratio, 0 , 0 );
193230 ctx.transform (
194231 clipTimeTransform[0 ],
@@ -222,7 +259,7 @@ class _CanvasPool extends _SaveStackTracking {
222259 transform[5 ] != prevTransform[5 ] ||
223260 transform[12 ] != prevTransform[12 ] ||
224261 transform[13 ] != prevTransform[13 ]) {
225- final double ratio = EnginePlatformDispatcher .browserDevicePixelRatio ;
262+ final double ratio = dpi ;
226263 ctx.setTransform (ratio, 0 , 0 , ratio, 0 , 0 );
227264 ctx.transform (transform[0 ], transform[1 ], transform[4 ], transform[5 ],
228265 transform[12 ], transform[13 ]);
@@ -300,15 +337,19 @@ class _CanvasPool extends _SaveStackTracking {
300337 // is applied on the DOM elements.
301338 ctx.setTransform (1 , 0 , 0 , 1 , 0 , 0 );
302339 if (clearCanvas) {
303- ctx.clearRect (0 , 0 , _widthInBitmapPixels, _heightInBitmapPixels);
340+ ctx.clearRect (0 , 0 , _widthInBitmapPixels * _density,
341+ _heightInBitmapPixels * _density);
304342 }
305343
306344 // This scale makes sure that 1 CSS pixel is translated to the correct
307345 // number of bitmap pixels.
308- ctx.scale (EnginePlatformDispatcher .browserDevicePixelRatio,
309- EnginePlatformDispatcher .browserDevicePixelRatio);
346+ ctx.scale (dpi, dpi);
310347 }
311348
349+ /// Returns effective dpi (browser DPI and pixel density due to transform).
350+ double get dpi =>
351+ EnginePlatformDispatcher .browserDevicePixelRatio * _density;
352+
312353 void resetTransform () {
313354 final html.CanvasElement ? canvas = _canvas;
314355 if (canvas != null ) {
@@ -688,8 +729,9 @@ class _CanvasPool extends _SaveStackTracking {
688729class ContextStateHandle {
689730 final html.CanvasRenderingContext2D context;
690731 final _CanvasPool _canvasPool;
732+ final double density;
691733
692- ContextStateHandle (this ._canvasPool, this .context);
734+ ContextStateHandle (this ._canvasPool, this .context, this .density );
693735 ui.BlendMode ? _currentBlendMode = ui.BlendMode .srcOver;
694736 ui.StrokeCap ? _currentStrokeCap = ui.StrokeCap .butt;
695737 ui.StrokeJoin ? _currentStrokeJoin = ui.StrokeJoin .miter;
@@ -778,7 +820,8 @@ class ContextStateHandle {
778820 if (paint.shader != null ) {
779821 final EngineGradient engineShader = paint.shader as EngineGradient ;
780822 final Object paintStyle =
781- engineShader.createPaintStyle (_canvasPool.context, shaderBounds);
823+ engineShader.createPaintStyle (_canvasPool.context, shaderBounds,
824+ density);
782825 fillStyle = paintStyle;
783826 strokeStyle = paintStyle;
784827 } else if (paint.color != null ) {
0 commit comments