[canvaskit] Expose sampling variants of drawImage and drawImageRect

Change-Id: I91f40374de1861385bddb977917f2e7a24fb6a27
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/345130
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Kevin Lubick 2020-12-16 16:00:55 -05:00
parent 7fb39366a0
commit 72f407636e
8 changed files with 183 additions and 13 deletions

View File

@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- `Canvas.drawImageCubic`, `Canvas.drawImageOptions`, `Canvas.drawImageRectCubic`,
`Canvas.drawImageRectOptions` to replace functionality that previously required FilterQuality.
## [0.21.0] - 2020-12-16 ## [0.21.0] - 2020-12-16
### Added ### Added

View File

@ -112,11 +112,17 @@ function canvasTests(CK: CanvasKit, canvas?: Canvas, paint?: Paint, path?: Path,
canvas.drawImage(img, 0, -43, paint); canvas.drawImage(img, 0, -43, paint);
canvas.drawImageAtCurrentFrame(aImg, 0, -43); canvas.drawImageAtCurrentFrame(aImg, 0, -43);
canvas.drawImageAtCurrentFrame(aImg, 0, -43, paint); canvas.drawImageAtCurrentFrame(aImg, 0, -43, paint);
canvas.drawImageCubic(img, 0, -43, 1 / 3, 1 / 4, null);
canvas.drawImageOptions(img, 0, -43, CK.FilterMode.Nearest, CK.MipmapMode.Nearest, paint);
canvas.drawImageNine(img, someRect, someRect, paint); canvas.drawImageNine(img, someRect, someRect, paint);
canvas.drawImageNine(img, CK.XYWHiRect(10, 20, 40, 40), someRect, paint); canvas.drawImageNine(img, CK.XYWHiRect(10, 20, 40, 40), someRect, paint);
canvas.drawImageRect(img, someRect, someRect, paint); canvas.drawImageRect(img, someRect, someRect, paint);
canvas.drawImageRect(img, CK.XYWHRect(90, 90, 40, 40), someRect, paint); canvas.drawImageRect(img, CK.XYWHRect(90, 90, 40, 40), someRect, paint);
canvas.drawImageRect(img, someRect, someRect, paint, true); canvas.drawImageRect(img, someRect, someRect, paint, true);
canvas.drawImageRectCubic(img, someRect, someRect, 1 / 5, 1 / 6);
canvas.drawImageRectCubic(img, someRect, someRect, 1 / 5, 1 / 6, paint);
canvas.drawImageRectOptions(img, someRect, someRect, CK.FilterMode.Linear, CK.MipmapMode.None);
canvas.drawImageRectOptions(img, someRect, someRect, CK.FilterMode.Linear, CK.MipmapMode.None, paint);
canvas.drawLine(1, 2, 3, 4, paint); canvas.drawLine(1, 2, 3, 4, paint);
canvas.drawOval(someRect, paint); canvas.drawOval(someRect, paint);
canvas.drawPaint(paint); canvas.drawPaint(paint);

View File

@ -1039,6 +1039,33 @@ export interface Canvas extends EmbindObject<Canvas> {
*/ */
drawImage(img: Image, left: number, top: number, paint?: Paint): void; drawImage(img: Image, left: number, top: number, paint?: Paint): void;
/**
* Draws the given image with its top-left corner at (left, top) using the current clip,
* the current matrix. It will use the cubic sampling options B and C if necessary.
* @param img
* @param left
* @param top
* @param B - See CubicResampler in SkSamplingOptions.h for more information
* @param C - See CubicResampler in SkSamplingOptions.h for more information
* @param paint
*/
drawImageCubic(img: Image, left: number, top: number, B: number, C: number,
paint: Paint | null): void;
/**
* Draws the given image with its top-left corner at (left, top) using the current clip,
* the current matrix. It will use the provided sampling options if necessary.
* @param img
* @param left
* @param top
* @param fm - The filter mode.
* @param mm - The mipmap mode. Note: for settings other than None, the image must have mipmaps
* calculated with makeCopyWithDefaultMipmaps;
* @param paint
*/
drawImageOptions(img: Image, left: number, top: number, fm: FilterMode,
mm: MipmapMode, paint: Paint | null): void;
/** /**
* Draws the current frame of the given animated image with its top-left corner at * Draws the current frame of the given animated image with its top-left corner at
* (left, top) using the current clip, the current matrix, and optionally-provided paint. * (left, top) using the current clip, the current matrix, and optionally-provided paint.
@ -1072,6 +1099,33 @@ export interface Canvas extends EmbindObject<Canvas> {
drawImageRect(img: Image, src: InputRect, dest: InputRect, paint: Paint, drawImageRect(img: Image, src: InputRect, dest: InputRect, paint: Paint,
fastSample?: boolean): void; fastSample?: boolean): void;
/**
* Draws sub-rectangle src from provided image, scaled and translated to fill dst rectangle.
* It will use the cubic sampling options B and C if necessary.
* @param img
* @param src
* @param dest
* @param B - See CubicResampler in SkSamplingOptions.h for more information
* @param C - See CubicResampler in SkSamplingOptions.h for more information
* @param paint
*/
drawImageRectCubic(img: Image, src: InputRect, dest: InputRect,
B: number, C: number, paint?: Paint): void;
/**
* Draws sub-rectangle src from provided image, scaled and translated to fill dst rectangle.
* It will use the provided sampling options if necessary.
* @param img
* @param src
* @param dest
* @param fm - The filter mode.
* @param mm - The mipmap mode. Note: for settings other than None, the image must have mipmaps
* calculated with makeCopyWithDefaultMipmaps;
* @param paint
*/
drawImageRectOptions(img: Image, src: InputRect, dest: InputRect, fm: FilterMode,
mm: MipmapMode, paint?: Paint): void;
/** /**
* Draws line segment from (x0, y0) to (x1, y1) using the current clip, current matrix, * Draws line segment from (x0, y0) to (x1, y1) using the current clip, current matrix,
* and the provided paint. * and the provided paint.
@ -1597,11 +1651,11 @@ export interface Image extends EmbindObject<Image> {
* Returns this image as a shader with the specified tiling. It will use cubic sampling. * Returns this image as a shader with the specified tiling. It will use cubic sampling.
* @param tx - tile mode in the x direction. * @param tx - tile mode in the x direction.
* @param ty - tile mode in the y direction. * @param ty - tile mode in the y direction.
* @param b - See CubicResampler in SkSamplingOptions.h for more information * @param B - See CubicResampler in SkSamplingOptions.h for more information
* @param c - See CubicResampler in SkSamplingOptions.h for more information * @param C - See CubicResampler in SkSamplingOptions.h for more information
* @param localMatrix * @param localMatrix
*/ */
makeShaderCubic(tx: TileMode, ty: TileMode, b: number, c: number, makeShaderCubic(tx: TileMode, ty: TileMode, B: number, C: number,
localMatrix?: InputMatrix): Shader; localMatrix?: InputMatrix): Shader;
/** /**

View File

@ -894,11 +894,24 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.drawDRRect(ptrToSkRRect(outerPtr), ptrToSkRRect(innerPtr), paint); self.drawDRRect(ptrToSkRRect(outerPtr), ptrToSkRRect(innerPtr), paint);
})) }))
.function("drawImage", select_overload<void (const sk_sp<SkImage>&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers()) .function("drawImage", select_overload<void (const sk_sp<SkImage>&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers())
.function("drawImageCubic", optional_override([](SkCanvas& self, const sk_sp<SkImage>& img,
SkScalar left, SkScalar top,
float B, float C, // See SkSamplingOptions.h for docs.
const SkPaint* paint)->void {
self.drawImage(img.get(), left, top, SkSamplingOptions({B, C}), paint);
}), allow_raw_pointers())
.function("drawImageOptions", optional_override([](SkCanvas& self, const sk_sp<SkImage>& img,
SkScalar left, SkScalar top,
SkFilterMode filter, SkMipmapMode mipmap,
const SkPaint* paint)->void {
self.drawImage(img.get(), left, top, {filter, mipmap}, paint);
}), allow_raw_pointers())
.function("drawImageAtCurrentFrame", optional_override([](SkCanvas& self, sk_sp<SkAnimatedImage> aImg, .function("drawImageAtCurrentFrame", optional_override([](SkCanvas& self, sk_sp<SkAnimatedImage> aImg,
SkScalar left, SkScalar top, const SkPaint* paint)->void { SkScalar left, SkScalar top, const SkPaint* paint)->void {
auto img = aImg->getCurrentFrame(); auto img = aImg->getCurrentFrame();
self.drawImage(img, left, top, paint); self.drawImage(img, left, top, paint);
}), allow_raw_pointers()) }), allow_raw_pointers())
.function("_drawImageNine", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image, .function("_drawImageNine", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
uintptr_t /* int* */ centerPtr, uintptr_t /* float* */ dstPtr, uintptr_t /* int* */ centerPtr, uintptr_t /* float* */ dstPtr,
const SkPaint* paint)->void { const SkPaint* paint)->void {
@ -916,6 +929,22 @@ EMSCRIPTEN_BINDINGS(Skia) {
fastSample ? SkCanvas::kFast_SrcRectConstraint: fastSample ? SkCanvas::kFast_SrcRectConstraint:
SkCanvas::kStrict_SrcRectConstraint); SkCanvas::kStrict_SrcRectConstraint);
}), allow_raw_pointers()) }), allow_raw_pointers())
.function("_drawImageRectCubic", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
uintptr_t /* float* */ srcPtr, uintptr_t /* float* */ dstPtr,
float B, float C, // See SkSamplingOptions.h for docs.
const SkPaint* paint)->void {
const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
self.drawImageRect(image.get(), *src, *dst, SkSamplingOptions({B, C}), paint);
}), allow_raw_pointers())
.function("_drawImageRectOptions", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
uintptr_t /* float* */ srcPtr, uintptr_t /* float* */ dstPtr,
SkFilterMode filter, SkMipmapMode mipmap,
const SkPaint* paint)->void {
const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
self.drawImageRect(image.get(), *src, *dst, {filter, mipmap}, paint);
}), allow_raw_pointers())
.function("drawLine", select_overload<void (SkScalar, SkScalar, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawLine)) .function("drawLine", select_overload<void (SkScalar, SkScalar, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawLine))
.function("_drawOval", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, .function("_drawOval", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr,
const SkPaint& paint)->void { const SkPaint& paint)->void {
@ -1235,7 +1264,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
})) }))
.function("_makeShaderCubic", optional_override([](sk_sp<SkImage> self, .function("_makeShaderCubic", optional_override([](sk_sp<SkImage> self,
SkTileMode tx, SkTileMode ty, SkTileMode tx, SkTileMode ty,
float B, float C, // See SkImage::CubicResampler for docs. float B, float C, // See SkSamplingOptions.h for docs.
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> { uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
return self->makeShader(tx, ty, SkSamplingOptions({B, C}), OptionalMatrix(mPtr)); return self->makeShader(tx, ty, SkSamplingOptions({B, C}), OptionalMatrix(mPtr));
}), allow_raw_pointers()) }), allow_raw_pointers())
@ -1296,6 +1325,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
float* fourFloats = reinterpret_cast<float*>(cPtr); float* fourFloats = reinterpret_cast<float*>(cPtr);
memcpy(fourFloats, c.vec(), 4 * sizeof(SkScalar)); memcpy(fourFloats, c.vec(), 4 * sizeof(SkScalar));
})) }))
.function("getFilterQuality", &SkPaint::getFilterQuality)
.function("getStrokeCap", &SkPaint::getStrokeCap) .function("getStrokeCap", &SkPaint::getStrokeCap)
.function("getStrokeJoin", &SkPaint::getStrokeJoin) .function("getStrokeJoin", &SkPaint::getStrokeJoin)
.function("getStrokeMiter", &SkPaint::getStrokeMiter) .function("getStrokeMiter", &SkPaint::getStrokeMiter)
@ -1315,6 +1345,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.setColor(SkColor4f::FromColor(color), colorSpace.get()); self.setColor(SkColor4f::FromColor(color), colorSpace.get());
})) }))
.function("setColorFilter", &SkPaint::setColorFilter) .function("setColorFilter", &SkPaint::setColorFilter)
.function("setFilterQuality", &SkPaint::setFilterQuality)
.function("setImageFilter", &SkPaint::setImageFilter) .function("setImageFilter", &SkPaint::setImageFilter)
.function("setMaskFilter", &SkPaint::setMaskFilter) .function("setMaskFilter", &SkPaint::setMaskFilter)
.function("setPathEffect", &SkPaint::setPathEffect) .function("setPathEffect", &SkPaint::setPathEffect)

View File

@ -199,6 +199,8 @@ var CanvasKit = {
drawCircle: function() {}, drawCircle: function() {},
drawColorInt: function() {}, drawColorInt: function() {},
drawImage: function() {}, drawImage: function() {},
drawImageCubic: function() {},
drawImageOptions: function() {},
drawImageAtCurrentFrame: function() {}, drawImageAtCurrentFrame: function() {},
drawLine: function() {}, drawLine: function() {},
drawPaint: function() {}, drawPaint: function() {},
@ -227,7 +229,6 @@ var CanvasKit = {
clear: function() {}, clear: function() {},
clipRRect: function() {}, clipRRect: function() {},
clipRect: function() {}, clipRect: function() {},
concat44: function() {}, // deprecated
concat: function() {}, concat: function() {},
drawArc: function() {}, drawArc: function() {},
drawAtlas: function() {}, drawAtlas: function() {},
@ -236,6 +237,8 @@ var CanvasKit = {
drawDRRect: function() {}, drawDRRect: function() {},
drawImageNine: function() {}, drawImageNine: function() {},
drawImageRect: function() {}, drawImageRect: function() {},
drawImageRectCubic: function() {},
drawImageRectOptions: function() {},
drawOval: function() {}, drawOval: function() {},
drawPoints: function() {}, drawPoints: function() {},
drawRect: function() {}, drawRect: function() {},
@ -261,6 +264,8 @@ var CanvasKit = {
_drawDRRect: function() {}, _drawDRRect: function() {},
_drawImageNine: function() {}, _drawImageNine: function() {},
_drawImageRect: function() {}, _drawImageRect: function() {},
_drawImageRectCubic: function() {},
_drawImageRectOptions: function() {},
_drawOval: function() {}, _drawOval: function() {},
_drawPoints: function() {}, _drawPoints: function() {},
_drawRect: function() {}, _drawRect: function() {},

View File

@ -1113,6 +1113,18 @@ CanvasKit.onRuntimeInitialized = function() {
this._drawImageRect(img, sPtr, dPtr, paint, !!fastSample); this._drawImageRect(img, sPtr, dPtr, paint, !!fastSample);
}; };
CanvasKit.Canvas.prototype.drawImageRectCubic = function(img, src, dest, B, C, paint) {
var sPtr = copyRectToWasm(src, _scratchRectPtr);
var dPtr = copyRectToWasm(dest, _scratchRect2Ptr);
this._drawImageRectCubic(img, sPtr, dPtr, B, C, paint || null);
};
CanvasKit.Canvas.prototype.drawImageRectOptions = function(img, src, dest, filter, mipmap, paint) {
var sPtr = copyRectToWasm(src, _scratchRectPtr);
var dPtr = copyRectToWasm(dest, _scratchRect2Ptr);
this._drawImageRectOptions(img, sPtr, dPtr, filter, mipmap, paint || null);
};
CanvasKit.Canvas.prototype.drawOval = function(oval, paint) { CanvasKit.Canvas.prototype.drawOval = function(oval, paint) {
var oPtr = copyRectToWasm(oval); var oPtr = copyRectToWasm(oval);
this._drawOval(oPtr, paint); this._drawOval(oPtr, paint);

View File

@ -200,7 +200,7 @@ describe('Core canvas behavior', () => {
canvas.clear(CanvasKit.WHITE); canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.Paint(); const paint = new CanvasKit.Paint();
const font = new CanvasKit.Font(null, 14); const font = new CanvasKit.Font(null, 14);
canvas.drawText('The following heart should be rotated 90 due to exif.', canvas.drawText('The following heart should be rotated 90 CCW due to exif.',
5, 25, paint, font); 5, 25, paint, font);
// TODO(kjlubick) it would be nice to also to test MakeAnimatedImageFromEncoded but // TODO(kjlubick) it would be nice to also to test MakeAnimatedImageFromEncoded but
@ -376,15 +376,15 @@ describe('Core canvas behavior', () => {
const skImage4 = CanvasKit.MakeImageFromCanvasImageSource(image2); const skImage4 = CanvasKit.MakeImageFromCanvasImageSource(image2);
// Draw decoded images // Draw decoded images
const sourceRect = CanvasKit.XYWHRect(0,0, 150, 150); const sourceRect = CanvasKit.XYWHRect(0, 0, 150, 150);
canvas.drawImageRect(skImage1, sourceRect, CanvasKit.XYWHRect(0,row * 100, 90, 90), null, false); canvas.drawImageRect(skImage1, sourceRect, CanvasKit.XYWHRect(0, row * 100, 90, 90), null, false);
canvas.drawImageRect(skImage2, sourceRect, CanvasKit.XYWHRect(100,row * 100, 90, 90), null, false); canvas.drawImageRect(skImage2, sourceRect, CanvasKit.XYWHRect(100, row * 100, 90, 90), null, false);
canvas.drawImageRect(skImage3, sourceRect, CanvasKit.XYWHRect(200,row * 100, 90, 90), null, false); canvas.drawImageRect(skImage3, sourceRect, CanvasKit.XYWHRect(200, row * 100, 90, 90), null, false);
canvas.drawImageRect(skImage4, sourceRect, CanvasKit.XYWHRect(300,row * 100, 90, 90), null, false); canvas.drawImageRect(skImage4, sourceRect, CanvasKit.XYWHRect(300, row * 100, 90, 90), null, false);
row++; row++;
} }
//Label images with the method used to decode them // Label images with the method used to decode them
const paint = new CanvasKit.Paint(); const paint = new CanvasKit.Paint();
const textFont = new CanvasKit.Font(null, 7); const textFont = new CanvasKit.Font(null, 7);
canvas.drawText('WASM Decoding', 0, 90, paint, textFont); canvas.drawText('WASM Decoding', 0, 90, paint, textFont);
@ -724,6 +724,61 @@ describe('Core canvas behavior', () => {
img.delete(); img.delete();
}, '/assets/flightAnim.gif'); }, '/assets/flightAnim.gif');
gm('drawImageVariants', (canvas, fetchedByteBuffers) => {
const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
expect(img).toBeTruthy();
canvas.clear(CanvasKit.WHITE);
canvas.scale(2, 2);
const paint = new CanvasKit.Paint();
const clipTo = (x, y) => {
canvas.save();
canvas.clipRect(CanvasKit.XYWHRect(x, y, 128, 128), CanvasKit.ClipOp.Intersect);
};
clipTo(0, 0);
canvas.drawImage(img, 0, 0, paint);
canvas.restore();
clipTo(128, 0);
canvas.drawImageCubic(img, 128, 0, 1/3, 1/3, null);
canvas.restore();
clipTo(0, 128);
canvas.drawImageOptions(img, 0, 128, CanvasKit.FilterMode.Linear, CanvasKit.MipmapMode.None, null);
canvas.restore();
const mipImg = img.makeCopyWithDefaultMipmaps();
clipTo(128, 128);
canvas.drawImageOptions(mipImg, 128, 128,
CanvasKit.FilterMode.Nearest, CanvasKit.MipmapMode.Nearest, null);
canvas.restore();
paint.delete();
mipImg.delete();
img.delete();
}, '/assets/mandrill_512.png');
gm('drawImageRectVariants', (canvas, fetchedByteBuffers) => {
const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
expect(img).toBeTruthy();
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.Paint();
const src = CanvasKit.XYWHRect(100, 100, 128, 128);
canvas.drawImageRect(img, src, CanvasKit.XYWHRect(0, 0, 256, 256), paint);
canvas.drawImageRectCubic(img, src, CanvasKit.XYWHRect(256, 0, 256, 256), 1/3, 1/3);
canvas.drawImageRectOptions(img, src, CanvasKit.XYWHRect(0, 256, 256, 256),
CanvasKit.FilterMode.Linear, CanvasKit.MipmapMode.None);
const mipImg = img.makeCopyWithDefaultMipmaps();
canvas.drawImageRectOptions(mipImg, src, CanvasKit.XYWHRect(256, 256, 256, 256),
CanvasKit.FilterMode.Nearest, CanvasKit.MipmapMode.Nearest);
paint.delete();
mipImg.delete();
img.delete();
}, '/assets/mandrill_512.png');
gm('drawImage_skp', (canvas, fetchedByteBuffers) => { gm('drawImage_skp', (canvas, fetchedByteBuffers) => {
const pic = CanvasKit.MakePicture(fetchedByteBuffers[0]); const pic = CanvasKit.MakePicture(fetchedByteBuffers[0]);
expect(pic).toBeTruthy(); expect(pic).toBeTruthy();

View File

@ -201,6 +201,9 @@ function reportSurface(surface, testname, done) {
const reportingCanvas = document.getElementById('report'); const reportingCanvas = document.getElementById('report');
reportingCanvas.getContext('2d').putImageData(imageData, 0, 0); reportingCanvas.getContext('2d').putImageData(imageData, 0, 0);
if (!done) {
return;
}
reportCanvas(reportingCanvas, testname).then(() => { reportCanvas(reportingCanvas, testname).then(() => {
// TODO(kjlubick): should we call surface.delete() here? // TODO(kjlubick): should we call surface.delete() here?
done(); done();