[canvaskit] Add shadow and save/restore support
This also exposes the ShadowUtils::drawShadow on Canvas, even though it wasn't what was needed to duplicate the Canvas effect. Bug: skia: Change-Id: I12276ef106244218e4827b7fcd7949c83cf13e5f Reviewed-on: https://skia-review.googlesource.com/c/172967 Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
parent
2a1848d2aa
commit
61ef7b2589
@ -24,6 +24,8 @@
|
|||||||
<canvas id=api2_c width=300 height=300></canvas>
|
<canvas id=api2_c width=300 height=300></canvas>
|
||||||
<img id=api3 width=300 height=300>
|
<img id=api3 width=300 height=300>
|
||||||
<canvas id=api3_c width=300 height=300></canvas>
|
<canvas id=api3_c width=300 height=300></canvas>
|
||||||
|
<img id=api4 width=300 height=300>
|
||||||
|
<canvas id=api4_c width=300 height=300></canvas>
|
||||||
|
|
||||||
<h2> CanvasKit draws Paths to the browser</h2>
|
<h2> CanvasKit draws Paths to the browser</h2>
|
||||||
<canvas id=vertex1 width=300 height=300></canvas>
|
<canvas id=vertex1 width=300 height=300></canvas>
|
||||||
@ -91,6 +93,7 @@
|
|||||||
CanvasAPI1(CanvasKit);
|
CanvasAPI1(CanvasKit);
|
||||||
CanvasAPI2(CanvasKit);
|
CanvasAPI2(CanvasKit);
|
||||||
CanvasAPI3(CanvasKit);
|
CanvasAPI3(CanvasKit);
|
||||||
|
CanvasAPI4(CanvasKit);
|
||||||
|
|
||||||
VertexAPI1(CanvasKit);
|
VertexAPI1(CanvasKit);
|
||||||
VertexAPI2(CanvasKit, bonesImage);
|
VertexAPI2(CanvasKit, bonesImage);
|
||||||
@ -556,6 +559,59 @@
|
|||||||
document.getElementById('api3').src = skcanvas.toDataURL();
|
document.getElementById('api3').src = skcanvas.toDataURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CanvasAPI4(CanvasKit) {
|
||||||
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
||||||
|
let realCanvas = document.getElementById('api4_c');
|
||||||
|
realCanvas.width = 300;
|
||||||
|
realCanvas.height = 300;
|
||||||
|
|
||||||
|
for (let canvas of [skcanvas, realCanvas]) {
|
||||||
|
let ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.fillStyle = '#CCC';
|
||||||
|
ctx.shadowColor = 'rebeccapurple';
|
||||||
|
ctx.shadowBlur = 1;
|
||||||
|
ctx.shadowOffsetX = 3;
|
||||||
|
ctx.shadowOffsetY = -8;
|
||||||
|
ctx.rect(10, 10, 30, 30);
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = '#C00';
|
||||||
|
ctx.fillStyle = '#00C';
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
ctx.shadowColor = 'transparent';
|
||||||
|
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(36, 148);
|
||||||
|
ctx.quadraticCurveTo(66, 188, 120, 136);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.shadowColor = '#993366AA';
|
||||||
|
ctx.shadowOffsetX = 8;
|
||||||
|
ctx.shadowBlur = 5;
|
||||||
|
ctx.setTransform(2, 0, -.5, 2.5, -40, 120);
|
||||||
|
ctx.rect(110, 10, 20, 20);
|
||||||
|
ctx.lineTo(110, 0);
|
||||||
|
ctx.resetTransform();
|
||||||
|
ctx.lineTo(220, 120);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.fillStyle = 'green';
|
||||||
|
ctx.font = '16pt Arial';
|
||||||
|
ctx.fillText('This should be shadowed', 20, 80);
|
||||||
|
|
||||||
|
}
|
||||||
|
document.getElementById('api4').src = skcanvas.toDataURL();
|
||||||
|
}
|
||||||
|
|
||||||
function NimaExample(CanvasKit, nimaFile, nimaTexture) {
|
function NimaExample(CanvasKit, nimaFile, nimaTexture) {
|
||||||
if (!CanvasKit || !nimaFile || !nimaTexture) {
|
if (!CanvasKit || !nimaFile || !nimaTexture) {
|
||||||
return;
|
return;
|
||||||
|
@ -22,9 +22,11 @@
|
|||||||
#include "SkDiscretePathEffect.h"
|
#include "SkDiscretePathEffect.h"
|
||||||
#include "SkEncodedImageFormat.h"
|
#include "SkEncodedImageFormat.h"
|
||||||
#include "SkFontMgr.h"
|
#include "SkFontMgr.h"
|
||||||
|
#include "SkBlurTypes.h"
|
||||||
#include "SkFontMgrPriv.h"
|
#include "SkFontMgrPriv.h"
|
||||||
#include "SkGradientShader.h"
|
#include "SkGradientShader.h"
|
||||||
#include "SkImageShader.h"
|
#include "SkImageShader.h"
|
||||||
|
#include "SkMaskFilter.h"
|
||||||
#include "SkPaint.h"
|
#include "SkPaint.h"
|
||||||
#include "SkParsePath.h"
|
#include "SkParsePath.h"
|
||||||
#include "SkPath.h"
|
#include "SkPath.h"
|
||||||
@ -32,6 +34,7 @@
|
|||||||
#include "SkPathOps.h"
|
#include "SkPathOps.h"
|
||||||
#include "SkScalar.h"
|
#include "SkScalar.h"
|
||||||
#include "SkShader.h"
|
#include "SkShader.h"
|
||||||
|
#include "SkShadowUtils.h"
|
||||||
#include "SkString.h"
|
#include "SkString.h"
|
||||||
#include "SkStrokeRec.h"
|
#include "SkStrokeRec.h"
|
||||||
#include "SkSurface.h"
|
#include "SkSurface.h"
|
||||||
@ -349,6 +352,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
|
function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
|
||||||
function("MakeSkCornerPathEffect", &SkCornerPathEffect::Make, allow_raw_pointers());
|
function("MakeSkCornerPathEffect", &SkCornerPathEffect::Make, allow_raw_pointers());
|
||||||
function("MakeSkDiscretePathEffect", &SkDiscretePathEffect::Make, allow_raw_pointers());
|
function("MakeSkDiscretePathEffect", &SkDiscretePathEffect::Make, allow_raw_pointers());
|
||||||
|
function("MakeBlurMaskFilter", optional_override([](SkBlurStyle style, SkScalar sigma, bool respectCTM)->sk_sp<SkMaskFilter> {
|
||||||
|
// Adds a little helper because emscripten doesn't expose default params.
|
||||||
|
return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
|
||||||
|
}), allow_raw_pointers());
|
||||||
function("MakePathFromOp", &MakePathFromOp);
|
function("MakePathFromOp", &MakePathFromOp);
|
||||||
|
|
||||||
// These won't be called directly, there's a JS helper to deal with typed arrays.
|
// These won't be called directly, there's a JS helper to deal with typed arrays.
|
||||||
@ -445,7 +452,16 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
.function("drawPaint", &SkCanvas::drawPaint)
|
.function("drawPaint", &SkCanvas::drawPaint)
|
||||||
.function("drawPath", &SkCanvas::drawPath)
|
.function("drawPath", &SkCanvas::drawPath)
|
||||||
.function("drawRect", &SkCanvas::drawRect)
|
.function("drawRect", &SkCanvas::drawRect)
|
||||||
.function("drawText", optional_override([](SkCanvas& self, std::string text, SkScalar x, SkScalar y, const SkPaint& p) {
|
.function("drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
|
||||||
|
const SkPoint3& zPlaneParams,
|
||||||
|
const SkPoint3& lightPos, SkScalar lightRadius,
|
||||||
|
JSColor ambientColor, JSColor spotColor,
|
||||||
|
uint32_t flags) {
|
||||||
|
SkShadowUtils::DrawShadow(&self, path, zPlaneParams, lightPos, lightRadius,
|
||||||
|
SkColor(ambientColor), SkColor(spotColor), flags);
|
||||||
|
}))
|
||||||
|
.function("drawText", optional_override([](SkCanvas& self, std::string text, SkScalar x,
|
||||||
|
SkScalar y, const SkPaint& p) {
|
||||||
// TODO(kjlubick): This does not work well for non-ascii
|
// TODO(kjlubick): This does not work well for non-ascii
|
||||||
// Need to maybe add a helper in interface.js that supports UTF-8
|
// Need to maybe add a helper in interface.js that supports UTF-8
|
||||||
// Otherwise, go with std::wstring and set UTF-32 encoding.
|
// Otherwise, go with std::wstring and set UTF-32 encoding.
|
||||||
@ -471,12 +487,20 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
.function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData))
|
.function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData))
|
||||||
.function("_encodeToDataWithFormat", select_overload<sk_sp<SkData>(SkEncodedImageFormat encodedImageFormat, int quality)const>(&SkImage::encodeToData));
|
.function("_encodeToDataWithFormat", select_overload<sk_sp<SkData>(SkEncodedImageFormat encodedImageFormat, int quality)const>(&SkImage::encodeToData));
|
||||||
|
|
||||||
|
class_<SkMaskFilter>("SkMaskFilter")
|
||||||
|
.smart_ptr<sk_sp<SkMaskFilter>>("sk_sp<SkMaskFilter>");
|
||||||
|
|
||||||
class_<SkPaint>("SkPaint")
|
class_<SkPaint>("SkPaint")
|
||||||
.constructor<>()
|
.constructor<>()
|
||||||
.function("copy", optional_override([](const SkPaint& self)->SkPaint {
|
.function("copy", optional_override([](const SkPaint& self)->SkPaint {
|
||||||
SkPaint p(self);
|
SkPaint p(self);
|
||||||
return p;
|
return p;
|
||||||
}))
|
}))
|
||||||
|
.function("getColor", optional_override([](SkPaint& self)->JSColor {
|
||||||
|
// JS side gives us a signed int instead of an unsigned int for color
|
||||||
|
// Add a optional_override to change it out.
|
||||||
|
return JSColor(self.getColor());
|
||||||
|
}))
|
||||||
.function("getStrokeWidth", &SkPaint::getStrokeWidth)
|
.function("getStrokeWidth", &SkPaint::getStrokeWidth)
|
||||||
.function("getStrokeMiter", &SkPaint::getStrokeMiter)
|
.function("getStrokeMiter", &SkPaint::getStrokeMiter)
|
||||||
.function("getStrokeCap", &SkPaint::getStrokeCap)
|
.function("getStrokeCap", &SkPaint::getStrokeCap)
|
||||||
@ -494,6 +518,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
// Add a optional_override to change it out.
|
// Add a optional_override to change it out.
|
||||||
self.setColor(SkColor(color));
|
self.setColor(SkColor(color));
|
||||||
}))
|
}))
|
||||||
|
.function("setMaskFilter", &SkPaint::setMaskFilter)
|
||||||
.function("setPathEffect", &SkPaint::setPathEffect)
|
.function("setPathEffect", &SkPaint::setPathEffect)
|
||||||
.function("setShader", &SkPaint::setShader)
|
.function("setShader", &SkPaint::setShader)
|
||||||
.function("setStrokeWidth", &SkPaint::setStrokeWidth)
|
.function("setStrokeWidth", &SkPaint::setStrokeWidth)
|
||||||
@ -616,10 +641,11 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
.value("Color", SkBlendMode::kColor)
|
.value("Color", SkBlendMode::kColor)
|
||||||
.value("Luminosity", SkBlendMode::kLuminosity);
|
.value("Luminosity", SkBlendMode::kLuminosity);
|
||||||
|
|
||||||
enum_<SkPaint::Style>("PaintStyle")
|
enum_<SkBlurStyle>("BlurStyle")
|
||||||
.value("Fill", SkPaint::Style::kFill_Style)
|
.value("Normal", SkBlurStyle::kNormal_SkBlurStyle)
|
||||||
.value("Stroke", SkPaint::Style::kStroke_Style)
|
.value("Solid", SkBlurStyle::kSolid_SkBlurStyle)
|
||||||
.value("StrokeAndFill", SkPaint::Style::kStrokeAndFill_Style);
|
.value("Outer", SkBlurStyle::kOuter_SkBlurStyle)
|
||||||
|
.value("Inner", SkBlurStyle::kInner_SkBlurStyle);
|
||||||
|
|
||||||
enum_<SkPath::FillType>("FillType")
|
enum_<SkPath::FillType>("FillType")
|
||||||
.value("Winding", SkPath::FillType::kWinding_FillType)
|
.value("Winding", SkPath::FillType::kWinding_FillType)
|
||||||
@ -627,6 +653,15 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
.value("InverseWinding", SkPath::FillType::kInverseWinding_FillType)
|
.value("InverseWinding", SkPath::FillType::kInverseWinding_FillType)
|
||||||
.value("InverseEvenOdd", SkPath::FillType::kInverseEvenOdd_FillType);
|
.value("InverseEvenOdd", SkPath::FillType::kInverseEvenOdd_FillType);
|
||||||
|
|
||||||
|
enum_<SkEncodedImageFormat>("ImageFormat")
|
||||||
|
.value("PNG", SkEncodedImageFormat::kPNG)
|
||||||
|
.value("JPEG", SkEncodedImageFormat::kJPEG);
|
||||||
|
|
||||||
|
enum_<SkPaint::Style>("PaintStyle")
|
||||||
|
.value("Fill", SkPaint::Style::kFill_Style)
|
||||||
|
.value("Stroke", SkPaint::Style::kStroke_Style)
|
||||||
|
.value("StrokeAndFill", SkPaint::Style::kStrokeAndFill_Style);
|
||||||
|
|
||||||
enum_<SkPathOp>("PathOp")
|
enum_<SkPathOp>("PathOp")
|
||||||
.value("Difference", SkPathOp::kDifference_SkPathOp)
|
.value("Difference", SkPathOp::kDifference_SkPathOp)
|
||||||
.value("Intersect", SkPathOp::kIntersect_SkPathOp)
|
.value("Intersect", SkPathOp::kIntersect_SkPathOp)
|
||||||
@ -660,10 +695,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
.value("TrianglesStrip", SkVertices::VertexMode::kTriangleStrip_VertexMode)
|
.value("TrianglesStrip", SkVertices::VertexMode::kTriangleStrip_VertexMode)
|
||||||
.value("TriangleFan", SkVertices::VertexMode::kTriangleFan_VertexMode);
|
.value("TriangleFan", SkVertices::VertexMode::kTriangleFan_VertexMode);
|
||||||
|
|
||||||
enum_<SkEncodedImageFormat>("ImageFormat")
|
|
||||||
.value("PNG", SkEncodedImageFormat::kPNG)
|
|
||||||
.value("JPEG", SkEncodedImageFormat::kJPEG);
|
|
||||||
|
|
||||||
|
|
||||||
// A value object is much simpler than a class - it is returned as a JS
|
// A value object is much simpler than a class - it is returned as a JS
|
||||||
// object and does not require delete().
|
// object and does not require delete().
|
||||||
@ -685,6 +716,12 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
.element(&SkPoint::fX)
|
.element(&SkPoint::fX)
|
||||||
.element(&SkPoint::fY);
|
.element(&SkPoint::fY);
|
||||||
|
|
||||||
|
// SkPoint3s can be represented by [x, y, z]
|
||||||
|
value_array<SkPoint3>("SkPoint3")
|
||||||
|
.element(&SkPoint3::fX)
|
||||||
|
.element(&SkPoint3::fY)
|
||||||
|
.element(&SkPoint3::fZ);
|
||||||
|
|
||||||
// {"w": Number, "h", Number}
|
// {"w": Number, "h", Number}
|
||||||
value_object<SkSize>("SkSize")
|
value_object<SkSize>("SkSize")
|
||||||
.field("w", &SkSize::fWidth)
|
.field("w", &SkSize::fWidth)
|
||||||
@ -717,6 +754,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
|||||||
constant("BLUE", (JSColor) SK_ColorBLUE);
|
constant("BLUE", (JSColor) SK_ColorBLUE);
|
||||||
constant("YELLOW", (JSColor) SK_ColorYELLOW);
|
constant("YELLOW", (JSColor) SK_ColorYELLOW);
|
||||||
constant("CYAN", (JSColor) SK_ColorCYAN);
|
constant("CYAN", (JSColor) SK_ColorCYAN);
|
||||||
|
constant("BLACK", (JSColor) SK_ColorBLACK);
|
||||||
// TODO(?)
|
// TODO(?)
|
||||||
|
|
||||||
#if SK_INCLUDE_SKOTTIE
|
#if SK_INCLUDE_SKOTTIE
|
||||||
|
@ -27,19 +27,21 @@ var CanvasKit = {
|
|||||||
Color: function() {},
|
Color: function() {},
|
||||||
/** @return {CanvasKit.SkRect} */
|
/** @return {CanvasKit.SkRect} */
|
||||||
LTRBRect: function() {},
|
LTRBRect: function() {},
|
||||||
|
MakeBlurMaskFilter: function() {},
|
||||||
MakeCanvas: function() {},
|
MakeCanvas: function() {},
|
||||||
MakeCanvasSurface: function() {},
|
MakeCanvasSurface: function() {},
|
||||||
MakeSWCanvasSurface: function() {},
|
|
||||||
MakeWebGLCanvasSurface: function() {},
|
|
||||||
MakeImageShader: function() {},
|
MakeImageShader: function() {},
|
||||||
MakeLinearGradientShader: function() {},
|
MakeLinearGradientShader: function() {},
|
||||||
MakeRadialGradientShader: function() {},
|
|
||||||
MakeNimaActor: function() {},
|
MakeNimaActor: function() {},
|
||||||
|
MakeRadialGradientShader: function() {},
|
||||||
|
MakeSWCanvasSurface: function() {},
|
||||||
MakeSkDashPathEffect: function() {},
|
MakeSkDashPathEffect: function() {},
|
||||||
MakeSkVertices: function() {},
|
MakeSkVertices: function() {},
|
||||||
MakeSurface: function() {},
|
MakeSurface: function() {},
|
||||||
|
MakeWebGLCanvasSurface: function() {},
|
||||||
currentContext: function() {},
|
currentContext: function() {},
|
||||||
getSkDataBytes: function() {},
|
getSkDataBytes: function() {},
|
||||||
|
getColorComponents: function() {},
|
||||||
initFonts: function() {},
|
initFonts: function() {},
|
||||||
setCurrentContext: function() {},
|
setCurrentContext: function() {},
|
||||||
|
|
||||||
@ -78,6 +80,7 @@ var CanvasKit = {
|
|||||||
drawPaint: function() {},
|
drawPaint: function() {},
|
||||||
drawPath: function() {},
|
drawPath: function() {},
|
||||||
drawText: function() {},
|
drawText: function() {},
|
||||||
|
drawShadow: function() {},
|
||||||
flush: function() {},
|
flush: function() {},
|
||||||
rotate: function() {},
|
rotate: function() {},
|
||||||
save: function() {},
|
save: function() {},
|
||||||
@ -110,6 +113,7 @@ var CanvasKit = {
|
|||||||
// public API (from C++ bindings)
|
// public API (from C++ bindings)
|
||||||
/** @return {CanvasKit.SkPaint} */
|
/** @return {CanvasKit.SkPaint} */
|
||||||
copy: function() {},
|
copy: function() {},
|
||||||
|
getColor: function() {},
|
||||||
getStrokeCap: function() {},
|
getStrokeCap: function() {},
|
||||||
getStrokeJoin: function() {},
|
getStrokeJoin: function() {},
|
||||||
getStrokeMiter: function() {},
|
getStrokeMiter: function() {},
|
||||||
@ -118,6 +122,7 @@ var CanvasKit = {
|
|||||||
measureText: function() {},
|
measureText: function() {},
|
||||||
setAntiAlias: function() {},
|
setAntiAlias: function() {},
|
||||||
setColor: function() {},
|
setColor: function() {},
|
||||||
|
setMaskFilter: function() {},
|
||||||
setPathEffect: function() {},
|
setPathEffect: function() {},
|
||||||
setShader: function() {},
|
setShader: function() {},
|
||||||
setStrokeCap: function() {},
|
setStrokeCap: function() {},
|
||||||
@ -203,6 +208,52 @@ var CanvasKit = {
|
|||||||
gpu: {},
|
gpu: {},
|
||||||
skottie: {},
|
skottie: {},
|
||||||
|
|
||||||
|
TRANSPARENT: {},
|
||||||
|
RED: {},
|
||||||
|
BLUE: {},
|
||||||
|
YELLOW: {},
|
||||||
|
CYAN: {},
|
||||||
|
BLACK: {},
|
||||||
|
|
||||||
|
BlendMode: {
|
||||||
|
Clear: {},
|
||||||
|
Src: {},
|
||||||
|
Dst: {},
|
||||||
|
SrcOver: {},
|
||||||
|
DstOver: {},
|
||||||
|
SrcIn: {},
|
||||||
|
DstIn: {},
|
||||||
|
SrcOut: {},
|
||||||
|
DstOut: {},
|
||||||
|
SrcATop: {},
|
||||||
|
DstATop: {},
|
||||||
|
Xor: {},
|
||||||
|
Plus: {},
|
||||||
|
Modulate: {},
|
||||||
|
Screen: {},
|
||||||
|
Overlay: {},
|
||||||
|
Darken: {},
|
||||||
|
Lighten: {},
|
||||||
|
ColorDodge: {},
|
||||||
|
ColorBurn: {},
|
||||||
|
HardLight: {},
|
||||||
|
SoftLight: {},
|
||||||
|
Difference: {},
|
||||||
|
Exclusion: {},
|
||||||
|
Multiply: {},
|
||||||
|
Hue: {},
|
||||||
|
Saturation: {},
|
||||||
|
Color: {},
|
||||||
|
Luminosity: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
BlurStyle: {
|
||||||
|
Normal: {},
|
||||||
|
Solid: {},
|
||||||
|
Outer: {},
|
||||||
|
Inner: {},
|
||||||
|
},
|
||||||
|
|
||||||
FillType: {
|
FillType: {
|
||||||
Winding: {},
|
Winding: {},
|
||||||
EvenOdd: {},
|
EvenOdd: {},
|
||||||
@ -312,6 +363,7 @@ CanvasRenderingContext2D.prototype.clearHitRegions = function() {};
|
|||||||
CanvasRenderingContext2D.prototype.closePath = function() {};
|
CanvasRenderingContext2D.prototype.closePath = function() {};
|
||||||
CanvasRenderingContext2D.prototype.drawFocusIfNeeded = function() {};
|
CanvasRenderingContext2D.prototype.drawFocusIfNeeded = function() {};
|
||||||
CanvasRenderingContext2D.prototype.ellipse = function() {};
|
CanvasRenderingContext2D.prototype.ellipse = function() {};
|
||||||
|
CanvasRenderingContext2D.prototype.fill = function() {};
|
||||||
CanvasRenderingContext2D.prototype.fillText = function() {};
|
CanvasRenderingContext2D.prototype.fillText = function() {};
|
||||||
CanvasRenderingContext2D.prototype.lineTo = function() {};
|
CanvasRenderingContext2D.prototype.lineTo = function() {};
|
||||||
CanvasRenderingContext2D.prototype.measureText = function() {};
|
CanvasRenderingContext2D.prototype.measureText = function() {};
|
||||||
@ -320,7 +372,9 @@ CanvasRenderingContext2D.prototype.quadraticCurveTo = function() {};
|
|||||||
CanvasRenderingContext2D.prototype.rect = function() {};
|
CanvasRenderingContext2D.prototype.rect = function() {};
|
||||||
CanvasRenderingContext2D.prototype.removeHitRegion = function() {};
|
CanvasRenderingContext2D.prototype.removeHitRegion = function() {};
|
||||||
CanvasRenderingContext2D.prototype.resetTransform = function() {};
|
CanvasRenderingContext2D.prototype.resetTransform = function() {};
|
||||||
|
CanvasRenderingContext2D.prototype.restore = function() {};
|
||||||
CanvasRenderingContext2D.prototype.rotate = function() {};
|
CanvasRenderingContext2D.prototype.rotate = function() {};
|
||||||
|
CanvasRenderingContext2D.prototype.save = function() {};
|
||||||
CanvasRenderingContext2D.prototype.scale = function() {};
|
CanvasRenderingContext2D.prototype.scale = function() {};
|
||||||
CanvasRenderingContext2D.prototype.scrollPathIntoView = function() {};
|
CanvasRenderingContext2D.prototype.scrollPathIntoView = function() {};
|
||||||
CanvasRenderingContext2D.prototype.setTransform = function() {};
|
CanvasRenderingContext2D.prototype.setTransform = function() {};
|
||||||
|
@ -17,4 +17,15 @@
|
|||||||
}
|
}
|
||||||
return (clamp(a*255) << 24) | (clamp(r) << 16) | (clamp(g) << 8) | (clamp(b) << 0);
|
return (clamp(a*255) << 24) | (clamp(r) << 16) | (clamp(g) << 8) | (clamp(b) << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns [r, g, b, a] from a color
|
||||||
|
// where a is scaled between 0 and 1.0
|
||||||
|
CanvasKit.getColorComponents = function(color) {
|
||||||
|
return [
|
||||||
|
(color >> 16) & 0xFF,
|
||||||
|
(color >> 8) & 0xFF,
|
||||||
|
(color >> 0) & 0xFF,
|
||||||
|
((color >> 24) & 0xFF) / 255,
|
||||||
|
]
|
||||||
|
}
|
||||||
}(Module)); // When this file is loaded in, the high level object is "Module";
|
}(Module)); // When this file is loaded in, the high level object is "Module";
|
||||||
|
@ -80,10 +80,19 @@
|
|||||||
this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt);
|
this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt);
|
||||||
this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter);
|
this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter);
|
||||||
|
|
||||||
|
this._strokeColor = CanvasKit.BLACK;
|
||||||
|
this._fillColor = CanvasKit.BLACK;
|
||||||
|
this._shadowBlur = 0;
|
||||||
|
this._shadowColor = CanvasKit.TRANSPARENT;
|
||||||
|
this._shadowOffsetX = 0;
|
||||||
|
this._shadowOffsetY = 0;
|
||||||
|
|
||||||
this._currentPath = new CanvasKit.SkPath();
|
this._currentPath = new CanvasKit.SkPath();
|
||||||
this._currentSubpath = null;
|
this._currentSubpath = null;
|
||||||
this._currentTransform = CanvasKit.SkMatrix.identity();
|
this._currentTransform = CanvasKit.SkMatrix.identity();
|
||||||
|
|
||||||
|
this._canvasStateStack = [];
|
||||||
|
|
||||||
this._dispose = function() {
|
this._dispose = function() {
|
||||||
this._currentPath.delete();
|
this._currentPath.delete();
|
||||||
this._currentSubpath && this._currentSubpath.delete();
|
this._currentSubpath && this._currentSubpath.delete();
|
||||||
@ -92,17 +101,88 @@
|
|||||||
// by the surface of which it is based.
|
// by the surface of which it is based.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'fillStyle', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return colorToString(this._fillColor);
|
||||||
|
},
|
||||||
|
set: function(newStyle) {
|
||||||
|
this._fillColor = parseColor(newStyle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, 'font', {
|
Object.defineProperty(this, 'font', {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
|
get: function(newStyle) {
|
||||||
|
// TODO generate this
|
||||||
|
return '10px sans-serif';
|
||||||
|
},
|
||||||
set: function(newStyle) {
|
set: function(newStyle) {
|
||||||
var size = parseFontSize(newStyle);
|
var size = parseFontSize(newStyle);
|
||||||
// TODO styles
|
// TODO(kjlubick) styles, font name
|
||||||
this._paint.setTextSize(size);
|
this._paint.setTextSize(size);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'lineCap', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
switch (this._paint.getStrokeCap()) {
|
||||||
|
case CanvasKit.StrokeCap.Butt:
|
||||||
|
return 'butt';
|
||||||
|
case CanvasKit.StrokeCap.Round:
|
||||||
|
return 'round';
|
||||||
|
case CanvasKit.StrokeCap.Square:
|
||||||
|
return 'square';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set: function(newCap) {
|
||||||
|
switch (newCap) {
|
||||||
|
case 'butt':
|
||||||
|
this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt);
|
||||||
|
return;
|
||||||
|
case 'round':
|
||||||
|
this._paint.setStrokeCap(CanvasKit.StrokeCap.Round);
|
||||||
|
return;
|
||||||
|
case 'square':
|
||||||
|
this._paint.setStrokeCap(CanvasKit.StrokeCap.Square);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'lineJoin', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
switch (this._paint.getStrokeJoin()) {
|
||||||
|
case CanvasKit.StrokeJoin.Miter:
|
||||||
|
return 'miter';
|
||||||
|
case CanvasKit.StrokeJoin.Round:
|
||||||
|
return 'round';
|
||||||
|
case CanvasKit.StrokeJoin.Bevel:
|
||||||
|
return 'bevel';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set: function(newJoin) {
|
||||||
|
switch (newJoin) {
|
||||||
|
case 'miter':
|
||||||
|
this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter);
|
||||||
|
return;
|
||||||
|
case 'round':
|
||||||
|
this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Round);
|
||||||
|
return;
|
||||||
|
case 'bevel':
|
||||||
|
this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Bevel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, 'lineWidth', {
|
Object.defineProperty(this, 'lineWidth', {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return this._paint.getStrokeWidth();
|
||||||
|
},
|
||||||
set: function(newWidth) {
|
set: function(newWidth) {
|
||||||
if (newWidth <= 0 || !newWidth) {
|
if (newWidth <= 0 || !newWidth) {
|
||||||
// Spec says to ignore NaN/Inf/0/negative values
|
// Spec says to ignore NaN/Inf/0/negative values
|
||||||
@ -112,10 +192,77 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'miterLimit', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return this._paint.getStrokeMiter();
|
||||||
|
},
|
||||||
|
set: function(newLimit) {
|
||||||
|
if (newLimit <= 0 || !newLimit) {
|
||||||
|
// Spec says to ignore NaN/Inf/0/negative values
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._paint.setStrokeMiter(newLimit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'shadowBlur', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return this._shadowBlur;
|
||||||
|
},
|
||||||
|
set: function(newBlur) {
|
||||||
|
// ignore negative, inf and NAN (but not 0) as per the spec.
|
||||||
|
if (newBlur < 0 || !isFinite(newBlur)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._shadowBlur = newBlur;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'shadowColor', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return colorToString(this._shadowColor);
|
||||||
|
},
|
||||||
|
set: function(newColor) {
|
||||||
|
this._shadowColor = parseColor(newColor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'shadowOffsetX', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return this._shadowOffsetX;
|
||||||
|
},
|
||||||
|
set: function(newOffset) {
|
||||||
|
if (!isFinite(newOffset)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._shadowOffsetX = newOffset;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'shadowOffsetY', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return this._shadowOffsetY;
|
||||||
|
},
|
||||||
|
set: function(newOffset) {
|
||||||
|
if (!isFinite(newOffset)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._shadowOffsetY = newOffset;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, 'strokeStyle', {
|
Object.defineProperty(this, 'strokeStyle', {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return colorToString(this._strokeColor);
|
||||||
|
},
|
||||||
set: function(newStyle) {
|
set: function(newStyle) {
|
||||||
this._paint.setColor(parseColor(newStyle));
|
this._strokeColor = parseColor(newStyle);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -215,8 +362,48 @@
|
|||||||
temp.delete();
|
temp.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.fill = function() {
|
||||||
|
this._commitSubpath();
|
||||||
|
this._paint.setStyle(CanvasKit.PaintStyle.Fill);
|
||||||
|
this._paint.setColor(this._fillColor);
|
||||||
|
var orig = this._paint.getStrokeWidth();
|
||||||
|
// This is not in the spec, but it appears Chrome scales up
|
||||||
|
// the line width by some amount when stroking (and filling?).
|
||||||
|
var scaledWidth = orig * this._scalefactor();
|
||||||
|
this._paint.setStrokeWidth(scaledWidth);
|
||||||
|
|
||||||
|
var shadowPaint = this._shadowPaint();
|
||||||
|
if (shadowPaint) {
|
||||||
|
var offsetMatrix = CanvasKit.SkMatrix.multiply(
|
||||||
|
this._currentTransform,
|
||||||
|
CanvasKit.SkMatrix.translated(this._shadowOffsetX, this._shadowOffsetY)
|
||||||
|
);
|
||||||
|
this._canvas.setMatrix(offsetMatrix);
|
||||||
|
this._canvas.drawPath(this._currentPath, shadowPaint);
|
||||||
|
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
|
||||||
|
shadowPaint.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._canvas.drawPath(this._currentPath, this._paint);
|
||||||
|
// set stroke width back to original size:
|
||||||
|
this._paint.setStrokeWidth(orig);
|
||||||
|
}
|
||||||
|
|
||||||
this.fillText = function(text, x, y, maxWidth) {
|
this.fillText = function(text, x, y, maxWidth) {
|
||||||
// TODO do something with maxWidth, probably involving measure
|
// TODO do something with maxWidth, probably involving measure
|
||||||
|
this._paint.setStyle(CanvasKit.PaintStyle.Fill);
|
||||||
|
this._paint.setColor(this._fillColor);
|
||||||
|
var shadowPaint = this._shadowPaint();
|
||||||
|
if (shadowPaint) {
|
||||||
|
var offsetMatrix = CanvasKit.SkMatrix.multiply(
|
||||||
|
this._currentTransform,
|
||||||
|
CanvasKit.SkMatrix.translated(this._shadowOffsetX, this._shadowOffsetY)
|
||||||
|
);
|
||||||
|
this._canvas.setMatrix(offsetMatrix);
|
||||||
|
this._canvas.drawText(text, x, y, shadowPaint);
|
||||||
|
shadowPaint.dispose();
|
||||||
|
// Don't need to setMatrix back, it will be handled by the next few lines.
|
||||||
|
}
|
||||||
this._canvas.setMatrix(this._currentTransform);
|
this._canvas.setMatrix(this._currentTransform);
|
||||||
this._canvas.drawText(text, x, y, this._paint);
|
this._canvas.drawText(text, x, y, this._paint);
|
||||||
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
|
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
|
||||||
@ -296,12 +483,52 @@
|
|||||||
this._currentTransform = CanvasKit.SkMatrix.identity();
|
this._currentTransform = CanvasKit.SkMatrix.identity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.restore = function() {
|
||||||
|
var newState = this._canvasStateStack.pop();
|
||||||
|
if (!newState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._currentTransform = newState.ctm;
|
||||||
|
// TODO(kjlubick): clipping region
|
||||||
|
// TODO(kjlubick): dash list
|
||||||
|
this._paint.setStrokeWidth(newState.sw);
|
||||||
|
this._strokeColor = newState.sc;
|
||||||
|
this._fillColor = newState.fc;
|
||||||
|
this._paint.setStrokeCap(newState.cap);
|
||||||
|
this._paint.setStrokeJoin(newState.jn);
|
||||||
|
this._paint.setStrokeMiter(newState.mtr);
|
||||||
|
this._shadowOffsetX = newState.sox;
|
||||||
|
this._shadowOffsetY = newState.soy;
|
||||||
|
this._shadowBlur = newState.sb;
|
||||||
|
this._shadowColor = newState.shc;
|
||||||
|
//TODO: globalAlpha, lineDashOffset, filter, globalCompositeOperation, font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
|
||||||
|
}
|
||||||
|
|
||||||
this.rotate = function(radians, px, py) {
|
this.rotate = function(radians, px, py) {
|
||||||
this._currentTransform = CanvasKit.SkMatrix.multiply(
|
this._currentTransform = CanvasKit.SkMatrix.multiply(
|
||||||
this._currentTransform,
|
this._currentTransform,
|
||||||
CanvasKit.SkMatrix.rotated(radians, px, py));
|
CanvasKit.SkMatrix.rotated(radians, px, py));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.save = function() {
|
||||||
|
this._canvasStateStack.push({
|
||||||
|
ctm: this._currentTransform.slice(),
|
||||||
|
// TODO(kjlubick): clipping region
|
||||||
|
// TODO(kjlubick): dash list
|
||||||
|
sw: this._paint.getStrokeWidth(),
|
||||||
|
sc: this._strokeColor,
|
||||||
|
fc: this._fillColor,
|
||||||
|
cap: this._paint.getStrokeCap(),
|
||||||
|
jn: this._paint.getStrokeJoin(),
|
||||||
|
mtr: this._paint.getStrokeMiter(),
|
||||||
|
sox: this._shadowOffsetX,
|
||||||
|
soy: this._shadowOffsetY,
|
||||||
|
sb: this._shadowBlur,
|
||||||
|
shc: this._shadowColor,
|
||||||
|
//TODO: globalAlpha, lineDashOffset, filter, globalCompositeOperation, font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.scale = function(sx, sy) {
|
this.scale = function(sx, sy) {
|
||||||
this._currentTransform = CanvasKit.SkMatrix.multiply(
|
this._currentTransform = CanvasKit.SkMatrix.multiply(
|
||||||
this._currentTransform,
|
this._currentTransform,
|
||||||
@ -323,24 +550,78 @@
|
|||||||
0, 0, 1];
|
0, 0, 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stroke = function() {
|
// Returns the shadow paint for the current settings or null if there
|
||||||
if (this._currentSubpath) {
|
// should be no shadow. This ends up being a copy of the current
|
||||||
this._commitSubpath();
|
// paint with a blur maskfilter and the correct color.
|
||||||
this._paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
this._shadowPaint = function() {
|
||||||
var orig = this._paint.getStrokeWidth();
|
// if alpha is zero, no shadows
|
||||||
// This is not in the spec, but it appears Chrome scales up
|
if (!CanvasKit.getColorComponents(this._shadowColor)[3]) {
|
||||||
// the line width by some amount when stroking (and filling?).
|
return null;
|
||||||
var scaledWidth = orig * this._scalefactor();
|
|
||||||
this._paint.setStrokeWidth(scaledWidth);
|
|
||||||
this._canvas.drawPath(this._currentPath, this._paint);
|
|
||||||
// set stroke width back to original size:
|
|
||||||
this._paint.setStrokeWidth(orig);
|
|
||||||
}
|
}
|
||||||
|
// one of these must also be non-zero (otherwise the shadow is
|
||||||
|
// completely hidden. And the spec says so).
|
||||||
|
if (!(this._shadowBlur || this._shadowOffsetY || this._shadowOffsetX)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var shadowPaint = this._paint.copy();
|
||||||
|
shadowPaint.setColor(this._shadowColor);
|
||||||
|
var blurEffect = CanvasKit.MakeBlurMaskFilter(CanvasKit.BlurStyle.Normal,
|
||||||
|
Math.max(1, this._shadowBlur/2), // very little blur when < 1
|
||||||
|
false);
|
||||||
|
shadowPaint.setMaskFilter(blurEffect);
|
||||||
|
|
||||||
|
// hack up a "destructor" which also cleans up the blurEffect. Otherwise,
|
||||||
|
// we leak the blurEffect (since smart pointers don't help us in JS land).
|
||||||
|
shadowPaint.dispose = function() {
|
||||||
|
blurEffect.delete();
|
||||||
|
this.delete();
|
||||||
|
};
|
||||||
|
return shadowPaint;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stroke = function() {
|
||||||
|
this._commitSubpath();
|
||||||
|
this._paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||||
|
|
||||||
|
this._paint.setColor(this._strokeColor);
|
||||||
|
var orig = this._paint.getStrokeWidth();
|
||||||
|
// This is not in the spec, but it appears Chrome scales up
|
||||||
|
// the line width by some amount when stroking (and filling?).
|
||||||
|
var scaledWidth = orig * this._scalefactor();
|
||||||
|
this._paint.setStrokeWidth(scaledWidth);
|
||||||
|
|
||||||
|
var shadowPaint = this._shadowPaint();
|
||||||
|
if (shadowPaint) {
|
||||||
|
var offsetMatrix = CanvasKit.SkMatrix.multiply(
|
||||||
|
this._currentTransform,
|
||||||
|
CanvasKit.SkMatrix.translated(this._shadowOffsetX, this._shadowOffsetY)
|
||||||
|
);
|
||||||
|
this._canvas.setMatrix(offsetMatrix);
|
||||||
|
this._canvas.drawPath(this._currentPath, shadowPaint);
|
||||||
|
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
|
||||||
|
shadowPaint.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._canvas.drawPath(this._currentPath, this._paint);
|
||||||
|
// set stroke width back to original size:
|
||||||
|
this._paint.setStrokeWidth(orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.strokeText = function(text, x, y, maxWidth) {
|
this.strokeText = function(text, x, y, maxWidth) {
|
||||||
// TODO do something with maxWidth, probably involving measure
|
// TODO do something with maxWidth, probably involving measure
|
||||||
this._paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
this._paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||||
|
this._paint.setColor(this._strokeColor);
|
||||||
|
var shadowPaint = this._shadowPaint();
|
||||||
|
if (shadowPaint) {
|
||||||
|
var offsetMatrix = CanvasKit.SkMatrix.multiply(
|
||||||
|
this._currentTransform,
|
||||||
|
CanvasKit.SkMatrix.translated(this._shadowOffsetX, this._shadowOffsetY)
|
||||||
|
);
|
||||||
|
this._canvas.setMatrix(offsetMatrix);
|
||||||
|
this._canvas.drawText(text, x, y, shadowPaint);
|
||||||
|
shadowPaint.dispose();
|
||||||
|
// Don't need to setMatrix back, it will be handled by the next few lines.
|
||||||
|
}
|
||||||
this._canvas.setMatrix(this._currentTransform);
|
this._canvas.setMatrix(this._currentTransform);
|
||||||
this._canvas.drawText(text, x, y, this._paint);
|
this._canvas.drawText(text, x, y, this._paint);
|
||||||
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
|
this._canvas.setMatrix(CanvasKit.SkMatrix.identity());
|
||||||
@ -421,6 +702,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function colorToString(skcolor) {
|
||||||
|
// https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color
|
||||||
|
var components = CanvasKit.getColorComponents(skcolor);
|
||||||
|
var r = components[0];
|
||||||
|
var g = components[1];
|
||||||
|
var b = components[2];
|
||||||
|
var a = components[3];
|
||||||
|
if (a === 1.0) {
|
||||||
|
// hex
|
||||||
|
r = r.toString(16).toLowerCase();
|
||||||
|
g = g.toString(16).toLowerCase();
|
||||||
|
b = b.toString(16).toLowerCase();
|
||||||
|
r = (r.length === 1 ? '0'+r: r);
|
||||||
|
g = (g.length === 1 ? '0'+g: g);
|
||||||
|
b = (b.length === 1 ? '0'+b: b);
|
||||||
|
return '#'+r+g+b;
|
||||||
|
} else {
|
||||||
|
a = (a === 0 || a === 1) ? a : a.toFixed(8);
|
||||||
|
return 'rgba('+r+', '+g+', '+b+', '+a+')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function valueOrPercent(aStr) {
|
function valueOrPercent(aStr) {
|
||||||
var a = parseFloat(aStr) || 1;
|
var a = parseFloat(aStr) || 1;
|
||||||
if (aStr && aStr.indexOf('%') !== -1) {
|
if (aStr && aStr.indexOf('%') !== -1) {
|
||||||
@ -484,6 +787,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
CanvasKit._testing['parseColor'] = parseColor;
|
CanvasKit._testing['parseColor'] = parseColor;
|
||||||
|
CanvasKit._testing['colorToString'] = colorToString;
|
||||||
|
|
||||||
// Create the following with
|
// Create the following with
|
||||||
// node ./htmlcanvas/_namedcolors.js --expose-wasm
|
// node ./htmlcanvas/_namedcolors.js --expose-wasm
|
||||||
|
@ -33,14 +33,14 @@ describe('CanvasKit\'s Canvas 2d Behavior', function() {
|
|||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('color string parsing', function() {
|
describe('color strings', function() {
|
||||||
function hex(s) {
|
function hex(s) {
|
||||||
return parseInt(s, 16);
|
return parseInt(s, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('parses hex color strings', function(done) {
|
it('parses hex color strings', function(done) {
|
||||||
LoadCanvasKit.then(catchException(done, () => {
|
LoadCanvasKit.then(catchException(done, () => {
|
||||||
let parseColor = CanvasKit._testing.parseColor;
|
const parseColor = CanvasKit._testing.parseColor;
|
||||||
expect(parseColor('#FED')).toEqual(
|
expect(parseColor('#FED')).toEqual(
|
||||||
CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), 1));
|
CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), 1));
|
||||||
expect(parseColor('#FEDC')).toEqual(
|
expect(parseColor('#FEDC')).toEqual(
|
||||||
@ -54,7 +54,7 @@ describe('CanvasKit\'s Canvas 2d Behavior', function() {
|
|||||||
});
|
});
|
||||||
it('parses rgba color strings', function(done) {
|
it('parses rgba color strings', function(done) {
|
||||||
LoadCanvasKit.then(catchException(done, () => {
|
LoadCanvasKit.then(catchException(done, () => {
|
||||||
let parseColor = CanvasKit._testing.parseColor;
|
const parseColor = CanvasKit._testing.parseColor;
|
||||||
expect(parseColor('rgba(117, 33, 64, 0.75)')).toEqual(
|
expect(parseColor('rgba(117, 33, 64, 0.75)')).toEqual(
|
||||||
CanvasKit.Color(117, 33, 64, 0.75));
|
CanvasKit.Color(117, 33, 64, 0.75));
|
||||||
expect(parseColor('rgb(117, 33, 64, 0.75)')).toEqual(
|
expect(parseColor('rgb(117, 33, 64, 0.75)')).toEqual(
|
||||||
@ -68,7 +68,7 @@ describe('CanvasKit\'s Canvas 2d Behavior', function() {
|
|||||||
});
|
});
|
||||||
it('parses named color strings', function(done) {
|
it('parses named color strings', function(done) {
|
||||||
LoadCanvasKit.then(catchException(done, () => {
|
LoadCanvasKit.then(catchException(done, () => {
|
||||||
let parseColor = CanvasKit._testing.parseColor;
|
const parseColor = CanvasKit._testing.parseColor;
|
||||||
expect(parseColor('grey')).toEqual(
|
expect(parseColor('grey')).toEqual(
|
||||||
CanvasKit.Color(128, 128, 128, 1.0));
|
CanvasKit.Color(128, 128, 128, 1.0));
|
||||||
expect(parseColor('blanchedalmond')).toEqual(
|
expect(parseColor('blanchedalmond')).toEqual(
|
||||||
@ -78,6 +78,19 @@ describe('CanvasKit\'s Canvas 2d Behavior', function() {
|
|||||||
done();
|
done();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('properly produces color strings', function(done) {
|
||||||
|
LoadCanvasKit.then(catchException(done, () => {
|
||||||
|
const colorToString = CanvasKit._testing.colorToString;
|
||||||
|
|
||||||
|
expect(colorToString(CanvasKit.Color(102, 51, 153, 1.0))).toEqual('#663399');
|
||||||
|
|
||||||
|
expect(colorToString(CanvasKit.Color(255, 235, 205, 0.5))).toEqual(
|
||||||
|
'rgba(255, 235, 205, 0.50196078)');
|
||||||
|
|
||||||
|
done();
|
||||||
|
}));
|
||||||
|
});
|
||||||
}); // end describe('color string parsing')
|
}); // end describe('color string parsing')
|
||||||
|
|
||||||
function multipleCanvasTest(testname, done, test) {
|
function multipleCanvasTest(testname, done, test) {
|
||||||
@ -192,6 +205,76 @@ describe('CanvasKit\'s Canvas 2d Behavior', function() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('properly saves and restores states, even when drawing shadows', function(done) {
|
||||||
|
LoadCanvasKit.then(catchException(done, () => {
|
||||||
|
multipleCanvasTest('shadows_and_save_restore', done, (canvas) => {
|
||||||
|
let ctx = canvas.getContext('2d');
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.fillStyle = '#CCC';
|
||||||
|
ctx.shadowColor = 'rebeccapurple';
|
||||||
|
ctx.shadowBlur = 1;
|
||||||
|
ctx.shadowOffsetX = 3;
|
||||||
|
ctx.shadowOffsetY = -8;
|
||||||
|
ctx.rect(10, 10, 30, 30);
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = '#C00';
|
||||||
|
ctx.fillStyle = '#00C';
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
ctx.shadowColor = 'transparent';
|
||||||
|
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(36, 148);
|
||||||
|
ctx.quadraticCurveTo(66, 188, 120, 136);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.shadowColor = '#993366AA';
|
||||||
|
ctx.shadowOffsetX = 8;
|
||||||
|
ctx.shadowBlur = 5;
|
||||||
|
ctx.setTransform(2, 0, -.5, 2.5, -40, 120);
|
||||||
|
ctx.rect(110, 10, 20, 20);
|
||||||
|
ctx.lineTo(110, 0);
|
||||||
|
ctx.resetTransform();
|
||||||
|
ctx.lineTo(220, 120);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.fillStyle = 'green';
|
||||||
|
ctx.font = '16pt Arial';
|
||||||
|
ctx.fillText('This should be shadowed', 20, 80);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can read default properties', function(done) {
|
||||||
|
LoadCanvasKit.then(catchException(done, () => {
|
||||||
|
const skcanvas = CanvasKit.MakeCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
|
||||||
|
const realCanvas = document.getElementById('test');
|
||||||
|
realCanvas.width = CANVAS_WIDTH;
|
||||||
|
realCanvas.height = CANVAS_HEIGHT;
|
||||||
|
|
||||||
|
const skcontext = skcanvas.getContext('2d');
|
||||||
|
const realContext = realCanvas.getContext('2d');
|
||||||
|
|
||||||
|
const toTest = ['font', 'lineWidth', 'strokeStyle', 'lineCap',
|
||||||
|
'lineJoin', 'miterLimit', 'shadowOffsetY',
|
||||||
|
'shadowBlur', 'shadowColor', 'shadowOffsetX'];
|
||||||
|
|
||||||
|
for( let attr of toTest) {
|
||||||
|
expect(skcontext[attr]).toBe(realContext[attr], attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
skcanvas.dispose();
|
||||||
|
done();
|
||||||
|
}));
|
||||||
|
});
|
||||||
}); // end describe('Path drawing API')
|
}); // end describe('Path drawing API')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user