Skip to content

Commit 2c5b856

Browse files
Bryce Thomaskjlubick
authored andcommitted
Add drawOnce() API to SkSurface.
The existing |SkSurface.requestAnimationFrame| API provides a convenient way of drawing Skia animations using the same idiom as the well known |Window.requestAnimationFrame|. It gracefully handles providing the caller with access to the right canvas, as well as flushing after the user-supplied callback. The new |SkSurface.drawOnce| API added in this change provides the same conveniences around access to the right canvas and flushing, but for the use-case where the user wishes to draw a single frame only. Importantly, this new API disposes of the SkSurface upon completion, i.e. frees the memory associated with the underlying pixel storage an surface. This avoids memory leaks that occur when |SkSurface.requestAnimationFrame| is used for single-frame purposes. Bug: NONE Change-Id: Ic4e48e65dffc4809513ceaf72260ac0432b98952 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/265604 Reviewed-by: Kevin Lubick <kjlubick@google.com>
1 parent 732248c commit 2c5b856

5 files changed

Lines changed: 53 additions & 4 deletions

File tree

modules/canvaskit/canvaskit/example.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ <h2> CanvasKit can allow for text shaping (e.g. breaking, kerning)</h2>
231231
path.delete();
232232
rrect.delete();
233233
paint.delete();
234-
// Intentionally just draw frame once
235234
}
236-
surface.requestAnimationFrame(drawFrame);
235+
// Intentionally just draw frame once
236+
surface.drawOnce(drawFrame);
237237
}
238238

239239
function preventScrolling(canvas) {

modules/canvaskit/canvaskit/extra.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,8 @@ <h2> CanvasKit can serialize/deserialize .skp files</h2>
419419
function drawFrame(canvas) {
420420
canvas.clear(CanvasKit.TRANSPARENT);
421421
canvas.drawPicture(pic);
422-
// Intentionally just draw frame once
423422
}
424-
surface.requestAnimationFrame(drawFrame);
423+
// Intentionally just draw frame once
424+
surface.drawOnce(drawFrame);
425425
}
426426
</script>

modules/canvaskit/externs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ CanvasKit.SkPicture.prototype.saveAsFile = function() {};
776776
CanvasKit.SkSurface.prototype.dispose = function() {};
777777
CanvasKit.SkSurface.prototype.flush = function() {};
778778
CanvasKit.SkSurface.prototype.requestAnimationFrame = function() {};
779+
CanvasKit.SkSurface.prototype.drawOnce = function() {};
779780
CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function() {};
780781

781782
/** @return {CanvasKit.SkVertices} */

modules/canvaskit/interface.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,10 +755,28 @@ CanvasKit.onRuntimeInitialized = function() {
755755

756756
callback(this._cached_canvas);
757757

758+
// We do not dispose() of the SkSurface here, as the client will typically
759+
// call requestAnimationFrame again from within the supplied callback.
760+
// For drawing a single frame, prefer drawOnce().
758761
this.flush();
759762
}.bind(this));
760763
}
761764

765+
CanvasKit.SkSurface.prototype.drawOnce = function(callback, dirtyRect) {
766+
if (!this._cached_canvas) {
767+
this._cached_canvas = this.getCanvas();
768+
}
769+
window.requestAnimationFrame(function() {
770+
if (this._context !== undefined) {
771+
CanvasKit.setCurrentContext(this._context);
772+
}
773+
callback(this._cached_canvas);
774+
775+
this.flush();
776+
this.dispose();
777+
}.bind(this));
778+
}
779+
762780
// Run through the JS files that are added at compile time.
763781
if (CanvasKit._extraInitializations) {
764782
CanvasKit._extraInitializations.forEach(function(init) {

modules/canvaskit/tests/core.spec.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,36 @@ describe('Core canvas behavior', function() {
369369
});
370370
});
371371

372+
it('can draw once using drawOnce utility method', function(done) {
373+
LoadCanvasKit.then(catchException(done, () => {
374+
const surface = CanvasKit.MakeCanvasSurface('test');
375+
expect(surface).toBeTruthy('Could not make surface');
376+
if (!surface) {
377+
done();
378+
return;
379+
}
380+
381+
function drawFrame(canvas) {
382+
const paint = new CanvasKit.SkPaint();
383+
paint.setStrokeWidth(1.0);
384+
paint.setAntiAlias(true);
385+
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
386+
paint.setStyle(CanvasKit.PaintStyle.Stroke);
387+
const path = new CanvasKit.SkPath();
388+
path.moveTo(20, 5);
389+
path.lineTo(30, 20);
390+
path.lineTo(40, 10);
391+
canvas.drawPath(path, paint);
392+
393+
path.delete();
394+
paint.delete();
395+
}
396+
surface.drawOnce(drawFrame);
397+
398+
reportSurface(surface, 'drawOnce', done);
399+
}));
400+
});
401+
372402
it('can use DecodeCache APIs', function(done) {
373403
LoadCanvasKit.then(catchException(done, () => {
374404
const initialLimit = CanvasKit.getDecodeCacheLimitBytes();

0 commit comments

Comments
 (0)