[canvaskit] Add drawImage support
Adds drawImageRect as well.
Bug: skia:
Change-Id: Ib74f92a3ee22664297d8ce7ff1d2cd2644b806b7
Reviewed-on: https://skia-review.googlesource.com/c/173990
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/experimental/canvaskit/canvaskit/example.html b/experimental/canvaskit/canvaskit/example.html
index db19e1b..876e6d5 100644
--- a/experimental/canvaskit/canvaskit/example.html
+++ b/experimental/canvaskit/canvaskit/example.html
@@ -136,35 +136,23 @@
});
fetch('https://storage.googleapis.com/skia-cdn/misc/robot.nima').then((resp) => {
- resp.blob().then((blob) => {
- let reader = new FileReader();
- reader.addEventListener("loadend", function() {
- nimaFile = reader.result;
- NimaExample(CanvasKit, nimaFile, nimaTexture);
- });
- reader.readAsArrayBuffer(blob);
+ resp.arrayBuffer().then((buffer) => {
+ nimaFile = buffer;
+ NimaExample(CanvasKit, nimaFile, nimaTexture);
});
});
fetch('https://storage.googleapis.com/skia-cdn/misc/robot.nima.png').then((resp) => {
- resp.blob().then((blob) => {
- let reader = new FileReader();
- reader.addEventListener("loadend", function() {
- nimaTexture = reader.result;
- NimaExample(CanvasKit, nimaFile, nimaTexture);
- });
- reader.readAsArrayBuffer(blob);
+ resp.arrayBuffer().then((arrayBuffer) => {
+ nimaTexture = arrayBuffer;
+ NimaExample(CanvasKit, nimaFile, nimaTexture);
});
});
fetch('https://storage.googleapis.com/skia-cdn/misc/bones.jpg').then((resp) => {
- resp.blob().then((blob) => {
- let reader = new FileReader();
- reader.addEventListener("loadend", function() {
- bonesImage = reader.result;
- VertexAPI2(CanvasKit, bonesImage);
- });
- reader.readAsArrayBuffer(blob);
+ resp.arrayBuffer().then((buffer) => {
+ bonesImage = buffer;
+ VertexAPI2(CanvasKit, bonesImage);
});
});
@@ -448,25 +436,55 @@
let skcanvas = CanvasKit.MakeCanvas(300, 300);
let realCanvas = document.getElementById('api1_c');
- for (let canvas of [skcanvas, realCanvas]) {
- let ctx = canvas.getContext('2d');
- ctx.font = '30px Impact'
- ctx.rotate(.1);
- let text = ctx.measureText('Awesome');
- ctx.fillText('Awesome ', 50, 100);
- ctx.strokeText('Groovy!', 60+text.width, 100);
+ let skPromise = fetch('./test.png')
+ // if clients want to use a Blob, they are responsible
+ // for reading it themselves.
+ .then((response) => response.arrayBuffer())
+ .then((buffer) => {
+ skcanvas._img = skcanvas.decodeImage(buffer);
+ });
+ let realPromise = fetch('./test.png')
+ .then((response) => response.blob())
+ .then((blob) => createImageBitmap(blob))
+ .then((bitmap) => {
+ realCanvas._img = bitmap;
+ });
- // Draw line under Awesome
- ctx.strokeStyle = 'rgba(125,0,0,0.5)';
- ctx.beginPath();
- ctx.lineWidth=6;
- ctx.lineTo(50, 105);
- ctx.lineTo(50 + text.width, 105);
- ctx.stroke();
- }
- // TODO load image
- document.getElementById('api1').src = skcanvas.toDataURL();
+ Promise.all([realPromise, skPromise]).then(() => {
+ for (let canvas of [skcanvas, realCanvas]) {
+ let ctx = canvas.getContext('2d');
+ ctx.fillStyle = '#EEE';
+ ctx.fillRect(0, 0, 300, 300);
+ ctx.fillStyle = 'black';
+ ctx.font = '30px Impact'
+ ctx.rotate(.1);
+ let text = ctx.measureText('Awesome');
+ ctx.fillText('Awesome ', 50, 100);
+ ctx.strokeText('Groovy!', 60+text.width, 100);
+
+ // Draw line under Awesome
+ ctx.strokeStyle = 'rgba(125,0,0,0.5)';
+ ctx.beginPath();
+ ctx.lineWidth=6;
+ ctx.lineTo(50, 105);
+ ctx.lineTo(50 + text.width, 105);
+ ctx.stroke();
+
+ // squished vertically
+ ctx.globalAlpha = 0.7
+ ctx.imageSmoothingQuality = 'medium';
+ ctx.drawImage(canvas._img, 150, 150, 150, 100);
+ ctx.rotate(-.2);
+ ctx.imageSmoothingEnabled = false;
+ ctx.drawImage(canvas._img, 100, 150, 400, 350, 10, 200, 150, 100);
+ }
+
+
+ document.getElementById('api1').src = skcanvas.toDataURL();
+ skcanvas.dispose();
+ });
+
}
function CanvasAPI2(CanvasKit) {
diff --git a/experimental/canvaskit/canvaskit/node.example.js b/experimental/canvaskit/canvaskit/node.example.js
index 955b281..b9afb7f 100644
--- a/experimental/canvaskit/canvaskit/node.example.js
+++ b/experimental/canvaskit/canvaskit/node.example.js
@@ -1,6 +1,9 @@
console.log('hello world');
const CanvasKitInit = require('./bin/canvaskit.js');
+const fs = require('fs');
+const path = require('path');
+
CanvasKitInit({
locateFile: (file) => __dirname + '/bin/'+file,
@@ -8,6 +11,9 @@
CanvasKit = CK;
let canvas = CanvasKit.MakeCanvas(300, 300);
+ let img = fs.readFileSync(path.join(__dirname, 'test.png'));
+ img = canvas.decodeImage(img);
+
let ctx = canvas.getContext('2d');
ctx.font = '30px Impact'
ctx.rotate(.1);
@@ -23,6 +29,14 @@
ctx.lineTo(50 + text.width, 102);
ctx.stroke();
+ // squished vertically
+ ctx.globalAlpha = 0.7
+ ctx.imageSmoothingQuality = 'medium';
+ ctx.drawImage(img, 150, 150, 150, 100);
+ ctx.rotate(-.2);
+ ctx.imageSmoothingEnabled = false;
+ ctx.drawImage(img, 100, 150, 400, 350, 10, 200, 150, 100);
+
// TODO load an image from file
console.log('<img src="' + canvas.toDataURL() + '" />');
});
diff --git a/experimental/canvaskit/canvaskit/test.png b/experimental/canvaskit/canvaskit/test.png
new file mode 100644
index 0000000..c2efb81
--- /dev/null
+++ b/experimental/canvaskit/canvaskit/test.png
Binary files differ
diff --git a/experimental/canvaskit/canvaskit_bindings.cpp b/experimental/canvaskit/canvaskit_bindings.cpp
index 5d11347..24e37ac 100644
--- a/experimental/canvaskit/canvaskit_bindings.cpp
+++ b/experimental/canvaskit/canvaskit_bindings.cpp
@@ -21,9 +21,11 @@
#include "SkData.h"
#include "SkDiscretePathEffect.h"
#include "SkEncodedImageFormat.h"
+#include "SkFilterQuality.h"
#include "SkFontMgr.h"
#include "SkFontMgrPriv.h"
#include "SkGradientShader.h"
+#include "SkImage.h"
#include "SkImageInfo.h"
#include "SkImageShader.h"
#include "SkMakeUnique.h"
@@ -435,6 +437,12 @@
function("setCurrentContext", &emscripten_webgl_make_context_current);
constant("gpu", true);
#endif
+ function("_decodeImage", optional_override([](uintptr_t /* uint8_t* */ iptr,
+ size_t length)->sk_sp<SkImage> {
+ uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
+ sk_sp<SkData> bytes = SkData::MakeWithoutCopy(imgData, length);
+ return SkImage::MakeFromEncoded(bytes);
+ }), allow_raw_pointers());
function("_getRasterDirectSurface", optional_override([](const SimpleImageInfo ii,
uintptr_t /* uint8_t* */ pptr,
size_t rowBytes)->sk_sp<SkSurface> {
@@ -577,6 +585,14 @@
self.clear(SkColor(color));
}))
.function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
+ .function("drawImage", select_overload<void (const sk_sp<SkImage>&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers())
+ .function("drawImageRect", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
+ SkRect src, SkRect dst,
+ const SkPaint* paint, bool fastSample)->void {
+ self.drawImageRect(image, src, dst, paint,
+ fastSample ? SkCanvas::kFast_SrcRectConstraint :
+ SkCanvas::kStrict_SrcRectConstraint);
+ }), allow_raw_pointers())
.function("drawPaint", &SkCanvas::drawPaint)
.function("drawPath", &SkCanvas::drawPath)
.function("drawRect", &SkCanvas::drawRect)
@@ -613,6 +629,8 @@
class_<SkImage>("SkImage")
.smart_ptr<sk_sp<SkImage>>("sk_sp<SkImage>")
+ .function("height", &SkImage::height)
+ .function("width", &SkImage::width)
.function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData))
.function("_encodeToDataWithFormat", select_overload<sk_sp<SkData>(SkEncodedImageFormat encodedImageFormat, int quality)const>(&SkImage::encodeToData));
@@ -631,10 +649,11 @@
// Add a optional_override to change it out.
return JSColor(self.getColor());
}))
- .function("getStrokeWidth", &SkPaint::getStrokeWidth)
- .function("getStrokeMiter", &SkPaint::getStrokeMiter)
+ .function("getFilterQuality", &SkPaint::getFilterQuality)
.function("getStrokeCap", &SkPaint::getStrokeCap)
.function("getStrokeJoin", &SkPaint::getStrokeJoin)
+ .function("getStrokeMiter", &SkPaint::getStrokeMiter)
+ .function("getStrokeWidth", &SkPaint::getStrokeWidth)
.function("getTextSize", &SkPaint::getTextSize)
.function("measureText", optional_override([](SkPaint& self, std::string text) {
// TODO(kjlubick): This does not work well for non-ascii
@@ -649,13 +668,14 @@
// Add a optional_override to change it out.
self.setColor(SkColor(color));
}))
+ .function("setFilterQuality", &SkPaint::setFilterQuality)
.function("setMaskFilter", &SkPaint::setMaskFilter)
.function("setPathEffect", &SkPaint::setPathEffect)
.function("setShader", &SkPaint::setShader)
- .function("setStrokeWidth", &SkPaint::setStrokeWidth)
- .function("setStrokeMiter", &SkPaint::setStrokeMiter)
.function("setStrokeCap", &SkPaint::setStrokeCap)
.function("setStrokeJoin", &SkPaint::setStrokeJoin)
+ .function("setStrokeMiter", &SkPaint::setStrokeMiter)
+ .function("setStrokeWidth", &SkPaint::setStrokeWidth)
.function("setStyle", &SkPaint::setStyle)
.function("setTextSize", &SkPaint::setTextSize);
@@ -807,6 +827,12 @@
.value("InverseWinding", SkPath::FillType::kInverseWinding_FillType)
.value("InverseEvenOdd", SkPath::FillType::kInverseEvenOdd_FillType);
+ enum_<SkFilterQuality>("FilterQuality")
+ .value("None", SkFilterQuality::kNone_SkFilterQuality)
+ .value("Low", SkFilterQuality::kLow_SkFilterQuality)
+ .value("Medium", SkFilterQuality::kMedium_SkFilterQuality)
+ .value("High", SkFilterQuality::kHigh_SkFilterQuality);
+
enum_<SkEncodedImageFormat>("ImageFormat")
.value("PNG", SkEncodedImageFormat::kPNG)
.value("JPEG", SkEncodedImageFormat::kJPEG);
diff --git a/experimental/canvaskit/externs.js b/experimental/canvaskit/externs.js
index 5e2d724..b4e30a9 100644
--- a/experimental/canvaskit/externs.js
+++ b/experimental/canvaskit/externs.js
@@ -27,10 +27,14 @@
Color: function() {},
/** @return {CanvasKit.SkRect} */
LTRBRect: function() {},
+ /** @return {CanvasKit.SkRect} */
+ XYWHRect: function() {},
MakeBlurMaskFilter: function() {},
MakeCanvas: function() {},
MakeCanvasSurface: function() {},
MakeImageShader: function() {},
+ /** @return {CanvasKit.SkImage} */
+ MakeImageFromEncoded: function() {},
/** @return {LinearCanvasGradient} */
MakeLinearGradientShader: function() {},
MakeNimaActor: function() {},
@@ -58,6 +62,7 @@
_MakeSkDashPathEffect: function() {},
_MakeSkVertices: function() {},
_MakeTwoPointConicalGradientShader: function() {},
+ _decodeImage: function() {},
_getRasterDirectSurface: function() {},
_getRasterN32PremulSurface: function() {},
_getWebGLSurface: function() {},
@@ -84,6 +89,8 @@
// public API (from C++ bindings)
clear: function() {},
clipPath: function() {},
+ drawImage: function() {},
+ drawImageRect: function() {},
drawPaint: function() {},
drawPath: function() {},
drawRect: function() {},
@@ -104,6 +111,9 @@
},
SkImage: {
+ // public API (from C++ bindings)
+ height: function() {},
+ width: function() {},
// private API
_encodeToData: function() {},
_encodeToDataWithFormat: function() {},
@@ -125,6 +135,7 @@
copy: function() {},
getBlendMode: function() {},
getColor: function() {},
+ getFilterQuality: function() {},
getStrokeCap: function() {},
getStrokeJoin: function() {},
getStrokeMiter: function() {},
@@ -134,6 +145,7 @@
setAntiAlias: function() {},
setBlendMode: function() {},
setColor: function() {},
+ setFilterQuality: function() {},
setMaskFilter: function() {},
setPathEffect: function() {},
setShader: function() {},
@@ -301,6 +313,13 @@
InverseEvenOdd: {},
},
+ FilterQuality: {
+ None: {},
+ Low: {},
+ Medium: {},
+ High: {},
+ },
+
ImageFormat: {
PNG: {},
JPEG: {},
@@ -411,9 +430,10 @@
// Define everything created in the canvas2d spec here
var HTMLCanvas = {};
+HTMLCanvas.prototype.decodeImage = function() {};
+HTMLCanvas.prototype.dispose = function() {};
HTMLCanvas.prototype.getContext = function() {};
HTMLCanvas.prototype.toDataURL = function() {};
-HTMLCanvas.prototype.dispose = function() {};
var CanvasRenderingContext2D = {};
CanvasRenderingContext2D.prototype.addHitRegion = function() {};
@@ -428,6 +448,7 @@
CanvasRenderingContext2D.prototype.createLinearGradient = function() {};
CanvasRenderingContext2D.prototype.createRadialGradient = function() {};
CanvasRenderingContext2D.prototype.drawFocusIfNeeded = function() {};
+CanvasRenderingContext2D.prototype.drawImage = function() {};
CanvasRenderingContext2D.prototype.ellipse = function() {};
CanvasRenderingContext2D.prototype.fill = function() {};
CanvasRenderingContext2D.prototype.fillRect = function() {};
diff --git a/experimental/canvaskit/htmlcanvas/canvas2d.js b/experimental/canvaskit/htmlcanvas/canvas2d.js
index 1353b81..e025fb0 100644
--- a/experimental/canvaskit/htmlcanvas/canvas2d.js
+++ b/experimental/canvaskit/htmlcanvas/canvas2d.js
@@ -40,6 +40,17 @@
function HTMLCanvas(skSurface) {
this._surface = skSurface;
this._context = new CanvasRenderingContext2D(skSurface.getCanvas());
+ this._imgs = [];
+
+ // Data is either an ArrayBuffer, a TypedArray, or a Node Buffer
+ this.decodeImage = function(data) {
+ var img = CanvasKit.MakeImageFromEncoded(data);
+ if (!img) {
+ throw 'Invalid input';
+ }
+ this._imgs.push(img);
+ return img;
+ }
// A normal <canvas> requires that clients call getContext
this.getContext = function(type) {
@@ -76,6 +87,9 @@
this.dispose = function() {
this._context._dispose();
+ this._imgs.forEach(function(i) {
+ i.delete();
+ });
this._surface.dispose();
}
}
@@ -251,6 +265,8 @@
this._lineDashList = [];
// aka SkBlendMode
this._globalCompositeOperation = CanvasKit.BlendMode.SrcOver;
+ this._imageFilterQuality = CanvasKit.FilterQuality.Low;
+ this._imageSmoothingEnabled = true;
this._paint.setStrokeWidth(this._strokeWidth);
this._paint.setBlendMode(this._globalCompositeOperation);
@@ -524,6 +540,43 @@
}
});
+ Object.defineProperty(this, 'imageSmoothingEnabled', {
+ enumerable: true,
+ get: function() {
+ return this._imageSmoothingEnabled;
+ },
+ set: function(newVal) {
+ this._imageSmoothingEnabled = !!newVal;
+ }
+ });
+
+ Object.defineProperty(this, 'imageSmoothingQuality', {
+ enumerable: true,
+ get: function() {
+ switch (this._imageFilterQuality) {
+ case CanvasKit.FilterQuality.Low:
+ return 'low';
+ case CanvasKit.FilterQuality.Medium:
+ return 'medium';
+ case CanvasKit.FilterQuality.High:
+ return 'high';
+ }
+ },
+ set: function(newQuality) {
+ switch (newQuality) {
+ case 'low':
+ this._imageFilterQuality = CanvasKit.FilterQuality.Low;
+ return;
+ case 'medium':
+ this._imageFilterQuality = CanvasKit.FilterQuality.Medium;
+ return;
+ case 'high':
+ this._imageFilterQuality = CanvasKit.FilterQuality.High;
+ return;
+ }
+ }
+ });
+
Object.defineProperty(this, 'lineCap', {
enumerable: true,
get: function() {
@@ -742,7 +795,7 @@
this._canvas.setMatrix(this._currentTransform);
this._paint.setStyle(CanvasKit.PaintStyle.Fill);
this._paint.setBlendMode(CanvasKit.BlendMode.Clear);
- this._canvas.drawRect(CanvasKit.LTRBRect(x, y, x+width, y+height), this._paint);
+ this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), this._paint);
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
this._paint.setBlendMode(this._globalCompositeOperation);
}
@@ -784,7 +837,7 @@
return rcg;
}
- this._commitSubpath = function () {
+ this._commitSubpath = function() {
if (this._currentSubpath) {
this._currentPath.addPath(this._currentSubpath, false);
this._currentSubpath.delete();
@@ -792,6 +845,43 @@
}
}
+ this._imagePaint = function() {
+ var iPaint = this._fillPaint();
+ if (!this._imageSmoothingEnabled) {
+ iPaint.setFilterQuality(CanvasKit.FilterQuality.None);
+ } else {
+ iPaint.setFilterQuality(this._imageFilterQuality);
+ }
+ return iPaint;
+ }
+
+ this.drawImage = function(img) {
+ // 3 potential sets of arguments
+ // - image, dx, dy
+ // - image, dx, dy, dWidth, dHeight
+ // - image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight
+ this._canvas.setMatrix(this._currentTransform);
+ // use the fillPaint, which has the globalAlpha in it
+ // which drawImageRect will use.
+ var iPaint = this._imagePaint();
+ if (arguments.length === 3 || arguments.length === 5) {
+ var destRect = CanvasKit.XYWHRect(arguments[1], arguments[2],
+ arguments[3] || img.width(), arguments[4] || img.height());
+ var srcRect = CanvasKit.XYWHRect(0, 0, img.width(), img.height());
+ } else if (arguments.length === 9){
+ var destRect = CanvasKit.XYWHRect(arguments[5], arguments[6],
+ arguments[7], arguments[8]);
+ var srcRect = CanvasKit.XYWHRect(arguments[1], arguments[2],
+ arguments[3], arguments[4]);
+ } else {
+ throw 'invalid number of args for drawImage, need 3, 5, or 9; got '+ arguments.length;
+ }
+ this._canvas.drawImageRect(img, srcRect, destRect, iPaint, false);
+
+ this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
+ iPaint.dispose();
+ }
+
this.ellipse = function(x, y, radiusX, radiusY, rotation,
startAngle, endAngle, ccw) {
if (!allAreFinite(arguments)) {
@@ -866,7 +956,7 @@
this.fillRect = function(x, y, width, height) {
var fillPaint = this._fillPaint();
this._canvas.setMatrix(this._currentTransform);
- this._canvas.drawRect(CanvasKit.LTRBRect(x, y, x+width, y+height), fillPaint);
+ this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), fillPaint);
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
fillPaint.dispose();
}
@@ -991,7 +1081,9 @@
this._globalCompositeOperation = newState.gco;
this._paint.setBlendMode(this._globalCompositeOperation);
this._lineDashOffset = newState.ldo;
- //TODO: font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
+ this._imageSmoothingEnabled = newState.ise;
+ this._imageFilterQuality = newState.isq;
+ //TODO: font, textAlign, textBaseline, direction
// restores the clip
this._canvas.restore();
@@ -1034,7 +1126,9 @@
ga: this._globalAlpha,
ldo: this._lineDashOffset,
gco: this._globalCompositeOperation,
- //TODO: font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
+ ise: this._imageSmoothingEnabled,
+ isq: this._imageFilterQuality,
+ //TODO: font, textAlign, textBaseline, direction
});
// Saves the clip
this._canvas.save();
@@ -1161,7 +1255,7 @@
this.strokeRect = function(x, y, width, height) {
var strokePaint = this._strokePaint();
this._canvas.setMatrix(this._currentTransform);
- this._canvas.drawRect(CanvasKit.LTRBRect(x, y, x+width, y+height), strokePaint);
+ this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), strokePaint);
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
strokePaint.dispose();
}
diff --git a/experimental/canvaskit/interface.js b/experimental/canvaskit/interface.js
index 6ecb327..e3d6b3a 100644
--- a/experimental/canvaskit/interface.js
+++ b/experimental/canvaskit/interface.js
@@ -328,6 +328,15 @@
};
}
+ CanvasKit.XYWHRect = function(x, y, w, h) {
+ return {
+ fLeft: x,
+ fTop: y,
+ fRight: x+w,
+ fBottom: y+h,
+ };
+ }
+
var nullptr = 0; // emscripten doesn't like to take null as uintptr_t
// arr can be a normal JS array or a TypedArray
@@ -403,6 +412,26 @@
return dpe;
}
+ // data is a TypedArray or ArrayBuffer
+ CanvasKit.MakeImageFromEncoded = function(data) {
+ data = new Uint8Array(data);
+
+ var iptr = CanvasKit._malloc(data.byteLength);
+ CanvasKit.HEAPU8.set(data, iptr);
+ var img = CanvasKit._decodeImage(iptr, data.byteLength);
+ if (!img) {
+ SkDebug('Could not decode image');
+ CanvasKit._free(iptr);
+ return null;
+ }
+ var realDelete = img.delete.bind(img);
+ img.delete = function() {
+ CanvasKit._free(iptr);
+ realDelete();
+ }
+ return img;
+ }
+
CanvasKit.MakeImageShader = function(imgData, xTileMode, yTileMode) {
var iptr = CanvasKit._malloc(imgData.byteLength);
CanvasKit.HEAPU8.set(new Uint8Array(imgData), iptr);
diff --git a/experimental/canvaskit/karma.conf.js b/experimental/canvaskit/karma.conf.js
index 31f5b46..c324460 100644
--- a/experimental/canvaskit/karma.conf.js
+++ b/experimental/canvaskit/karma.conf.js
@@ -11,13 +11,15 @@
// list of files / patterns to load in the browser
files: [
{ pattern: 'canvaskit/bin/canvaskit.wasm', included:false, served:true},
+ { pattern: 'tests/assets/*', included:false, served:true},
'../../modules/pathkit/tests/testReporter.js',
'canvaskit/bin/canvaskit.js',
'tests/*.spec.js'
],
proxies: {
- '/canvaskit/': '/base/canvaskit/bin/'
+ '/assets/': '/base/tests/assets/',
+ '/canvaskit/': '/base/canvaskit/bin/',
},
// test results reporter to use
diff --git a/experimental/canvaskit/tests/assets/brickwork-texture.jpg b/experimental/canvaskit/tests/assets/brickwork-texture.jpg
new file mode 100644
index 0000000..9a7dd11
--- /dev/null
+++ b/experimental/canvaskit/tests/assets/brickwork-texture.jpg
Binary files differ
diff --git a/experimental/canvaskit/tests/assets/color_wheel.gif b/experimental/canvaskit/tests/assets/color_wheel.gif
new file mode 100644
index 0000000..ec90050
--- /dev/null
+++ b/experimental/canvaskit/tests/assets/color_wheel.gif
Binary files differ
diff --git a/experimental/canvaskit/tests/assets/mandrill_512.png b/experimental/canvaskit/tests/assets/mandrill_512.png
new file mode 100644
index 0000000..c2efb81
--- /dev/null
+++ b/experimental/canvaskit/tests/assets/mandrill_512.png
Binary files differ
diff --git a/experimental/canvaskit/tests/canvas2d.spec.js b/experimental/canvaskit/tests/canvas2d.spec.js
index 570ea0f..07a0ad8 100644
--- a/experimental/canvaskit/tests/canvas2d.spec.js
+++ b/experimental/canvaskit/tests/canvas2d.spec.js
@@ -344,40 +344,77 @@
it('supports gradients, which respect clip/save/restore', function(done) {
LoadCanvasKit.then(catchException(done, () => {
multipleCanvasTest('gradients_clip', done, (canvas) => {
- let ctx = canvas.getContext('2d');
+ let ctx = canvas.getContext('2d');
- var rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300);
+ var rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300);
- rgradient.addColorStop(0, 'red');
- rgradient.addColorStop(.7, 'white');
- rgradient.addColorStop(1, 'blue');
+ rgradient.addColorStop(0, 'red');
+ rgradient.addColorStop(.7, 'white');
+ rgradient.addColorStop(1, 'blue');
- ctx.fillStyle = rgradient;
- ctx.globalAlpha = 0.7;
- ctx.fillRect(0,0,600,600);
- ctx.globalAlpha = 0.95;
+ ctx.fillStyle = rgradient;
+ ctx.globalAlpha = 0.7;
+ ctx.fillRect(0,0,600,600);
+ ctx.globalAlpha = 0.95;
- ctx.beginPath();
- ctx.arc(300, 100, 90, 0, Math.PI*1.66);
- ctx.closePath();
- ctx.strokeStyle = 'yellow';
- ctx.lineWidth = 5;
- ctx.stroke();
- ctx.save();
- ctx.clip();
+ ctx.beginPath();
+ ctx.arc(300, 100, 90, 0, Math.PI*1.66);
+ ctx.closePath();
+ ctx.strokeStyle = 'yellow';
+ ctx.lineWidth = 5;
+ ctx.stroke();
+ ctx.save();
+ ctx.clip();
- var lgradient = ctx.createLinearGradient(200, 20, 420, 40);
+ var lgradient = ctx.createLinearGradient(200, 20, 420, 40);
- lgradient.addColorStop(0, 'green');
- lgradient.addColorStop(.5, 'cyan');
- lgradient.addColorStop(1, 'orange');
+ lgradient.addColorStop(0, 'green');
+ lgradient.addColorStop(.5, 'cyan');
+ lgradient.addColorStop(1, 'orange');
- ctx.fillStyle = lgradient;
+ ctx.fillStyle = lgradient;
- ctx.fillRect(200, 30, 200, 300);
+ ctx.fillRect(200, 30, 200, 300);
- ctx.restore();
- ctx.fillRect(550, 550, 40, 40);
+ ctx.restore();
+ ctx.fillRect(550, 550, 40, 40);
+ });
+ }));
+ });
+
+ it('can draw png images', function(done) {
+ let skImageData = null;
+ let htmlImage = null;
+ let skPromise = fetch('/assets/mandrill_512.png')
+ .then((response) => response.arrayBuffer())
+ .then((buffer) => {
+ skImageData = buffer;
+
+ });
+ let realPromise = fetch('/assets/mandrill_512.png')
+ .then((response) => response.blob())
+ .then((blob) => createImageBitmap(blob))
+ .then((bitmap) => {
+ htmlImage = bitmap;
+ });
+ LoadCanvasKit.then(catchException(done, () => {
+ Promise.all([realPromise, skPromise]).then(() => {
+ multipleCanvasTest('draw_image', done, (canvas) => {
+ let ctx = canvas.getContext('2d');
+ let img = htmlImage;
+ if (canvas._config == 'software_canvas') {
+ img = canvas.decodeImage(skImageData);
+ }
+ ctx.drawImage(img, 30, -200);
+
+ ctx.globalAlpha = 0.7
+ ctx.rotate(.1);
+ ctx.imageSmoothingQuality = 'medium';
+ ctx.drawImage(img, 200, 350, 150, 100);
+ ctx.rotate(-.2);
+ ctx.imageSmoothingEnabled = false;
+ ctx.drawImage(img, 100, 150, 400, 350, 10, 400, 150, 100);
+ });
});
}));
});
@@ -396,7 +433,8 @@
'lineJoin', 'miterLimit', 'shadowOffsetY',
'shadowBlur', 'shadowColor', 'shadowOffsetX',
'globalAlpha', 'globalCompositeOperation',
- 'lineDashOffset'];
+ 'lineDashOffset', 'imageSmoothingEnabled',
+ 'imageFilterQuality'];
// Compare all the default values of the properties of skcanvas
// to the default values on the properties of a real canvas.