[canvaskit] Add build flag for pathops

This fixes up some other build flag options:
 - font-specific js code is correctly omitted when no_font is set
 - SKP serialization is only compiled in debug (or with flag).


Bug: skia:9733
Change-Id: Ifdbd2ddac278cfcefa842f6d4826d5429b6ed64b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/262137
Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
Kevin Lubick 2020-01-06 08:10:05 -05:00
parent f1db2661f1
commit 933ea8c301
8 changed files with 320 additions and 255 deletions

View File

@ -6,15 +6,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- A "Core" build that removes Fonts, the Skottie animation player, the Particles demo,
and PathOps is available in `bin/core/`. It is about half the size of the "CoreWithFonts"
build.
- Experimental Runtime shader available for custom builds.
### Fixed
- `CanvasKit.SaveLayerInitWithPrevious` and `CanvasKit.SaveLayerF16ColorType` constants.
- Some compilation configurations, for example, those with no fonts or just one of particles/skottie.
### Changed
- Small tweaks to compilation settings to reduce code size and linkage time.
- JS functions are no longer provided when the underlying c++ calls have been compiled out.
### Removed
- `SkShader.Empty`
- `SkShader.Empty`
### Breaking
- In an effort to reduce code size for most clients, npm now contains two CanvasKit builds.
In `bin/` there is the "CoreWithFonts" build that contains most functionality from 0.10.0.
However, we no longer ship the Skottie animation player, nor the Particles demo. Further,
PathOps are removed from this build `MakePathFromOp`, `SkPath.op` and `SkPath.simplify`.
Clients who need any of those features are encouraged to create a custom build using
`compile.sh`.
- `SkPicture.DEBUGONLY_saveAsFile` was accidentally included in release builds. It has been
removed. Clients who need this in a release build (e.g. to file a bug report that only
reproduces in release) should do a custom build with the `force_serialize_skp` flag given.
## [0.10.0] - 2019-12-09

View File

@ -16,9 +16,6 @@
#include "include/core/SkDrawable.h"
#include "include/core/SkEncodedImageFormat.h"
#include "include/core/SkFilterQuality.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkImageInfo.h"
@ -47,7 +44,6 @@
#include "include/effects/SkImageFilters.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/effects/SkTrimPathEffect.h"
#include "include/pathops/SkPathOps.h"
#include "include/utils/SkParsePath.h"
#include "include/utils/SkShadowUtils.h"
#include "modules/skshaper/include/SkShaper.h"
@ -72,9 +68,20 @@
#include <emscripten/html5.h>
#endif
#ifndef SK_NO_FONTS
#include "include/core/SkFont.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontTypes.h"
#endif
#ifdef SK_INCLUDE_PARAGRAPH
#include "modules/skparagraph/include/Paragraph.h"
#endif
#ifdef SK_INCLUDE_PATHOPS
#include "include/pathops/SkPathOps.h"
#endif
// Aliases for less typing
using BoneIndices = SkVertices::BoneIndices;
using BoneWeights = SkVertices::BoneWeights;
@ -314,21 +321,31 @@ void ApplyTransform(SkPath& orig,
orig.transform(m);
}
bool EMSCRIPTEN_KEEPALIVE ApplySimplify(SkPath& path) {
#ifdef SK_INCLUDE_PATHOPS
bool ApplySimplify(SkPath& path) {
return Simplify(path, &path);
}
bool EMSCRIPTEN_KEEPALIVE ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
bool ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
return Op(pathOne, pathTwo, op, &pathOne);
}
JSString EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) {
SkPathOrNull MakePathFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
SkPath out;
if (Op(pathOne, pathTwo, op, &out)) {
return emscripten::val(out);
}
return emscripten::val::null();
}
#endif
JSString ToSVGString(const SkPath& path) {
SkString s;
SkParsePath::ToSVGString(path, &s);
return emscripten::val(s.c_str());
}
SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromSVGString(std::string str) {
SkPathOrNull MakePathFromSVGString(std::string str) {
SkPath path;
if (SkParsePath::FromSVGString(str.c_str(), &path)) {
return emscripten::val(path);
@ -336,20 +353,12 @@ SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromSVGString(std::string str) {
return emscripten::val::null();
}
SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
SkPath out;
if (Op(pathOne, pathTwo, op, &out)) {
return emscripten::val(out);
}
return emscripten::val::null();
}
SkPath EMSCRIPTEN_KEEPALIVE CopyPath(const SkPath& a) {
SkPath CopyPath(const SkPath& a) {
SkPath copy(a);
return copy;
}
bool EMSCRIPTEN_KEEPALIVE Equals(const SkPath& a, const SkPath& b) {
bool Equals(const SkPath& a, const SkPath& b) {
return a == b;
}
@ -374,7 +383,7 @@ void VisitPath(const SkPath& p, VisitFunc&& f) {
}
}
JSArray EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) {
JSArray ToCmds(const SkPath& path) {
JSArray cmds = emscripten::val::array();
VisitPath(path, [&cmds](SkPath::Verb verb, const SkPoint pts[4], SkPath::RawIter iter) {
@ -422,7 +431,7 @@ JSArray EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) {
// in our function type signatures. (this gives an error message like "Cannot call foo due to unbound
// types Pi, Pf"). But, we can just pretend they are numbers and cast them to be pointers and
// the compiler is happy.
SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromCmds(uintptr_t /* float* */ cptr, int numCmds) {
SkPathOrNull MakePathFromCmds(uintptr_t /* float* */ cptr, int numCmds) {
const auto* cmds = reinterpret_cast<const float*>(cptr);
SkPath path;
float x1, y1, x2, y2, x3, y3;
@ -737,7 +746,9 @@ EMSCRIPTEN_BINDINGS(Skia) {
return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
}), allow_raw_pointers());
function("_MakePathFromCmds", &MakePathFromCmds);
#ifdef SK_INCLUDE_PATHOPS
function("MakePathFromOp", &MakePathFromOp);
#endif
function("MakePathFromSVGString", &MakePathFromSVGString);
// These won't be called directly, there's a JS helper to deal with typed arrays.
@ -1265,10 +1276,11 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_trim", &ApplyTrim)
.function("_stroke", &ApplyStroke)
#ifdef SK_INCLUDE_PATHOPS
// PathOps
.function("_simplify", &ApplySimplify)
.function("_op", &ApplyPathOp)
#endif
// Exporting
.function("toSVGString", &ToSVGString)
.function("toCmds", &ToCmds)
@ -1322,6 +1334,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkPicture>("SkPicture")
.smart_ptr<sk_sp<SkPicture>>("sk_sp<SkPicture>")
#if defined(SK_DEBUG) || defined(SK_FORCE_SERIALIZE_SKP)
// The serialized format of an SkPicture (informally called an "skp"), is not something
// that clients should ever rely on. It is useful when filing bug reports, but that's
// about it. The format may change at anytime and no promises are made for backwards
@ -1330,7 +1343,9 @@ EMSCRIPTEN_BINDINGS(Skia) {
// Emscripten doesn't play well with optional arguments, which we don't
// want to expose anyway.
return self.serialize();
}), allow_raw_pointers());
}), allow_raw_pointers())
#endif
;
class_<SkShader>("SkShader")
.smart_ptr<sk_sp<SkShader>>("sk_sp<SkShader>")
@ -1549,12 +1564,14 @@ EMSCRIPTEN_BINDINGS(Skia) {
.value("Stroke", SkPaint::Style::kStroke_Style)
.value("StrokeAndFill", SkPaint::Style::kStrokeAndFill_Style);
#ifdef SK_INCLUDE_PATHOPS
enum_<SkPathOp>("PathOp")
.value("Difference", SkPathOp::kDifference_SkPathOp)
.value("Intersect", SkPathOp::kIntersect_SkPathOp)
.value("Union", SkPathOp::kUnion_SkPathOp)
.value("XOR", SkPathOp::kXOR_SkPathOp)
.value("ReverseDifference", SkPathOp::kReverseDifference_SkPathOp);
#endif
enum_<SkCanvas::PointMode>("PointMode")
.value("Points", SkCanvas::PointMode::kPoints_PointMode)

View File

@ -28,11 +28,13 @@ EMAR=`which emar`
RELEASE_CONF="-Oz --closure 1 --llvm-lto 1 -DSK_RELEASE --pre-js $BASE_DIR/release.js \
-DGR_GL_CHECK_ALLOC_WITH_GET_ERROR=0"
EXTRA_CFLAGS="\"-DSK_RELEASE\", \"-DGR_GL_CHECK_ALLOC_WITH_GET_ERROR=0\","
SKP_JS=""
if [[ $@ == *debug* ]]; then
echo "Building a Debug build"
EXTRA_CFLAGS="\"-DSK_DEBUG\""
RELEASE_CONF="-O0 --js-opts 0 -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=1 -s GL_ASSERTIONS=1 -g4 \
--source-map-base /node_modules/canvaskit/bin/ -DSK_DEBUG --pre-js $BASE_DIR/debug.js"
SKP_JS="--pre-js $BASE_DIR/skp.js"
BUILD_DIR=${BUILD_DIR:="out/canvaskit_wasm_debug"}
elif [[ $@ == *profiling* ]]; then
echo "Building a build for profiling"
@ -43,6 +45,13 @@ else
BUILD_DIR=${BUILD_DIR:="out/canvaskit_wasm"}
fi
# This should only be set by clients who are trying to serialize an
# skp to include in a bug report and for whatever reason, it is not
# showing up in a debug build.
if [[ $@ == *force_serialize_skp* ]] ; then
RELEASE_CONF+=" -DSK_FORCE_SERIALIZE_SKP"
fi
mkdir -p $BUILD_DIR
# sometimes the .a files keep old symbols around - cleaning them out makes sure
# we get a fresh build.
@ -98,6 +107,13 @@ if [[ $@ != *no_particles* || $@ != *no_skottie* ]] ; then
PARTICLES_BINDINGS+=" modules/skresources/src/SkResources.cpp"
fi
WASM_PATHOPS="-DSK_INCLUDE_PATHOPS"
PATHOPS_JS="--pre-js $BASE_DIR/pathops.js"
if [[ $@ == *no_pathops* ]] ; then
WASM_PATHOPS=""
PATHOPS_JS=""
fi
HTML_CANVAS_API="--pre-js $BASE_DIR/htmlcanvas/preamble.js \
--pre-js $BASE_DIR/htmlcanvas/util.js \
--pre-js $BASE_DIR/htmlcanvas/color.js \
@ -118,10 +134,12 @@ fi
GN_FONT="skia_enable_fontmgr_empty=false skia_enable_fontmgr_custom_empty=false"
FONT_CFLAGS=""
BUILTIN_FONT="$BASE_DIR/fonts/NotoMono-Regular.ttf.cpp"
FONT_JS="--pre-js $BASE_DIR/font.js"
if [[ $@ == *no_font* ]]; then
echo "Omitting the built-in font(s), font manager and all code dealing with fonts"
BUILTIN_FONT=""
FONT_CFLAGS="-DSK_NO_FONTS"
FONT_JS=""
GN_FONT="skia_enable_fontmgr_empty=true skia_enable_fontmgr_custom_empty=false"
elif [[ $@ == *no_embedded_font* ]]; then
echo "Omitting the built-in font(s)"
@ -243,6 +261,7 @@ ${EMCXX} \
-DSK_DISABLE_READBUFFER \
-DSK_DISABLE_AAA \
$WASM_GPU \
$WASM_PATHOPS \
$FONT_CFLAGS \
-std=c++14 \
--bind \
@ -252,6 +271,9 @@ ${EMCXX} \
$PARAGRAPH_JS \
$SKOTTIE_JS \
$PARTICLES_JS \
$PATHOPS_JS \
$FONT_JS \
$SKP_JS \
$HTML_CANVAS_API \
--pre-js $BASE_DIR/postamble.js \
--post-js $BASE_DIR/ready.js \

201
modules/canvaskit/font.js Normal file
View File

@ -0,0 +1,201 @@
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
// str can be either a text string or a ShapedText object
CanvasKit.SkCanvas.prototype.drawText = function(str, x, y, paint, font) {
if (typeof str === 'string') {
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
var strLen = lengthBytesUTF8(str);
// Add 1 for null terminator, which we need when copying/converting, but can ignore
// when we call into Skia.
var strPtr = CanvasKit._malloc(strLen + 1);
stringToUTF8(str, strPtr, strLen + 1);
this._drawSimpleText(strPtr, strLen, x, y, font, paint);
} else {
this._drawShapedText(str, x, y, paint);
}
}
// Returns an array of the widths of the glyphs in this string.
CanvasKit.SkFont.prototype.getWidths = function(str) {
// add 1 for null terminator
var codePoints = str.length + 1;
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
// Add 1 for null terminator
var strBytes = lengthBytesUTF8(str) + 1;
var strPtr = CanvasKit._malloc(strBytes);
stringToUTF8(str, strPtr, strBytes);
var bytesPerFloat = 4;
// allocate widths == numCodePoints
var widthPtr = CanvasKit._malloc(codePoints * bytesPerFloat);
if (!this._getWidths(strPtr, strBytes, codePoints, widthPtr)) {
SkDebug('Could not compute widths');
CanvasKit._free(strPtr);
CanvasKit._free(widthPtr);
return null;
}
// reminder, this shouldn't copy the data, just is a nice way to
// wrap 4 bytes together into a float.
var widths = new Float32Array(CanvasKit.buffer, widthPtr, codePoints);
// This copies the data so we can free the CanvasKit memory
var retVal = Array.from(widths);
CanvasKit._free(strPtr);
CanvasKit._free(widthPtr);
return retVal;
}
// arguments should all be arrayBuffers or be an array of arrayBuffers.
CanvasKit.SkFontMgr.FromData = function() {
if (!arguments.length) {
SkDebug('Could not make SkFontMgr from no font sources');
return null;
}
var fonts = arguments;
if (fonts.length === 1 && Array.isArray(fonts[0])) {
fonts = arguments[0];
}
if (!fonts.length) {
SkDebug('Could not make SkFontMgr from no font sources');
return null;
}
var dPtrs = [];
var sizes = [];
for (var i = 0; i < fonts.length; i++) {
var data = new Uint8Array(fonts[i]);
var dptr = copy1dArray(data, CanvasKit.HEAPU8);
dPtrs.push(dptr);
sizes.push(data.byteLength);
}
// Pointers are 32 bit unsigned ints
var datasPtr = copy1dArray(dPtrs, CanvasKit.HEAPU32);
var sizesPtr = copy1dArray(sizes, CanvasKit.HEAPU32);
var fm = CanvasKit.SkFontMgr._fromData(datasPtr, sizesPtr, fonts.length);
// The SkFontMgr has taken ownership of the bytes we allocated in the for loop.
CanvasKit._free(datasPtr);
CanvasKit._free(sizesPtr);
return fm;
}
// fontData should be an arrayBuffer
CanvasKit.SkFontMgr.prototype.MakeTypefaceFromData = function(fontData) {
var data = new Uint8Array(fontData);
var fptr = copy1dArray(data, CanvasKit.HEAPU8);
var font = this._makeTypefaceFromData(fptr, data.byteLength);
if (!font) {
SkDebug('Could not decode font data');
// We do not need to free the data since the C++ will do that for us
// when the font is deleted (or fails to decode);
return null;
}
return font;
}
CanvasKit.SkTextBlob.MakeOnPath = function(str, path, font, initialOffset) {
if (!str || !str.length) {
SkDebug('ignoring 0 length string');
return;
}
if (!path || !path.countPoints()) {
SkDebug('ignoring empty path');
return;
}
if (path.countPoints() === 1) {
SkDebug('path has 1 point, returning normal textblob');
return this.MakeFromText(str, font);
}
if (!initialOffset) {
initialOffset = 0;
}
var widths = font.getWidths(str);
var rsx = new CanvasKit.RSXFormBuilder();
var meas = new CanvasKit.SkPathMeasure(path, false, 1);
var dist = initialOffset;
for (var i = 0; i < str.length; i++) {
var width = widths[i];
dist += width/2;
if (dist > meas.getLength()) {
// jump to next contour
if (!meas.nextContour()) {
// We have come to the end of the path - terminate the string
// right here.
str = str.substring(0, i);
break;
}
dist = width/2;
}
// Gives us the (x, y) coordinates as well as the cos/sin of the tangent
// line at that position.
var xycs = meas.getPosTan(dist);
var cx = xycs[0];
var cy = xycs[1];
var cosT = xycs[2];
var sinT = xycs[3];
var adjustedX = cx - (width/2 * cosT);
var adjustedY = cy - (width/2 * sinT);
rsx.push(cosT, sinT, adjustedX, adjustedY);
dist += width/2;
}
var retVal = this.MakeFromRSXform(str, rsx, font);
rsx.delete();
meas.delete();
return retVal;
}
CanvasKit.SkTextBlob.MakeFromRSXform = function(str, rsxBuilder, font) {
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
// Add 1 for null terminator
var strLen = lengthBytesUTF8(str) + 1;
var strPtr = CanvasKit._malloc(strLen);
// Add 1 for the null terminator.
stringToUTF8(str, strPtr, strLen);
var rptr = rsxBuilder.build();
var blob = CanvasKit.SkTextBlob._MakeFromRSXform(strPtr, strLen - 1,
rptr, font, CanvasKit.TextEncoding.UTF8);
if (!blob) {
SkDebug('Could not make textblob from string "' + str + '"');
return null;
}
var origDelete = blob.delete.bind(blob);
blob.delete = function() {
CanvasKit._free(strPtr);
origDelete();
}
return blob;
}
CanvasKit.SkTextBlob.MakeFromText = function(str, font) {
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
// Add 1 for null terminator
var strLen = lengthBytesUTF8(str) + 1;
var strPtr = CanvasKit._malloc(strLen);
// Add 1 for the null terminator.
stringToUTF8(str, strPtr, strLen);
var blob = CanvasKit.SkTextBlob._MakeFromText(strPtr, strLen - 1, font, CanvasKit.TextEncoding.UTF8);
if (!blob) {
SkDebug('Could not make textblob from string "' + str + '"');
return null;
}
var origDelete = blob.delete.bind(blob);
blob.delete = function() {
CanvasKit._free(strPtr);
origDelete();
}
return blob;
}
});

View File

@ -428,13 +428,6 @@ CanvasKit.onRuntimeInitialized = function() {
return this;
};
CanvasKit.SkPath.prototype.op = function(otherPath, op) {
if (this._op(otherPath, op)) {
return this;
}
return null;
};
CanvasKit.SkPath.prototype.quadTo = function(cpx, cpy, x, y) {
this._quadTo(cpx, cpy, x, y);
return this;
@ -472,13 +465,6 @@ CanvasKit.onRuntimeInitialized = function() {
return this;
};
CanvasKit.SkPath.prototype.simplify = function() {
if (this._simplify()) {
return this;
}
return null;
};
CanvasKit.SkPath.prototype.stroke = function(opts) {
// Fill out any missing values with the default values.
/**
@ -676,22 +662,6 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit._free(ptr);
}
// str can be either a text string or a ShapedText object
CanvasKit.SkCanvas.prototype.drawText = function(str, x, y, paint, font) {
if (typeof str === 'string') {
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
var strLen = lengthBytesUTF8(str);
// Add 1 for null terminator, which we need when copying/converting, but can ignore
// when we call into Skia.
var strPtr = CanvasKit._malloc(strLen + 1);
stringToUTF8(str, strPtr, strLen + 1);
this._drawSimpleText(strPtr, strLen, x, y, font, paint);
} else {
this._drawShapedText(str, x, y, paint);
}
}
// returns Uint8Array
CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
colorType, dstRowBytes) {
@ -760,98 +730,6 @@ CanvasKit.onRuntimeInitialized = function() {
return m;
}
// Returns an array of the widths of the glyphs in this string.
CanvasKit.SkFont.prototype.getWidths = function(str) {
// add 1 for null terminator
var codePoints = str.length + 1;
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
// Add 1 for null terminator
var strBytes = lengthBytesUTF8(str) + 1;
var strPtr = CanvasKit._malloc(strBytes);
stringToUTF8(str, strPtr, strBytes);
var bytesPerFloat = 4;
// allocate widths == numCodePoints
var widthPtr = CanvasKit._malloc(codePoints * bytesPerFloat);
if (!this._getWidths(strPtr, strBytes, codePoints, widthPtr)) {
SkDebug('Could not compute widths');
CanvasKit._free(strPtr);
CanvasKit._free(widthPtr);
return null;
}
// reminder, this shouldn't copy the data, just is a nice way to
// wrap 4 bytes together into a float.
var widths = new Float32Array(CanvasKit.buffer, widthPtr, codePoints);
// This copies the data so we can free the CanvasKit memory
var retVal = Array.from(widths);
CanvasKit._free(strPtr);
CanvasKit._free(widthPtr);
return retVal;
}
// arguments should all be arrayBuffers or be an array of arrayBuffers.
CanvasKit.SkFontMgr.FromData = function() {
if (!arguments.length) {
SkDebug('Could not make SkFontMgr from no font sources');
return null;
}
var fonts = arguments;
if (fonts.length === 1 && Array.isArray(fonts[0])) {
fonts = arguments[0];
}
if (!fonts.length) {
SkDebug('Could not make SkFontMgr from no font sources');
return null;
}
var dPtrs = [];
var sizes = [];
for (var i = 0; i < fonts.length; i++) {
var data = new Uint8Array(fonts[i]);
var dptr = copy1dArray(data, CanvasKit.HEAPU8);
dPtrs.push(dptr);
sizes.push(data.byteLength);
}
// Pointers are 32 bit unsigned ints
var datasPtr = copy1dArray(dPtrs, CanvasKit.HEAPU32);
var sizesPtr = copy1dArray(sizes, CanvasKit.HEAPU32);
var fm = CanvasKit.SkFontMgr._fromData(datasPtr, sizesPtr, fonts.length);
// The SkFontMgr has taken ownership of the bytes we allocated in the for loop.
CanvasKit._free(datasPtr);
CanvasKit._free(sizesPtr);
return fm;
}
// fontData should be an arrayBuffer
CanvasKit.SkFontMgr.prototype.MakeTypefaceFromData = function(fontData) {
var data = new Uint8Array(fontData);
var fptr = copy1dArray(data, CanvasKit.HEAPU8);
var font = this._makeTypefaceFromData(fptr, data.byteLength);
if (!font) {
SkDebug('Could not decode font data');
// We do not need to free the data since the C++ will do that for us
// when the font is deleted (or fails to decode);
return null;
}
return font;
}
// The serialized format of an SkPicture (informally called an "skp"), is not something
// that clients should ever rely on. It is useful when filing bug reports, but that's
// about it. The format may change at anytime and no promises are made for backwards
// or forward compatibility.
CanvasKit.SkPicture.prototype.DEBUGONLY_saveAsFile = function(skpName) {
var data = this.DEBUGONLY_serialize();
if (!data) {
SkDebug('Could not serialize to skpicture.');
return;
}
var bytes = CanvasKit.getSkDataBytes(data);
saveBytesToFile(bytes, skpName);
data.delete();
}
CanvasKit.SkShader.Blend = function(mode, dst, src, localMatrix) {
if (!localMatrix) {
return this._Blend(mode, dst, src);
@ -894,111 +772,6 @@ CanvasKit.onRuntimeInitialized = function() {
}.bind(this));
}
CanvasKit.SkTextBlob.MakeOnPath = function(str, path, font, initialOffset) {
if (!str || !str.length) {
SkDebug('ignoring 0 length string');
return;
}
if (!path || !path.countPoints()) {
SkDebug('ignoring empty path');
return;
}
if (path.countPoints() === 1) {
SkDebug('path has 1 point, returning normal textblob');
return this.MakeFromText(str, font);
}
if (!initialOffset) {
initialOffset = 0;
}
var widths = font.getWidths(str);
var rsx = new CanvasKit.RSXFormBuilder();
var meas = new CanvasKit.SkPathMeasure(path, false, 1);
var dist = initialOffset;
for (var i = 0; i < str.length; i++) {
var width = widths[i];
dist += width/2;
if (dist > meas.getLength()) {
// jump to next contour
if (!meas.nextContour()) {
// We have come to the end of the path - terminate the string
// right here.
str = str.substring(0, i);
break;
}
dist = width/2;
}
// Gives us the (x, y) coordinates as well as the cos/sin of the tangent
// line at that position.
var xycs = meas.getPosTan(dist);
var cx = xycs[0];
var cy = xycs[1];
var cosT = xycs[2];
var sinT = xycs[3];
var adjustedX = cx - (width/2 * cosT);
var adjustedY = cy - (width/2 * sinT);
rsx.push(cosT, sinT, adjustedX, adjustedY);
dist += width/2;
}
var retVal = this.MakeFromRSXform(str, rsx, font);
rsx.delete();
meas.delete();
return retVal;
}
CanvasKit.SkTextBlob.MakeFromRSXform = function(str, rsxBuilder, font) {
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
// Add 1 for null terminator
var strLen = lengthBytesUTF8(str) + 1;
var strPtr = CanvasKit._malloc(strLen);
// Add 1 for the null terminator.
stringToUTF8(str, strPtr, strLen);
var rptr = rsxBuilder.build();
var blob = CanvasKit.SkTextBlob._MakeFromRSXform(strPtr, strLen - 1,
rptr, font, CanvasKit.TextEncoding.UTF8);
if (!blob) {
SkDebug('Could not make textblob from string "' + str + '"');
return null;
}
var origDelete = blob.delete.bind(blob);
blob.delete = function() {
CanvasKit._free(strPtr);
origDelete();
}
return blob;
}
CanvasKit.SkTextBlob.MakeFromText = function(str, font) {
// lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
// JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
// Add 1 for null terminator
var strLen = lengthBytesUTF8(str) + 1;
var strPtr = CanvasKit._malloc(strLen);
// Add 1 for the null terminator.
stringToUTF8(str, strPtr, strLen);
var blob = CanvasKit.SkTextBlob._MakeFromText(strPtr, strLen - 1, font, CanvasKit.TextEncoding.UTF8);
if (!blob) {
SkDebug('Could not make textblob from string "' + str + '"');
return null;
}
var origDelete = blob.delete.bind(blob);
blob.delete = function() {
CanvasKit._free(strPtr);
origDelete();
}
return blob;
}
// Run through the JS files that are added at compile time.
if (CanvasKit._extraInitializations) {
CanvasKit._extraInitializations.forEach(function(init) {

View File

@ -0,0 +1,17 @@
// Adds in the code to use pathops with SkPath
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
CanvasKit.SkPath.prototype.op = function(otherPath, op) {
if (this._op(otherPath, op)) {
return this;
}
return null;
};
CanvasKit.SkPath.prototype.simplify = function() {
if (this._simplify()) {
return this;
}
return null;
};
});

17
modules/canvaskit/skp.js Normal file
View File

@ -0,0 +1,17 @@
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
// The serialized format of an SkPicture (informally called an "skp"), is not something
// that clients should ever rely on. It is useful when filing bug reports, but that's
// about it. The format may change at anytime and no promises are made for backwards
// or forward compatibility.
CanvasKit.SkPicture.prototype.DEBUGONLY_saveAsFile = function(skpName) {
var data = this.DEBUGONLY_serialize();
if (!data) {
SkDebug('Could not serialize to skpicture.');
return;
}
var bytes = CanvasKit.getSkDataBytes(data);
saveBytesToFile(bytes, skpName);
data.delete();
}
});

View File

@ -15,17 +15,17 @@ describe('Skottie behavior', function() {
});
it('can draw one with an animated gif', function(done) {
if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
console.warn('Skipping test because not compiled with skottie');
done();
return;
}
const imgPromise = fetch('/assets/flightAnim.gif')
.then((response) => response.arrayBuffer());
const jsonPromise = fetch('/assets/animated_gif.json')
.then((response) => response.text());
Promise.all([imgPromise, jsonPromise, LoadCanvasKit]).then((values) => {
if (!CanvasKit.managed_skottie) {
console.warn('Skipping test because not compiled with skottie')
done();
return;
}
catchException(done, () => {
const imgBuffer = values[0];
expect(imgBuffer).toBeTruthy();