2018-09-20 21:39:31 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2018 Google LLC
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2019-09-18 20:18:17 +00:00
|
|
|
#include "include/android/SkAnimatedImage.h"
|
|
|
|
#include "include/codec/SkAndroidCodec.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkBlendMode.h"
|
|
|
|
#include "include/core/SkBlurTypes.h"
|
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "include/core/SkColor.h"
|
2019-09-12 15:11:25 +00:00
|
|
|
#include "include/core/SkColorFilter.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkData.h"
|
2019-09-18 20:18:17 +00:00
|
|
|
#include "include/core/SkDrawable.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#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"
|
2019-10-29 13:55:39 +00:00
|
|
|
#include "include/core/SkImageFilter.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkImageInfo.h"
|
|
|
|
#include "include/core/SkMaskFilter.h"
|
|
|
|
#include "include/core/SkPaint.h"
|
|
|
|
#include "include/core/SkPath.h"
|
|
|
|
#include "include/core/SkPathEffect.h"
|
|
|
|
#include "include/core/SkPathMeasure.h"
|
|
|
|
#include "include/core/SkPicture.h"
|
|
|
|
#include "include/core/SkPictureRecorder.h"
|
2019-09-03 16:59:06 +00:00
|
|
|
#include "include/core/SkRRect.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkScalar.h"
|
|
|
|
#include "include/core/SkShader.h"
|
|
|
|
#include "include/core/SkString.h"
|
|
|
|
#include "include/core/SkStrokeRec.h"
|
|
|
|
#include "include/core/SkSurface.h"
|
|
|
|
#include "include/core/SkSurfaceProps.h"
|
|
|
|
#include "include/core/SkTextBlob.h"
|
|
|
|
#include "include/core/SkTypeface.h"
|
|
|
|
#include "include/core/SkTypes.h"
|
|
|
|
#include "include/core/SkVertices.h"
|
|
|
|
#include "include/effects/SkCornerPathEffect.h"
|
|
|
|
#include "include/effects/SkDashPathEffect.h"
|
|
|
|
#include "include/effects/SkDiscretePathEffect.h"
|
|
|
|
#include "include/effects/SkGradientShader.h"
|
2019-10-29 13:55:39 +00:00
|
|
|
#include "include/effects/SkImageFilters.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#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"
|
|
|
|
#include "src/core/SkFontMgrPriv.h"
|
|
|
|
#include "src/core/SkMakeUnique.h"
|
2018-11-03 11:51:19 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <string>
|
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "modules/canvaskit/WasmAliases.h"
|
2018-09-20 21:39:31 +00:00
|
|
|
#include <emscripten.h>
|
|
|
|
#include <emscripten/bind.h>
|
2019-01-03 19:27:27 +00:00
|
|
|
|
2018-10-11 12:51:55 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/gpu/GrBackendSurface.h"
|
|
|
|
#include "include/gpu/GrContext.h"
|
|
|
|
#include "include/gpu/gl/GrGLInterface.h"
|
|
|
|
#include "include/gpu/gl/GrGLTypes.h"
|
2019-03-11 20:11:58 +00:00
|
|
|
|
2018-10-11 12:51:55 +00:00
|
|
|
#include <GL/gl.h>
|
2018-09-20 21:39:31 +00:00
|
|
|
#include <emscripten/html5.h>
|
2018-10-11 12:51:55 +00:00
|
|
|
#endif
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2019-10-03 15:22:08 +00:00
|
|
|
#ifdef SK_INCLUDE_PARAGRAPH
|
|
|
|
#include "modules/skparagraph/include/Paragraph.h"
|
|
|
|
#endif
|
2018-11-03 11:51:19 +00:00
|
|
|
// Aliases for less typing
|
|
|
|
using BoneIndices = SkVertices::BoneIndices;
|
|
|
|
using BoneWeights = SkVertices::BoneWeights;
|
|
|
|
using Bone = SkVertices::Bone;
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2019-09-26 17:20:50 +00:00
|
|
|
#ifndef SK_NO_FONTS
|
|
|
|
sk_sp<SkFontMgr> SkFontMgr_New_Custom_Data(const uint8_t** datas, const size_t* sizes, int n);
|
|
|
|
#endif
|
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
struct SimpleMatrix {
|
|
|
|
SkScalar scaleX, skewX, transX;
|
|
|
|
SkScalar skewY, scaleY, transY;
|
|
|
|
SkScalar pers0, pers1, pers2;
|
|
|
|
};
|
|
|
|
|
|
|
|
SkMatrix toSkMatrix(const SimpleMatrix& sm) {
|
|
|
|
return SkMatrix::MakeAll(sm.scaleX, sm.skewX , sm.transX,
|
|
|
|
sm.skewY , sm.scaleY, sm.transY,
|
|
|
|
sm.pers0 , sm.pers1 , sm.pers2);
|
|
|
|
}
|
|
|
|
|
|
|
|
SimpleMatrix toSimpleSkMatrix(const SkMatrix& sm) {
|
|
|
|
SimpleMatrix m {sm[0], sm[1], sm[2],
|
|
|
|
sm[3], sm[4], sm[5],
|
|
|
|
sm[6], sm[7], sm[8]};
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SimpleImageInfo {
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
SkColorType colorType;
|
|
|
|
SkAlphaType alphaType;
|
|
|
|
// TODO color spaces?
|
|
|
|
};
|
|
|
|
|
|
|
|
SkImageInfo toSkImageInfo(const SimpleImageInfo& sii) {
|
|
|
|
return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType);
|
|
|
|
}
|
|
|
|
|
2018-10-11 12:51:55 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
2019-03-08 15:04:28 +00:00
|
|
|
sk_sp<GrContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
|
|
|
|
{
|
2018-09-20 21:39:31 +00:00
|
|
|
EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(context);
|
|
|
|
if (r < 0) {
|
2019-01-03 21:20:04 +00:00
|
|
|
printf("failed to make webgl context current %d\n", r);
|
2018-09-20 21:39:31 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
2019-03-08 15:04:28 +00:00
|
|
|
// setup GrContext
|
|
|
|
auto interface = GrGLMakeNativeInterface();
|
|
|
|
// setup contexts
|
|
|
|
sk_sp<GrContext> grContext(GrContext::MakeGL(interface));
|
|
|
|
return grContext;
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrContext> grContext, int width, int height) {
|
2018-09-20 21:39:31 +00:00
|
|
|
glClearColor(0, 0, 0, 0);
|
|
|
|
glClearStencil(0);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
// Wrap the frame buffer object attached to the screen in a Skia render
|
|
|
|
// target so Skia can render to it
|
2018-09-20 21:39:31 +00:00
|
|
|
GrGLint buffer;
|
|
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer);
|
|
|
|
GrGLFramebufferInfo info;
|
|
|
|
info.fFBOID = (GrGLuint) buffer;
|
|
|
|
SkColorType colorType;
|
|
|
|
|
2019-11-15 16:02:15 +00:00
|
|
|
GrGLint stencil;
|
|
|
|
glGetIntegerv(GL_STENCIL_BITS, &stencil);
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
info.fFormat = GL_RGBA8;
|
|
|
|
colorType = kRGBA_8888_SkColorType;
|
|
|
|
|
2019-11-15 16:02:15 +00:00
|
|
|
GrBackendRenderTarget target(width, height, 0, stencil, info);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
|
|
|
|
kBottomLeft_GrSurfaceOrigin,
|
|
|
|
colorType, nullptr, nullptr));
|
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrContext> grContext, int width, int height) {
|
|
|
|
SkImageInfo info = SkImageInfo::MakeN32(width, height, SkAlphaType::kPremul_SkAlphaType);
|
2018-11-03 11:51:19 +00:00
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(grContext.get(),
|
|
|
|
SkBudgeted::kYes,
|
|
|
|
info, 0,
|
|
|
|
kBottomLeft_GrSurfaceOrigin,
|
|
|
|
nullptr, true));
|
|
|
|
return surface;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrContext> grContext, SimpleImageInfo sii) {
|
|
|
|
sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(grContext.get(),
|
|
|
|
SkBudgeted::kYes,
|
|
|
|
toSkImageInfo(sii), 0,
|
|
|
|
kBottomLeft_GrSurfaceOrigin,
|
|
|
|
nullptr, true));
|
|
|
|
return surface;
|
2018-12-07 15:09:11 +00:00
|
|
|
}
|
2019-03-08 15:04:28 +00:00
|
|
|
#endif
|
2018-12-07 15:09:11 +00:00
|
|
|
|
2018-11-30 19:05:58 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
//========================================================================================
|
|
|
|
// Path things
|
|
|
|
//========================================================================================
|
|
|
|
|
|
|
|
// All these Apply* methods are simple wrappers to avoid returning an object.
|
|
|
|
// The default WASM bindings produce code that will leak if a return value
|
|
|
|
// isn't assigned to a JS variable and has delete() called on it.
|
|
|
|
// These Apply methods, combined with the smarter binding code allow for chainable
|
|
|
|
// commands that don't leak if the return value is ignored (i.e. when used intuitively).
|
|
|
|
|
2018-11-20 17:51:16 +00:00
|
|
|
void ApplyAddArc(SkPath& orig, const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
|
|
|
|
orig.addArc(oval, startAngle, sweepAngle);
|
|
|
|
}
|
|
|
|
|
2019-08-26 19:48:09 +00:00
|
|
|
void ApplyAddOval(SkPath& orig, const SkRect& oval, bool ccw, unsigned start) {
|
2019-11-22 18:34:02 +00:00
|
|
|
orig.addOval(oval, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW, start);
|
2019-08-26 19:48:09 +00:00
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyAddPath(SkPath& orig, const SkPath& newPath,
|
|
|
|
SkScalar scaleX, SkScalar skewX, SkScalar transX,
|
|
|
|
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
2018-11-20 17:51:16 +00:00
|
|
|
SkScalar pers0, SkScalar pers1, SkScalar pers2,
|
|
|
|
bool extendPath) {
|
2018-09-20 21:39:31 +00:00
|
|
|
SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
|
|
|
|
skewY , scaleY, transY,
|
|
|
|
pers0 , pers1 , pers2);
|
2018-11-20 17:51:16 +00:00
|
|
|
orig.addPath(newPath, m, extendPath ? SkPath::kExtend_AddPathMode :
|
|
|
|
SkPath::kAppend_AddPathMode);
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2018-11-20 17:51:16 +00:00
|
|
|
void ApplyAddRect(SkPath& path, SkScalar left, SkScalar top,
|
|
|
|
SkScalar right, SkScalar bottom, bool ccw) {
|
2019-11-22 18:34:02 +00:00
|
|
|
path.addRect(left, top, right, bottom, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
|
2018-11-14 23:01:19 +00:00
|
|
|
}
|
|
|
|
|
2019-01-07 16:08:55 +00:00
|
|
|
void ApplyAddRoundRect(SkPath& path, SkScalar left, SkScalar top,
|
|
|
|
SkScalar right, SkScalar bottom, uintptr_t /* SkScalar* */ rPtr,
|
|
|
|
bool ccw) {
|
|
|
|
// See comment below for uintptr_t explanation
|
|
|
|
const SkScalar* radii = reinterpret_cast<const SkScalar*>(rPtr);
|
|
|
|
path.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), radii,
|
2019-11-22 18:34:02 +00:00
|
|
|
ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
|
2019-01-07 16:08:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyArcTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
|
|
|
SkScalar radius) {
|
|
|
|
p.arcTo(x1, y1, x2, y2, radius);
|
|
|
|
}
|
|
|
|
|
2018-12-07 18:03:08 +00:00
|
|
|
void ApplyArcToAngle(SkPath& p, SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) {
|
|
|
|
p.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
|
|
|
|
}
|
|
|
|
|
2019-11-01 18:36:52 +00:00
|
|
|
void ApplyArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
|
|
|
|
bool useSmallArc, bool ccw, SkScalar x, SkScalar y) {
|
2019-08-26 19:48:09 +00:00
|
|
|
auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize;
|
2019-11-22 18:34:02 +00:00
|
|
|
auto sweep = ccw ? SkPathDirection::kCCW : SkPathDirection::kCW;
|
2019-08-26 19:48:09 +00:00
|
|
|
orig.arcTo(rx, ry, xAxisRotate, arcSize, sweep, x, y);
|
|
|
|
}
|
|
|
|
|
2019-11-01 18:36:52 +00:00
|
|
|
void ApplyRArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
|
|
|
|
bool useSmallArc, bool ccw, SkScalar dx, SkScalar dy) {
|
|
|
|
auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize;
|
2019-11-22 18:34:02 +00:00
|
|
|
auto sweep = ccw ? SkPathDirection::kCCW : SkPathDirection::kCW;
|
2019-11-01 18:36:52 +00:00
|
|
|
orig.rArcTo(rx, ry, xAxisRotate, arcSize, sweep, dx, dy);
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyClose(SkPath& p) {
|
|
|
|
p.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyConicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
|
|
|
SkScalar w) {
|
|
|
|
p.conicTo(x1, y1, x2, y2, w);
|
|
|
|
}
|
|
|
|
|
2019-11-01 18:36:52 +00:00
|
|
|
void ApplyRConicTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
|
|
|
|
SkScalar w) {
|
|
|
|
p.rConicTo(dx1, dy1, dx2, dy2, w);
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyCubicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
|
|
|
SkScalar x3, SkScalar y3) {
|
|
|
|
p.cubicTo(x1, y1, x2, y2, x3, y3);
|
|
|
|
}
|
|
|
|
|
2019-11-01 18:36:52 +00:00
|
|
|
void ApplyRCubicTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
|
|
|
|
SkScalar dx3, SkScalar dy3) {
|
|
|
|
p.rCubicTo(dx1, dy1, dx2, dy2, dx3, dy3);
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyLineTo(SkPath& p, SkScalar x, SkScalar y) {
|
|
|
|
p.lineTo(x, y);
|
|
|
|
}
|
|
|
|
|
2019-11-01 18:36:52 +00:00
|
|
|
void ApplyRLineTo(SkPath& p, SkScalar dx, SkScalar dy) {
|
|
|
|
p.rLineTo(dx, dy);
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyMoveTo(SkPath& p, SkScalar x, SkScalar y) {
|
|
|
|
p.moveTo(x, y);
|
|
|
|
}
|
|
|
|
|
2019-11-01 18:36:52 +00:00
|
|
|
void ApplyRMoveTo(SkPath& p, SkScalar dx, SkScalar dy) {
|
|
|
|
p.rMoveTo(dx, dy);
|
|
|
|
}
|
|
|
|
|
2019-01-07 16:08:55 +00:00
|
|
|
void ApplyReset(SkPath& p) {
|
|
|
|
p.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyRewind(SkPath& p) {
|
|
|
|
p.rewind();
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyQuadTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
|
|
|
|
p.quadTo(x1, y1, x2, y2);
|
|
|
|
}
|
|
|
|
|
2019-11-01 18:36:52 +00:00
|
|
|
void ApplyRQuadTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2) {
|
|
|
|
p.rQuadTo(dx1, dy1, dx2, dy2);
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
void ApplyTransform(SkPath& orig,
|
|
|
|
SkScalar scaleX, SkScalar skewX, SkScalar transX,
|
|
|
|
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
|
|
|
SkScalar pers0, SkScalar pers1, SkScalar pers2) {
|
|
|
|
SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
|
|
|
|
skewY , scaleY, transY,
|
|
|
|
pers0 , pers1 , pers2);
|
|
|
|
orig.transform(m);
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
bool EMSCRIPTEN_KEEPALIVE ApplySimplify(SkPath& path) {
|
|
|
|
return Simplify(path, &path);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EMSCRIPTEN_KEEPALIVE ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
|
|
|
|
return Op(pathOne, pathTwo, op, &pathOne);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSString EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) {
|
|
|
|
SkString s;
|
|
|
|
SkParsePath::ToSVGString(path, &s);
|
|
|
|
return emscripten::val(s.c_str());
|
|
|
|
}
|
|
|
|
|
2018-12-17 21:01:36 +00:00
|
|
|
SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromSVGString(std::string str) {
|
|
|
|
SkPath path;
|
|
|
|
if (SkParsePath::FromSVGString(str.c_str(), &path)) {
|
|
|
|
return emscripten::val(path);
|
|
|
|
}
|
|
|
|
return emscripten::val::null();
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
SkPath EMSCRIPTEN_KEEPALIVE CopyPath(const SkPath& a) {
|
|
|
|
SkPath copy(a);
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EMSCRIPTEN_KEEPALIVE Equals(const SkPath& a, const SkPath& b) {
|
|
|
|
return a == b;
|
|
|
|
}
|
|
|
|
|
2019-01-07 16:08:55 +00:00
|
|
|
// =================================================================================
|
|
|
|
// Creating/Exporting Paths with cmd arrays
|
|
|
|
// =================================================================================
|
|
|
|
|
|
|
|
static const int MOVE = 0;
|
|
|
|
static const int LINE = 1;
|
|
|
|
static const int QUAD = 2;
|
|
|
|
static const int CONIC = 3;
|
|
|
|
static const int CUBIC = 4;
|
|
|
|
static const int CLOSE = 5;
|
|
|
|
|
|
|
|
template <typename VisitFunc>
|
|
|
|
void VisitPath(const SkPath& p, VisitFunc&& f) {
|
|
|
|
SkPath::RawIter iter(p);
|
|
|
|
SkPoint pts[4];
|
|
|
|
SkPath::Verb verb;
|
|
|
|
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
|
|
|
f(verb, pts, iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
JSArray EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) {
|
|
|
|
JSArray cmds = emscripten::val::array();
|
|
|
|
|
|
|
|
VisitPath(path, [&cmds](SkPath::Verb verb, const SkPoint pts[4], SkPath::RawIter iter) {
|
|
|
|
JSArray cmd = emscripten::val::array();
|
|
|
|
switch (verb) {
|
|
|
|
case SkPath::kMove_Verb:
|
|
|
|
cmd.call<void>("push", MOVE, pts[0].x(), pts[0].y());
|
|
|
|
break;
|
|
|
|
case SkPath::kLine_Verb:
|
|
|
|
cmd.call<void>("push", LINE, pts[1].x(), pts[1].y());
|
|
|
|
break;
|
|
|
|
case SkPath::kQuad_Verb:
|
|
|
|
cmd.call<void>("push", QUAD, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
|
|
|
|
break;
|
|
|
|
case SkPath::kConic_Verb:
|
|
|
|
cmd.call<void>("push", CONIC,
|
|
|
|
pts[1].x(), pts[1].y(),
|
|
|
|
pts[2].x(), pts[2].y(), iter.conicWeight());
|
|
|
|
break;
|
|
|
|
case SkPath::kCubic_Verb:
|
|
|
|
cmd.call<void>("push", CUBIC,
|
|
|
|
pts[1].x(), pts[1].y(),
|
|
|
|
pts[2].x(), pts[2].y(),
|
|
|
|
pts[3].x(), pts[3].y());
|
|
|
|
break;
|
|
|
|
case SkPath::kClose_Verb:
|
|
|
|
cmd.call<void>("push", CLOSE);
|
|
|
|
break;
|
|
|
|
case SkPath::kDone_Verb:
|
|
|
|
SkASSERT(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cmds.call<void>("push", cmd);
|
|
|
|
});
|
|
|
|
return cmds;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This type signature is a mess, but it's necessary. See, we can't use "bind" (EMSCRIPTEN_BINDINGS)
|
|
|
|
// and pointers to primitive types (Only bound types like SkPoint). We could if we used
|
|
|
|
// cwrap (see https://becominghuman.ai/passing-and-returning-webassembly-array-parameters-a0f572c65d97)
|
|
|
|
// but that requires us to stick to C code and, AFAIK, doesn't allow us to return nice things like
|
|
|
|
// SkPath or SkOpBuilder.
|
|
|
|
//
|
2019-09-26 17:20:50 +00:00
|
|
|
// So, basically, if we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primitive pointers
|
2019-01-07 16:08:55 +00:00
|
|
|
// 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) {
|
|
|
|
const auto* cmds = reinterpret_cast<const float*>(cptr);
|
|
|
|
SkPath path;
|
|
|
|
float x1, y1, x2, y2, x3, y3;
|
|
|
|
|
|
|
|
// if there are not enough arguments, bail with the path we've constructed so far.
|
|
|
|
#define CHECK_NUM_ARGS(n) \
|
|
|
|
if ((i + n) > numCmds) { \
|
|
|
|
SkDebugf("Not enough args to match the verbs. Saw %d commands\n", numCmds); \
|
|
|
|
return emscripten::val::null(); \
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = 0; i < numCmds;){
|
|
|
|
switch (sk_float_floor2int(cmds[i++])) {
|
|
|
|
case MOVE:
|
|
|
|
CHECK_NUM_ARGS(2);
|
|
|
|
x1 = cmds[i++], y1 = cmds[i++];
|
|
|
|
path.moveTo(x1, y1);
|
|
|
|
break;
|
|
|
|
case LINE:
|
|
|
|
CHECK_NUM_ARGS(2);
|
|
|
|
x1 = cmds[i++], y1 = cmds[i++];
|
|
|
|
path.lineTo(x1, y1);
|
|
|
|
break;
|
|
|
|
case QUAD:
|
|
|
|
CHECK_NUM_ARGS(4);
|
|
|
|
x1 = cmds[i++], y1 = cmds[i++];
|
|
|
|
x2 = cmds[i++], y2 = cmds[i++];
|
|
|
|
path.quadTo(x1, y1, x2, y2);
|
|
|
|
break;
|
|
|
|
case CONIC:
|
|
|
|
CHECK_NUM_ARGS(5);
|
|
|
|
x1 = cmds[i++], y1 = cmds[i++];
|
|
|
|
x2 = cmds[i++], y2 = cmds[i++];
|
|
|
|
x3 = cmds[i++]; // weight
|
|
|
|
path.conicTo(x1, y1, x2, y2, x3);
|
|
|
|
break;
|
|
|
|
case CUBIC:
|
|
|
|
CHECK_NUM_ARGS(6);
|
|
|
|
x1 = cmds[i++], y1 = cmds[i++];
|
|
|
|
x2 = cmds[i++], y2 = cmds[i++];
|
|
|
|
x3 = cmds[i++], y3 = cmds[i++];
|
|
|
|
path.cubicTo(x1, y1, x2, y2, x3, y3);
|
|
|
|
break;
|
|
|
|
case CLOSE:
|
|
|
|
path.close();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SkDebugf(" path: UNKNOWN command %f, aborting dump...\n", cmds[i-1]);
|
|
|
|
return emscripten::val::null();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef CHECK_NUM_ARGS
|
|
|
|
|
|
|
|
return emscripten::val(path);
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
//========================================================================================
|
|
|
|
// Path Effects
|
|
|
|
//========================================================================================
|
|
|
|
|
|
|
|
bool ApplyDash(SkPath& path, SkScalar on, SkScalar off, SkScalar phase) {
|
|
|
|
SkScalar intervals[] = { on, off };
|
|
|
|
auto pe = SkDashPathEffect::Make(intervals, 2, phase);
|
|
|
|
if (!pe) {
|
|
|
|
SkDebugf("Invalid args to dash()\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
|
|
|
|
if (pe->filterPath(&path, path, &rec, nullptr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
SkDebugf("Could not make dashed path\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ApplyTrim(SkPath& path, SkScalar startT, SkScalar stopT, bool isComplement) {
|
|
|
|
auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal;
|
|
|
|
auto pe = SkTrimPathEffect::Make(startT, stopT, mode);
|
|
|
|
if (!pe) {
|
|
|
|
SkDebugf("Invalid args to trim(): startT and stopT must be in [0,1]\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
|
|
|
|
if (pe->filterPath(&path, path, &rec, nullptr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
SkDebugf("Could not trim path\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct StrokeOpts {
|
2018-11-26 16:47:54 +00:00
|
|
|
// Default values are set in interface.js which allows clients
|
2018-11-03 11:51:19 +00:00
|
|
|
// to set any number of them. Otherwise, the binding code complains if
|
|
|
|
// any are omitted.
|
|
|
|
SkScalar width;
|
|
|
|
SkScalar miter_limit;
|
|
|
|
SkPaint::Join join;
|
|
|
|
SkPaint::Cap cap;
|
2018-12-07 18:03:08 +00:00
|
|
|
float precision;
|
2018-11-03 11:51:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
bool ApplyStroke(SkPath& path, StrokeOpts opts) {
|
|
|
|
SkPaint p;
|
|
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
|
|
p.setStrokeCap(opts.cap);
|
|
|
|
p.setStrokeJoin(opts.join);
|
|
|
|
p.setStrokeWidth(opts.width);
|
|
|
|
p.setStrokeMiter(opts.miter_limit);
|
|
|
|
|
2018-12-07 18:03:08 +00:00
|
|
|
return p.getFillPath(path, &path, nullptr, opts.precision);
|
2018-11-03 11:51:19 +00:00
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
// to map from raw memory to a uint8array
|
2018-11-03 11:51:19 +00:00
|
|
|
Uint8Array getSkDataBytes(const SkData *data) {
|
|
|
|
return Uint8Array(typed_memory_view(data->size(), data->bytes()));
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 15:04:06 +00:00
|
|
|
// Text Shaping abstraction
|
|
|
|
|
|
|
|
struct ShapedTextOpts {
|
|
|
|
SkFont font;
|
|
|
|
bool leftToRight;
|
|
|
|
std::string text;
|
|
|
|
SkScalar width;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<SkShaper> shaper;
|
|
|
|
|
|
|
|
static sk_sp<SkTextBlob> do_shaping(const ShapedTextOpts& opts, SkPoint* pt) {
|
2019-04-01 23:01:09 +00:00
|
|
|
SkTextBlobBuilderRunHandler builder(opts.text.c_str(), {0, 0});
|
2019-02-22 15:04:06 +00:00
|
|
|
if (!shaper) {
|
|
|
|
shaper = SkShaper::Make();
|
|
|
|
}
|
2019-04-01 23:01:09 +00:00
|
|
|
shaper->shape(opts.text.c_str(), opts.text.length(),
|
|
|
|
opts.font, opts.leftToRight,
|
|
|
|
opts.width, &builder);
|
|
|
|
*pt = builder.endPoint();
|
2019-02-22 15:04:06 +00:00
|
|
|
return builder.makeBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
class ShapedText {
|
|
|
|
public:
|
|
|
|
ShapedText(ShapedTextOpts opts) : fOpts(opts) {}
|
|
|
|
|
|
|
|
SkRect getBounds() {
|
|
|
|
this->init();
|
|
|
|
return SkRect::MakeLTRB(0, 0, fOpts.width, fPoint.y());
|
|
|
|
}
|
|
|
|
|
|
|
|
SkTextBlob* blob() {
|
|
|
|
this->init();
|
|
|
|
return fBlob.get();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
const ShapedTextOpts fOpts;
|
|
|
|
SkPoint fPoint;
|
|
|
|
sk_sp<SkTextBlob> fBlob;
|
|
|
|
|
|
|
|
void init() {
|
|
|
|
if (!fBlob) {
|
|
|
|
fBlob = do_shaping(fOpts, &fPoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void drawShapedText(SkCanvas& canvas, ShapedText st, SkScalar x,
|
2019-10-29 14:48:26 +00:00
|
|
|
SkScalar y, SkPaint paint) {
|
2019-02-22 15:04:06 +00:00
|
|
|
canvas.drawTextBlob(st.blob(), x, y, paint);
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:48:26 +00:00
|
|
|
int saveLayerRec(SkCanvas& canvas, const SkPaint* paint,
|
|
|
|
const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
|
|
|
|
return canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, paint, backdrop, flags));
|
|
|
|
}
|
|
|
|
|
|
|
|
int saveLayerRecBounds(SkCanvas& canvas, const SkPaint* paint, const SkImageFilter* backdrop,
|
|
|
|
SkCanvas::SaveLayerFlags flags, const SkRect& bounds) {
|
|
|
|
return canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, paint, backdrop, flags));
|
|
|
|
}
|
|
|
|
|
2019-03-15 19:36:29 +00:00
|
|
|
// This is simpler than dealing with an SkPoint and SkVector
|
|
|
|
struct PosTan {
|
|
|
|
SkScalar px, py, tx, ty;
|
|
|
|
};
|
|
|
|
|
2019-09-03 16:59:06 +00:00
|
|
|
// SimpleRRect is simpler than passing a (complex) SkRRect over the wire to JS.
|
|
|
|
struct SimpleRRect {
|
|
|
|
SkRect rect;
|
2019-09-11 18:22:22 +00:00
|
|
|
|
|
|
|
SkScalar rx1;
|
|
|
|
SkScalar ry1;
|
|
|
|
SkScalar rx2;
|
|
|
|
SkScalar ry2;
|
|
|
|
SkScalar rx3;
|
|
|
|
SkScalar ry3;
|
|
|
|
SkScalar rx4;
|
|
|
|
SkScalar ry4;
|
2019-09-03 16:59:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
SkRRect toRRect(const SimpleRRect& r) {
|
2019-09-11 18:22:22 +00:00
|
|
|
SkVector fRadii[4] = {{r.rx1, r.ry1}, {r.rx2, r.ry2},
|
|
|
|
{r.rx3, r.ry3}, {r.rx4, r.ry4}};
|
|
|
|
SkRRect rr;
|
|
|
|
rr.setRectRadii(r.rect, fRadii);
|
|
|
|
return rr;
|
2019-09-03 16:59:06 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 19:23:00 +00:00
|
|
|
struct TonalColors {
|
|
|
|
SkColor ambientColor;
|
|
|
|
SkColor spotColor;
|
|
|
|
};
|
|
|
|
|
|
|
|
TonalColors computeTonalColors(const TonalColors& in) {
|
|
|
|
TonalColors out;
|
|
|
|
SkShadowUtils::ComputeTonalColors(in.ambientColor, in.spotColor,
|
|
|
|
&out.ambientColor, &out.spotColor);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2019-09-03 16:59:06 +00:00
|
|
|
// These objects have private destructors / delete methods - I don't think
|
2018-11-03 11:51:19 +00:00
|
|
|
// we need to do anything other than tell emscripten to do nothing.
|
2018-09-20 21:39:31 +00:00
|
|
|
namespace emscripten {
|
|
|
|
namespace internal {
|
|
|
|
template<typename ClassType>
|
|
|
|
void raw_destructor(ClassType *);
|
|
|
|
|
2019-11-20 19:17:53 +00:00
|
|
|
template<>
|
|
|
|
void raw_destructor<SkContourMeasure>(SkContourMeasure *ptr) {
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
template<>
|
|
|
|
void raw_destructor<SkData>(SkData *ptr) {
|
|
|
|
}
|
2018-11-03 11:51:19 +00:00
|
|
|
|
2018-12-12 15:35:13 +00:00
|
|
|
template<>
|
2019-09-26 17:20:50 +00:00
|
|
|
void raw_destructor<SkVertices>(SkVertices *ptr) {
|
2018-12-12 15:35:13 +00:00
|
|
|
}
|
|
|
|
|
2019-09-26 17:20:50 +00:00
|
|
|
#ifndef SK_NO_FONTS
|
2018-11-03 11:51:19 +00:00
|
|
|
template<>
|
2019-09-26 17:20:50 +00:00
|
|
|
void raw_destructor<SkTextBlob>(SkTextBlob *ptr) {
|
2018-11-03 11:51:19 +00:00
|
|
|
}
|
2019-01-14 13:36:08 +00:00
|
|
|
|
|
|
|
template<>
|
2019-09-26 17:20:50 +00:00
|
|
|
void raw_destructor<SkTypeface>(SkTypeface *ptr) {
|
2019-01-14 13:36:08 +00:00
|
|
|
}
|
2019-09-26 17:20:50 +00:00
|
|
|
#endif
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-26 17:20:50 +00:00
|
|
|
// Some signatures below have uintptr_t instead of a pointer to a primitive
|
2018-09-20 21:39:31 +00:00
|
|
|
// type (e.g. SkScalar). This is necessary because we can't use "bind" (EMSCRIPTEN_BINDINGS)
|
|
|
|
// and pointers to primitive types (Only bound types like SkPoint). We could if we used
|
|
|
|
// cwrap (see https://becominghuman.ai/passing-and-returning-webassembly-array-parameters-a0f572c65d97)
|
|
|
|
// but that requires us to stick to C code and, AFAIK, doesn't allow us to return nice things like
|
|
|
|
// SkPath or SkCanvas.
|
|
|
|
//
|
2019-09-26 17:20:50 +00:00
|
|
|
// So, basically, if we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primitive pointers
|
2018-09-20 21:39:31 +00:00
|
|
|
// 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.
|
|
|
|
EMSCRIPTEN_BINDINGS(Skia) {
|
2018-10-11 12:51:55 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
function("currentContext", &emscripten_webgl_get_current_context);
|
|
|
|
function("setCurrentContext", &emscripten_webgl_make_context_current);
|
2019-03-08 15:04:28 +00:00
|
|
|
function("MakeGrContext", &MakeGrContext);
|
|
|
|
function("MakeOnScreenGLSurface", &MakeOnScreenGLSurface);
|
|
|
|
function("MakeRenderTarget", select_overload<sk_sp<SkSurface>(sk_sp<GrContext>, int, int)>(&MakeRenderTarget));
|
|
|
|
function("MakeRenderTarget", select_overload<sk_sp<SkSurface>(sk_sp<GrContext>, SimpleImageInfo)>(&MakeRenderTarget));
|
|
|
|
|
2018-10-16 14:15:01 +00:00
|
|
|
constant("gpu", true);
|
2018-11-20 19:07:42 +00:00
|
|
|
#endif
|
2019-09-11 19:23:00 +00:00
|
|
|
function("computeTonalColors", &computeTonalColors);
|
2019-09-18 20:18:17 +00:00
|
|
|
function("_decodeAnimatedImage", optional_override([](uintptr_t /* uint8_t* */ iptr,
|
|
|
|
size_t length)->sk_sp<SkAnimatedImage> {
|
|
|
|
uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
|
|
|
|
sk_sp<SkData> bytes = SkData::MakeFromMalloc(imgData, length);
|
|
|
|
auto codec = SkAndroidCodec::MakeFromData(bytes);
|
|
|
|
if (nullptr == codec) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return SkAnimatedImage::Make(std::move(codec));
|
|
|
|
}), allow_raw_pointers());
|
2018-12-03 17:31:04 +00:00
|
|
|
function("_decodeImage", optional_override([](uintptr_t /* uint8_t* */ iptr,
|
|
|
|
size_t length)->sk_sp<SkImage> {
|
|
|
|
uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
|
2019-02-28 21:05:09 +00:00
|
|
|
sk_sp<SkData> bytes = SkData::MakeFromMalloc(imgData, length);
|
|
|
|
return SkImage::MakeFromEncoded(std::move(bytes));
|
2018-12-03 17:31:04 +00:00
|
|
|
}), allow_raw_pointers());
|
2018-11-30 19:05:58 +00:00
|
|
|
function("_getRasterDirectSurface", optional_override([](const SimpleImageInfo ii,
|
2018-12-04 18:57:36 +00:00
|
|
|
uintptr_t /* uint8_t* */ pPtr,
|
2018-11-30 19:05:58 +00:00
|
|
|
size_t rowBytes)->sk_sp<SkSurface> {
|
2018-12-04 18:57:36 +00:00
|
|
|
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
|
2018-11-30 19:05:58 +00:00
|
|
|
SkImageInfo imageInfo = toSkImageInfo(ii);
|
|
|
|
return SkSurface::MakeRasterDirect(imageInfo, pixels, rowBytes, nullptr);
|
|
|
|
}), allow_raw_pointers());
|
2018-10-11 12:51:55 +00:00
|
|
|
function("_getRasterN32PremulSurface", optional_override([](int width, int height)->sk_sp<SkSurface> {
|
|
|
|
return SkSurface::MakeRasterN32Premul(width, height, nullptr);
|
|
|
|
}), allow_raw_pointers());
|
2018-11-20 19:07:42 +00:00
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
|
2018-09-20 21:39:31 +00:00
|
|
|
function("MakeSkCornerPathEffect", &SkCornerPathEffect::Make, allow_raw_pointers());
|
|
|
|
function("MakeSkDiscretePathEffect", &SkDiscretePathEffect::Make, allow_raw_pointers());
|
2019-10-29 13:55:39 +00:00
|
|
|
// Deprecated: use Canvaskit.SkMaskFilter.MakeBlur
|
2018-11-27 18:26:59 +00:00
|
|
|
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());
|
2019-01-07 16:08:55 +00:00
|
|
|
function("_MakePathFromCmds", &MakePathFromCmds);
|
2018-11-03 11:51:19 +00:00
|
|
|
function("MakePathFromOp", &MakePathFromOp);
|
2018-12-17 21:01:36 +00:00
|
|
|
function("MakePathFromSVGString", &MakePathFromSVGString);
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
// These won't be called directly, there's a JS helper to deal with typed arrays.
|
2018-09-20 21:39:31 +00:00
|
|
|
function("_MakeSkDashPathEffect", optional_override([](uintptr_t /* float* */ cptr, int count, SkScalar phase)->sk_sp<SkPathEffect> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const float* intervals = reinterpret_cast<const float*>(cptr);
|
|
|
|
return SkDashPathEffect::Make(intervals, count, phase);
|
|
|
|
}), allow_raw_pointers());
|
2018-12-04 18:57:36 +00:00
|
|
|
function("_MakeImage", optional_override([](SimpleImageInfo ii,
|
|
|
|
uintptr_t /* uint8_t* */ pPtr, int plen,
|
|
|
|
size_t rowBytes)->sk_sp<SkImage> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
|
|
|
|
SkImageInfo info = toSkImageInfo(ii);
|
|
|
|
sk_sp<SkData> pixelData = SkData::MakeFromMalloc(pixels, plen);
|
|
|
|
|
|
|
|
return SkImage::MakeRasterData(info, pixelData, rowBytes);
|
|
|
|
}), allow_raw_pointers());
|
2018-11-03 11:51:19 +00:00
|
|
|
function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
|
|
|
|
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
2019-04-03 20:51:47 +00:00
|
|
|
int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
|
2018-11-03 11:51:19 +00:00
|
|
|
SkPoint points[] = { start, end };
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
|
|
|
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
|
|
|
|
|
|
|
return SkGradientShader::MakeLinear(points, colors, positions, count,
|
|
|
|
mode, flags, nullptr);
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
|
|
|
|
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
2019-04-03 20:51:47 +00:00
|
|
|
int count, SkTileMode mode, uint32_t flags,
|
2018-11-03 11:51:19 +00:00
|
|
|
const SimpleMatrix& lm)->sk_sp<SkShader> {
|
|
|
|
SkPoint points[] = { start, end };
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
|
|
|
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
|
|
|
|
|
|
|
SkMatrix localMatrix = toSkMatrix(lm);
|
|
|
|
|
|
|
|
return SkGradientShader::MakeLinear(points, colors, positions, count,
|
|
|
|
mode, flags, &localMatrix);
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
|
|
|
|
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
2019-04-03 20:51:47 +00:00
|
|
|
int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
|
2018-11-03 11:51:19 +00:00
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
|
|
|
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
|
|
|
|
|
|
|
return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
|
|
|
|
mode, flags, nullptr);
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
|
|
|
|
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
2019-04-03 20:51:47 +00:00
|
|
|
int count, SkTileMode mode, uint32_t flags,
|
2018-11-03 11:51:19 +00:00
|
|
|
const SimpleMatrix& lm)->sk_sp<SkShader> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
|
|
|
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
|
|
|
|
|
|
|
SkMatrix localMatrix = toSkMatrix(lm);
|
|
|
|
return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
|
|
|
|
mode, flags, &localMatrix);
|
|
|
|
}), allow_raw_pointers());
|
2018-11-29 20:07:02 +00:00
|
|
|
function("_MakeTwoPointConicalGradientShader", optional_override([](
|
|
|
|
SkPoint start, SkScalar startRadius,
|
|
|
|
SkPoint end, SkScalar endRadius,
|
|
|
|
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
2019-04-03 20:51:47 +00:00
|
|
|
int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
|
2018-11-29 20:07:02 +00:00
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
|
|
|
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
|
|
|
|
|
|
|
return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
|
|
|
|
colors, positions, count, mode,
|
|
|
|
flags, nullptr);
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
function("_MakeTwoPointConicalGradientShader", optional_override([](
|
|
|
|
SkPoint start, SkScalar startRadius,
|
|
|
|
SkPoint end, SkScalar endRadius,
|
|
|
|
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
2019-04-03 20:51:47 +00:00
|
|
|
int count, SkTileMode mode, uint32_t flags,
|
2018-11-29 20:07:02 +00:00
|
|
|
const SimpleMatrix& lm)->sk_sp<SkShader> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
|
|
|
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
|
|
|
|
|
|
|
SkMatrix localMatrix = toSkMatrix(lm);
|
|
|
|
return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
|
|
|
|
colors, positions, count, mode,
|
|
|
|
flags, &localMatrix);
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
class_<GrContext>("GrContext")
|
2019-03-22 19:41:36 +00:00
|
|
|
.smart_ptr<sk_sp<GrContext>>("sk_sp<GrContext>")
|
|
|
|
.function("getResourceCacheLimitBytes", optional_override([](GrContext& self)->size_t {
|
|
|
|
int maxResources = 0;// ignored
|
|
|
|
size_t currMax = 0;
|
|
|
|
self.getResourceCacheLimits(&maxResources, &currMax);
|
|
|
|
return currMax;
|
|
|
|
}))
|
|
|
|
.function("getResourceCacheUsageBytes", optional_override([](GrContext& self)->size_t {
|
|
|
|
int usedResources = 0;// ignored
|
|
|
|
size_t currUsage = 0;
|
|
|
|
self.getResourceCacheUsage(&usedResources, &currUsage);
|
|
|
|
return currUsage;
|
|
|
|
}))
|
|
|
|
.function("setResourceCacheLimitBytes", optional_override([](GrContext& self, size_t maxResourceBytes)->void {
|
|
|
|
int maxResources = 0;
|
|
|
|
size_t currMax = 0; // ignored
|
|
|
|
self.getResourceCacheLimits(&maxResources, &currMax);
|
|
|
|
self.setResourceCacheLimits(maxResources, maxResourceBytes);
|
|
|
|
}));
|
2019-03-08 15:04:28 +00:00
|
|
|
#endif
|
|
|
|
|
2019-09-18 20:18:17 +00:00
|
|
|
class_<SkAnimatedImage>("SkAnimatedImage")
|
|
|
|
.smart_ptr<sk_sp<SkAnimatedImage>>("sk_sp<SkAnimatedImage>")
|
2019-11-08 14:55:15 +00:00
|
|
|
.function("decodeNextFrame", &SkAnimatedImage::decodeNextFrame)
|
|
|
|
.function("getFrameCount", &SkAnimatedImage::getFrameCount)
|
2019-09-18 20:18:17 +00:00
|
|
|
.function("getRepetitionCount", &SkAnimatedImage::getRepetitionCount)
|
2019-11-08 14:55:15 +00:00
|
|
|
.function("height", optional_override([](SkAnimatedImage& self)->int32_t {
|
|
|
|
return self.dimensions().height();
|
|
|
|
}))
|
|
|
|
.function("reset", &SkAnimatedImage::reset)
|
|
|
|
.function("width", optional_override([](SkAnimatedImage& self)->int32_t {
|
|
|
|
return self.dimensions().width();
|
|
|
|
}));
|
2019-09-18 20:18:17 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
class_<SkCanvas>("SkCanvas")
|
|
|
|
.constructor<>()
|
2019-03-29 14:39:52 +00:00
|
|
|
.function("clear", &SkCanvas::clear)
|
2018-11-29 20:07:02 +00:00
|
|
|
.function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
|
2019-11-08 14:55:15 +00:00
|
|
|
.function("clipRRect", optional_override([](SkCanvas& self, const SimpleRRect& r, SkClipOp op, bool doAntiAlias) {
|
2019-10-22 13:43:34 +00:00
|
|
|
self.clipRRect(toRRect(r), op, doAntiAlias);
|
|
|
|
}))
|
2018-12-04 18:57:36 +00:00
|
|
|
.function("clipRect", select_overload<void (const SkRect&, SkClipOp, bool)>(&SkCanvas::clipRect))
|
|
|
|
.function("concat", optional_override([](SkCanvas& self, const SimpleMatrix& m) {
|
|
|
|
self.concat(toSkMatrix(m));
|
|
|
|
}))
|
2019-01-07 16:08:55 +00:00
|
|
|
.function("drawArc", &SkCanvas::drawArc)
|
2019-03-29 14:39:52 +00:00
|
|
|
.function("_drawAtlas", optional_override([](SkCanvas& self,
|
|
|
|
const sk_sp<SkImage>& atlas, uintptr_t /* SkRSXform* */ xptr,
|
|
|
|
uintptr_t /* SkRect* */ rptr, uintptr_t /* SkColor* */ cptr, int count,
|
|
|
|
SkBlendMode mode, const SkPaint* paint)->void {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkRSXform* dstXforms = reinterpret_cast<const SkRSXform*>(xptr);
|
|
|
|
const SkRect* srcRects = reinterpret_cast<const SkRect*>(rptr);
|
|
|
|
const SkColor* colors = nullptr;
|
|
|
|
if (cptr) {
|
|
|
|
colors = reinterpret_cast<const SkColor*>(cptr);
|
|
|
|
}
|
|
|
|
self.drawAtlas(atlas, dstXforms, srcRects, colors, count, mode, nullptr, paint);
|
|
|
|
}), allow_raw_pointers())
|
2019-08-26 19:48:09 +00:00
|
|
|
.function("drawCircle", select_overload<void (SkScalar, SkScalar, SkScalar, const SkPaint& paint)>(&SkCanvas::drawCircle))
|
2019-10-22 13:43:34 +00:00
|
|
|
.function("drawColor", &SkCanvas::drawColor)
|
2019-09-03 16:59:06 +00:00
|
|
|
.function("drawDRRect",optional_override([](SkCanvas& self, const SimpleRRect& o, const SimpleRRect& i, const SkPaint& paint) {
|
|
|
|
self.drawDRRect(toRRect(o), toRRect(i), paint);
|
|
|
|
}))
|
2019-09-18 20:18:17 +00:00
|
|
|
.function("drawAnimatedImage", optional_override([](SkCanvas& self, sk_sp<SkAnimatedImage>& aImg,
|
2019-11-11 15:06:08 +00:00
|
|
|
SkScalar x, SkScalar y)->void {
|
2019-09-18 20:18:17 +00:00
|
|
|
self.drawDrawable(aImg.get(), x, y);
|
|
|
|
}), allow_raw_pointers())
|
2018-12-03 17:31:04 +00:00
|
|
|
.function("drawImage", select_overload<void (const sk_sp<SkImage>&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers())
|
2019-11-11 15:06:08 +00:00
|
|
|
.function("drawImageNine", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
|
|
|
|
SkIRect center, SkRect dst,
|
|
|
|
const SkPaint* paint)->void {
|
|
|
|
self.drawImageNine(image, center, dst, paint);
|
|
|
|
}), allow_raw_pointers())
|
2018-12-03 17:31:04 +00:00
|
|
|
.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())
|
2019-01-07 16:08:55 +00:00
|
|
|
.function("drawLine", select_overload<void (SkScalar, SkScalar, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawLine))
|
|
|
|
.function("drawOval", &SkCanvas::drawOval)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("drawPaint", &SkCanvas::drawPaint)
|
2019-10-03 15:22:08 +00:00
|
|
|
#ifdef SK_INCLUDE_PARAGRAPH
|
|
|
|
.function("drawParagraph", optional_override([](SkCanvas& self, skia::textlayout::Paragraph* p,
|
|
|
|
SkScalar x, SkScalar y) {
|
|
|
|
p->paint(&self, x, y);
|
|
|
|
}), allow_raw_pointers())
|
|
|
|
#endif
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("drawPath", &SkCanvas::drawPath)
|
2019-04-05 17:00:01 +00:00
|
|
|
// Of note, picture is *not* what is colloquially thought of as a "picture", what we call
|
|
|
|
// a bitmap. An SkPicture is a series of draw commands.
|
|
|
|
.function("drawPicture", select_overload<void (const sk_sp<SkPicture>&)>(&SkCanvas::drawPicture))
|
2019-11-11 15:06:08 +00:00
|
|
|
.function("_drawPoints", optional_override([](SkCanvas& self, SkCanvas::PointMode mode,
|
|
|
|
uintptr_t /* SkPoint* */ pptr,
|
|
|
|
int count, SkPaint paint)->void {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkPoint* pts = reinterpret_cast<const SkPoint*>(pptr);
|
|
|
|
self.drawPoints(mode, count, pts, paint);
|
|
|
|
}))
|
2019-09-03 16:59:06 +00:00
|
|
|
.function("drawRRect",optional_override([](SkCanvas& self, const SimpleRRect& r, const SkPaint& paint) {
|
|
|
|
self.drawRRect(toRRect(r), paint);
|
|
|
|
}))
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("drawRect", &SkCanvas::drawRect)
|
2019-01-07 16:08:55 +00:00
|
|
|
.function("drawRoundRect", &SkCanvas::drawRoundRect)
|
2018-11-27 18:26:59 +00:00
|
|
|
.function("drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
|
|
|
|
const SkPoint3& zPlaneParams,
|
|
|
|
const SkPoint3& lightPos, SkScalar lightRadius,
|
2019-03-29 14:39:52 +00:00
|
|
|
SkColor ambientColor, SkColor spotColor,
|
2018-11-27 18:26:59 +00:00
|
|
|
uint32_t flags) {
|
|
|
|
SkShadowUtils::DrawShadow(&self, path, zPlaneParams, lightPos, lightRadius,
|
2019-03-29 14:39:52 +00:00
|
|
|
ambientColor, spotColor, flags);
|
2018-11-27 18:26:59 +00:00
|
|
|
}))
|
2019-09-26 17:20:50 +00:00
|
|
|
#ifndef SK_NO_FONTS
|
2019-02-22 15:04:06 +00:00
|
|
|
.function("_drawShapedText", &drawShapedText)
|
2019-01-14 13:36:08 +00:00
|
|
|
.function("_drawSimpleText", optional_override([](SkCanvas& self, uintptr_t /* char* */ sptr,
|
|
|
|
size_t len, SkScalar x, SkScalar y, const SkFont& font,
|
|
|
|
const SkPaint& paint) {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const char* str = reinterpret_cast<const char*>(sptr);
|
|
|
|
|
|
|
|
self.drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
|
2018-09-20 21:39:31 +00:00
|
|
|
}))
|
2019-01-14 13:36:08 +00:00
|
|
|
.function("drawTextBlob", select_overload<void (const sk_sp<SkTextBlob>&, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawTextBlob))
|
2019-09-26 17:20:50 +00:00
|
|
|
#endif
|
2018-11-03 11:51:19 +00:00
|
|
|
.function("drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("flush", &SkCanvas::flush)
|
2019-08-26 19:48:09 +00:00
|
|
|
.function("getSaveCount", &SkCanvas::getSaveCount)
|
2018-12-07 15:09:11 +00:00
|
|
|
.function("getTotalMatrix", optional_override([](const SkCanvas& self)->SimpleMatrix {
|
|
|
|
SkMatrix m = self.getTotalMatrix();
|
|
|
|
return toSimpleSkMatrix(m);
|
|
|
|
}))
|
2019-03-08 15:04:28 +00:00
|
|
|
.function("makeSurface", optional_override([](SkCanvas& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
|
|
|
|
return self.makeSurface(toSkImageInfo(sii), nullptr);
|
|
|
|
}), allow_raw_pointers())
|
2018-12-04 18:57:36 +00:00
|
|
|
.function("_readPixels", optional_override([](SkCanvas& self, SimpleImageInfo di,
|
|
|
|
uintptr_t /* uint8_t* */ pPtr,
|
|
|
|
size_t dstRowBytes, int srcX, int srcY) {
|
|
|
|
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
|
|
|
|
SkImageInfo dstInfo = toSkImageInfo(di);
|
|
|
|
|
|
|
|
return self.readPixels(dstInfo, pixels, dstRowBytes, srcX, srcY);
|
|
|
|
}))
|
2018-11-29 20:07:02 +00:00
|
|
|
.function("restore", &SkCanvas::restore)
|
2019-03-06 13:25:36 +00:00
|
|
|
.function("restoreToCount", &SkCanvas::restoreToCount)
|
2018-11-03 11:51:19 +00:00
|
|
|
.function("rotate", select_overload<void (SkScalar, SkScalar, SkScalar)>(&SkCanvas::rotate))
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("save", &SkCanvas::save)
|
2019-10-29 14:48:26 +00:00
|
|
|
// 2 params
|
2019-03-06 13:25:36 +00:00
|
|
|
.function("saveLayer", select_overload<int (const SkRect&, const SkPaint*)>(&SkCanvas::saveLayer),
|
|
|
|
allow_raw_pointers())
|
2019-10-29 14:48:26 +00:00
|
|
|
// 3 params (effectively with SaveLayerRec, but no bounds)
|
|
|
|
.function("saveLayer", saveLayerRec, allow_raw_pointers())
|
|
|
|
// 4 params (effectively with SaveLayerRec)
|
|
|
|
.function("saveLayer", saveLayerRecBounds, allow_raw_pointers())
|
|
|
|
|
2018-10-19 18:34:34 +00:00
|
|
|
.function("scale", &SkCanvas::scale)
|
|
|
|
.function("skew", &SkCanvas::skew)
|
2018-12-04 18:57:36 +00:00
|
|
|
.function("translate", &SkCanvas::translate)
|
|
|
|
.function("_writePixels", optional_override([](SkCanvas& self, SimpleImageInfo di,
|
|
|
|
uintptr_t /* uint8_t* */ pPtr,
|
|
|
|
size_t srcRowBytes, int dstX, int dstY) {
|
|
|
|
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
|
|
|
|
SkImageInfo dstInfo = toSkImageInfo(di);
|
|
|
|
|
|
|
|
return self.writePixels(dstInfo, pixels, srcRowBytes, dstX, dstY);
|
2019-10-03 15:22:08 +00:00
|
|
|
}));
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2019-09-12 15:11:25 +00:00
|
|
|
class_<SkColorFilter>("SkColorFilter")
|
|
|
|
.smart_ptr<sk_sp<SkColorFilter>>("sk_sp<SkColorFilter>>")
|
|
|
|
.class_function("MakeBlend", &SkColorFilters::Blend)
|
|
|
|
.class_function("MakeCompose", &SkColorFilters::Compose)
|
|
|
|
.class_function("MakeLerp", &SkColorFilters::Lerp)
|
|
|
|
.class_function("MakeLinearToSRGBGamma", &SkColorFilters::LinearToSRGBGamma)
|
|
|
|
.class_function("_makeMatrix", optional_override([](uintptr_t /* float* */ fPtr) {
|
|
|
|
float* twentyFloats = reinterpret_cast<float*>(fPtr);
|
|
|
|
return SkColorFilters::Matrix(twentyFloats);
|
|
|
|
}))
|
|
|
|
.class_function("MakeSRGBToLinearGamma", &SkColorFilters::SRGBToLinearGamma);
|
|
|
|
|
2019-11-20 19:17:53 +00:00
|
|
|
class_<SkContourMeasureIter>("SkContourMeasureIter")
|
|
|
|
.constructor<const SkPath&, bool, SkScalar>()
|
|
|
|
.function("next", &SkContourMeasureIter::next);
|
|
|
|
|
|
|
|
class_<SkContourMeasure>("SkContourMeasure")
|
|
|
|
.smart_ptr<sk_sp<SkContourMeasure>>("sk_sp<SkContourMeasure>>")
|
|
|
|
.function("getPosTan", optional_override([](SkContourMeasure& self,
|
|
|
|
SkScalar distance) -> PosTan {
|
|
|
|
SkPoint p{0, 0};
|
|
|
|
SkVector v{0, 0};
|
|
|
|
if (!self.getPosTan(distance, &p, &v)) {
|
|
|
|
SkDebugf("zero-length path in getPosTan\n");
|
|
|
|
}
|
|
|
|
return PosTan{p.x(), p.y(), v.x(), v.y()};
|
|
|
|
}))
|
|
|
|
.function("getSegment", optional_override([](SkContourMeasure& self, SkScalar startD,
|
|
|
|
SkScalar stopD, bool startWithMoveTo) -> SkPath {
|
|
|
|
SkPath p;
|
|
|
|
bool ok = self.getSegment(startD, stopD, &p, startWithMoveTo);
|
|
|
|
if (ok) {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return SkPath();
|
|
|
|
}))
|
|
|
|
.function("isClosed", &SkContourMeasure::isClosed)
|
|
|
|
.function("length", &SkContourMeasure::length);
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
class_<SkData>("SkData")
|
|
|
|
.smart_ptr<sk_sp<SkData>>("sk_sp<SkData>>")
|
|
|
|
.function("size", &SkData::size);
|
|
|
|
|
2019-09-18 20:18:17 +00:00
|
|
|
class_<SkDrawable>("SkDrawable")
|
|
|
|
.smart_ptr<sk_sp<SkDrawable>>("sk_sp<SkDrawable>>");
|
|
|
|
|
2019-09-26 17:20:50 +00:00
|
|
|
#ifndef SK_NO_FONTS
|
2019-01-02 20:13:57 +00:00
|
|
|
class_<SkFont>("SkFont")
|
|
|
|
.constructor<>()
|
|
|
|
.constructor<sk_sp<SkTypeface>>()
|
|
|
|
.constructor<sk_sp<SkTypeface>, SkScalar>()
|
|
|
|
.constructor<sk_sp<SkTypeface>, SkScalar, SkScalar, SkScalar>()
|
|
|
|
.function("getScaleX", &SkFont::getScaleX)
|
|
|
|
.function("getSize", &SkFont::getSize)
|
|
|
|
.function("getSkewX", &SkFont::getSkewX)
|
|
|
|
.function("getTypeface", &SkFont::getTypeface, allow_raw_pointers())
|
2019-03-15 19:36:29 +00:00
|
|
|
.function("_getWidths", optional_override([](SkFont& self, uintptr_t /* char* */ sptr,
|
|
|
|
size_t strLen, size_t expectedCodePoints,
|
|
|
|
uintptr_t /* SkScalar* */ wptr) -> bool {
|
|
|
|
char* str = reinterpret_cast<char*>(sptr);
|
|
|
|
SkScalar* widths = reinterpret_cast<SkScalar*>(wptr);
|
|
|
|
|
|
|
|
SkGlyphID* glyphStorage = new SkGlyphID[expectedCodePoints];
|
|
|
|
int actualCodePoints = self.textToGlyphs(str, strLen, SkTextEncoding::kUTF8,
|
|
|
|
glyphStorage, expectedCodePoints);
|
|
|
|
if (actualCodePoints != expectedCodePoints) {
|
|
|
|
SkDebugf("Actually %d glyphs, expected only %d\n",
|
|
|
|
actualCodePoints, expectedCodePoints);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.getWidths(glyphStorage, actualCodePoints, widths);
|
|
|
|
delete[] glyphStorage;
|
|
|
|
return true;
|
|
|
|
}))
|
2019-01-02 20:13:57 +00:00
|
|
|
.function("measureText", optional_override([](SkFont& self, std::string text) {
|
|
|
|
// TODO(kjlubick): This does not work well for non-ascii
|
|
|
|
// Need to maybe add a helper in interface.js that supports UTF-8
|
|
|
|
// Otherwise, go with std::wstring and set UTF-32 encoding.
|
|
|
|
return self.measureText(text.c_str(), text.length(), SkTextEncoding::kUTF8);
|
|
|
|
}))
|
|
|
|
.function("setScaleX", &SkFont::setScaleX)
|
|
|
|
.function("setSize", &SkFont::setSize)
|
|
|
|
.function("setSkewX", &SkFont::setSkewX)
|
|
|
|
.function("setTypeface", &SkFont::setTypeface, allow_raw_pointers());
|
|
|
|
|
2019-02-22 15:04:06 +00:00
|
|
|
class_<ShapedText>("ShapedText")
|
|
|
|
.constructor<ShapedTextOpts>()
|
|
|
|
.function("getBounds", &ShapedText::getBounds);
|
|
|
|
|
2018-12-12 15:35:13 +00:00
|
|
|
class_<SkFontMgr>("SkFontMgr")
|
|
|
|
.smart_ptr<sk_sp<SkFontMgr>>("sk_sp<SkFontMgr>")
|
2019-09-26 17:20:50 +00:00
|
|
|
.class_function("_fromData", optional_override([](uintptr_t /* uint8_t** */ dPtr,
|
|
|
|
uintptr_t /* size_t* */ sPtr,
|
|
|
|
int numFonts)->sk_sp<SkFontMgr> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
auto datas = reinterpret_cast<const uint8_t**>(dPtr);
|
|
|
|
auto sizes = reinterpret_cast<const size_t*>(sPtr);
|
|
|
|
|
|
|
|
return SkFontMgr_New_Custom_Data(datas, sizes, numFonts);
|
|
|
|
}), allow_raw_pointers())
|
2018-12-12 15:35:13 +00:00
|
|
|
.class_function("RefDefault", &SkFontMgr::RefDefault)
|
|
|
|
#ifdef SK_DEBUG
|
|
|
|
.function("dumpFamilies", optional_override([](SkFontMgr& self) {
|
|
|
|
int numFam = self.countFamilies();
|
2019-09-26 17:20:50 +00:00
|
|
|
SkDebugf("There are %d font families\n", numFam);
|
2018-12-12 15:35:13 +00:00
|
|
|
for (int i = 0 ; i< numFam; i++) {
|
|
|
|
SkString s;
|
|
|
|
self.getFamilyName(i, &s);
|
2019-09-26 17:20:50 +00:00
|
|
|
SkDebugf("\t%s\n", s.c_str());
|
2018-12-12 15:35:13 +00:00
|
|
|
}
|
|
|
|
}))
|
|
|
|
#endif
|
|
|
|
.function("countFamilies", &SkFontMgr::countFamilies)
|
|
|
|
.function("_makeTypefaceFromData", optional_override([](SkFontMgr& self,
|
|
|
|
uintptr_t /* uint8_t* */ fPtr,
|
|
|
|
int flen)->sk_sp<SkTypeface> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
uint8_t* font = reinterpret_cast<uint8_t*>(fPtr);
|
|
|
|
sk_sp<SkData> fontData = SkData::MakeFromMalloc(font, flen);
|
|
|
|
|
|
|
|
return self.makeFromData(fontData);
|
|
|
|
}), allow_raw_pointers());
|
2019-09-26 17:20:50 +00:00
|
|
|
#endif
|
2018-12-12 15:35:13 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
class_<SkImage>("SkImage")
|
|
|
|
.smart_ptr<sk_sp<SkImage>>("sk_sp<SkImage>")
|
2018-12-03 17:31:04 +00:00
|
|
|
.function("height", &SkImage::height)
|
|
|
|
.function("width", &SkImage::width)
|
2018-11-14 23:01:19 +00:00
|
|
|
.function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData))
|
2019-04-04 13:28:53 +00:00
|
|
|
.function("_encodeToDataWithFormat", select_overload<sk_sp<SkData>(SkEncodedImageFormat encodedImageFormat, int quality)const>(&SkImage::encodeToData))
|
|
|
|
// Allow localMatrix to be optional, so we have 2 declarations of these shaders
|
|
|
|
.function("_makeShader", optional_override([](sk_sp<SkImage> self,
|
|
|
|
SkTileMode tx, SkTileMode ty)->sk_sp<SkShader> {
|
|
|
|
return self->makeShader(tx, ty, nullptr);
|
|
|
|
}), allow_raw_pointers())
|
|
|
|
.function("_makeShader", optional_override([](sk_sp<SkImage> self,
|
|
|
|
SkTileMode tx, SkTileMode ty,
|
|
|
|
const SimpleMatrix& lm)->sk_sp<SkShader> {
|
|
|
|
SkMatrix localMatrix = toSkMatrix(lm);
|
|
|
|
|
|
|
|
return self->makeShader(tx, ty, &localMatrix);
|
2019-05-06 17:04:03 +00:00
|
|
|
}), allow_raw_pointers())
|
|
|
|
.function("_readPixels", optional_override([](sk_sp<SkImage> self,
|
|
|
|
SimpleImageInfo sii, uintptr_t /* uint8_t* */ pPtr,
|
|
|
|
size_t dstRowBytes, int srcX, int srcY)->bool {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
|
|
|
|
SkImageInfo ii = toSkImageInfo(sii);
|
|
|
|
|
|
|
|
return self->readPixels(ii, pixels, dstRowBytes, srcX, srcY);
|
2019-04-04 13:28:53 +00:00
|
|
|
}), allow_raw_pointers());
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2019-10-29 13:55:39 +00:00
|
|
|
class_<SkImageFilter>("SkImageFilter")
|
|
|
|
.smart_ptr<sk_sp<SkImageFilter>>("sk_sp<SkImageFilter>")
|
|
|
|
.class_function("MakeBlur", optional_override([](SkScalar sigmaX, SkScalar sigmaY,
|
|
|
|
SkTileMode tileMode, sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
|
|
|
|
// Emscripten does not like default args nor SkIRect* much
|
|
|
|
return SkImageFilters::Blur(sigmaX, sigmaY, tileMode, input);
|
|
|
|
}))
|
|
|
|
.class_function("MakeColorFilter", optional_override([](sk_sp<SkColorFilter> cf,
|
|
|
|
sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
|
|
|
|
// Emscripten does not like default args nor SkIRect* much
|
|
|
|
return SkImageFilters::ColorFilter(cf, input);
|
|
|
|
}))
|
2019-11-15 19:13:20 +00:00
|
|
|
.class_function("MakeCompose", &SkImageFilters::Compose)
|
|
|
|
.class_function("MakeMatrixTransform", optional_override([](SimpleMatrix sm, SkFilterQuality fq,
|
|
|
|
sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
|
|
|
|
return SkImageFilters::MatrixTransform(toSkMatrix(sm), fq, input);
|
|
|
|
}));
|
2019-10-29 13:55:39 +00:00
|
|
|
|
2018-11-27 18:26:59 +00:00
|
|
|
class_<SkMaskFilter>("SkMaskFilter")
|
2019-10-29 13:55:39 +00:00
|
|
|
.smart_ptr<sk_sp<SkMaskFilter>>("sk_sp<SkMaskFilter>")
|
|
|
|
.class_function("MakeBlur", 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());
|
2018-11-27 18:26:59 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
class_<SkPaint>("SkPaint")
|
|
|
|
.constructor<>()
|
|
|
|
.function("copy", optional_override([](const SkPaint& self)->SkPaint {
|
|
|
|
SkPaint p(self);
|
|
|
|
return p;
|
|
|
|
}))
|
2018-11-28 17:51:56 +00:00
|
|
|
.function("getBlendMode", &SkPaint::getBlendMode)
|
2019-03-29 14:39:52 +00:00
|
|
|
.function("getColor", &SkPaint::getColor)
|
2018-12-03 17:31:04 +00:00
|
|
|
.function("getFilterQuality", &SkPaint::getFilterQuality)
|
2018-11-26 16:47:54 +00:00
|
|
|
.function("getStrokeCap", &SkPaint::getStrokeCap)
|
|
|
|
.function("getStrokeJoin", &SkPaint::getStrokeJoin)
|
2018-12-03 17:31:04 +00:00
|
|
|
.function("getStrokeMiter", &SkPaint::getStrokeMiter)
|
|
|
|
.function("getStrokeWidth", &SkPaint::getStrokeWidth)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("setAntiAlias", &SkPaint::setAntiAlias)
|
2018-11-28 17:51:56 +00:00
|
|
|
.function("setBlendMode", &SkPaint::setBlendMode)
|
2019-05-20 15:47:49 +00:00
|
|
|
.function("setColor", optional_override([](SkPaint& self, SkColor c) {
|
|
|
|
self.setColor(c);
|
|
|
|
}))
|
|
|
|
.function("setColorf", optional_override([](SkPaint& self,
|
|
|
|
float r, float g, float b, float a) {
|
|
|
|
self.setColor({r, g, b, a});
|
|
|
|
}))
|
2019-09-12 15:11:25 +00:00
|
|
|
.function("setColorFilter", &SkPaint::setColorFilter)
|
2018-12-03 17:31:04 +00:00
|
|
|
.function("setFilterQuality", &SkPaint::setFilterQuality)
|
2019-10-29 13:55:39 +00:00
|
|
|
.function("setImageFilter", &SkPaint::setImageFilter)
|
2018-11-27 18:26:59 +00:00
|
|
|
.function("setMaskFilter", &SkPaint::setMaskFilter)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("setPathEffect", &SkPaint::setPathEffect)
|
|
|
|
.function("setShader", &SkPaint::setShader)
|
2018-11-26 16:47:54 +00:00
|
|
|
.function("setStrokeCap", &SkPaint::setStrokeCap)
|
|
|
|
.function("setStrokeJoin", &SkPaint::setStrokeJoin)
|
2018-12-03 17:31:04 +00:00
|
|
|
.function("setStrokeMiter", &SkPaint::setStrokeMiter)
|
|
|
|
.function("setStrokeWidth", &SkPaint::setStrokeWidth)
|
2019-01-14 13:36:08 +00:00
|
|
|
.function("setStyle", &SkPaint::setStyle);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
class_<SkPathEffect>("SkPathEffect")
|
|
|
|
.smart_ptr<sk_sp<SkPathEffect>>("sk_sp<SkPathEffect>");
|
|
|
|
|
|
|
|
class_<SkPath>("SkPath")
|
|
|
|
.constructor<>()
|
|
|
|
.constructor<const SkPath&>()
|
2018-11-20 17:51:16 +00:00
|
|
|
.function("_addArc", &ApplyAddArc)
|
2018-09-20 21:39:31 +00:00
|
|
|
// interface.js has 3 overloads of addPath
|
2019-08-26 19:48:09 +00:00
|
|
|
.function("_addOval", &ApplyAddOval)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("_addPath", &ApplyAddPath)
|
2019-11-11 15:06:08 +00:00
|
|
|
.function("_addPoly", optional_override([](SkPath& self,
|
|
|
|
uintptr_t /* SkPoint* */ pptr,
|
|
|
|
int count, bool close)->void {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const SkPoint* pts = reinterpret_cast<const SkPoint*>(pptr);
|
|
|
|
self.addPoly(pts, count, close);
|
|
|
|
}))
|
2018-11-20 17:51:16 +00:00
|
|
|
// interface.js has 4 overloads of addRect
|
|
|
|
.function("_addRect", &ApplyAddRect)
|
2019-01-07 16:08:55 +00:00
|
|
|
// interface.js has 4 overloads of addRoundRect
|
|
|
|
.function("_addRoundRect", &ApplyAddRoundRect)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("_arcTo", &ApplyArcTo)
|
2018-12-07 18:03:08 +00:00
|
|
|
.function("_arcTo", &ApplyArcToAngle)
|
2019-11-01 18:36:52 +00:00
|
|
|
.function("_arcTo", &ApplyArcToArcSize)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("_close", &ApplyClose)
|
|
|
|
.function("_conicTo", &ApplyConicTo)
|
2018-11-26 16:47:54 +00:00
|
|
|
.function("countPoints", &SkPath::countPoints)
|
2018-12-07 18:03:08 +00:00
|
|
|
.function("contains", &SkPath::contains)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("_cubicTo", &ApplyCubicTo)
|
2018-11-26 16:47:54 +00:00
|
|
|
.function("getPoint", &SkPath::getPoint)
|
2018-12-07 18:03:08 +00:00
|
|
|
.function("isEmpty", &SkPath::isEmpty)
|
2018-12-14 21:10:38 +00:00
|
|
|
.function("isVolatile", &SkPath::isVolatile)
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("_lineTo", &ApplyLineTo)
|
|
|
|
.function("_moveTo", &ApplyMoveTo)
|
2019-11-01 18:36:52 +00:00
|
|
|
.function("_quadTo", &ApplyQuadTo)
|
|
|
|
.function("_rArcTo", &ApplyRArcToArcSize)
|
|
|
|
.function("_rConicTo", &ApplyRConicTo)
|
|
|
|
.function("_rCubicTo", &ApplyRCubicTo)
|
|
|
|
.function("_rLineTo", &ApplyRLineTo)
|
|
|
|
.function("_rMoveTo", &ApplyRMoveTo)
|
|
|
|
.function("_rQuadTo", &ApplyRQuadTo)
|
2019-01-07 16:08:55 +00:00
|
|
|
.function("reset", &ApplyReset)
|
|
|
|
.function("rewind", &ApplyRewind)
|
2018-12-14 21:10:38 +00:00
|
|
|
.function("setIsVolatile", &SkPath::setIsVolatile)
|
2018-11-20 17:51:16 +00:00
|
|
|
.function("_transform", select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&ApplyTransform))
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
// PathEffects
|
|
|
|
.function("_dash", &ApplyDash)
|
|
|
|
.function("_trim", &ApplyTrim)
|
|
|
|
.function("_stroke", &ApplyStroke)
|
|
|
|
|
|
|
|
// PathOps
|
|
|
|
.function("_simplify", &ApplySimplify)
|
|
|
|
.function("_op", &ApplyPathOp)
|
|
|
|
|
|
|
|
// Exporting
|
|
|
|
.function("toSVGString", &ToSVGString)
|
2019-01-07 16:08:55 +00:00
|
|
|
.function("toCmds", &ToCmds)
|
2018-11-03 11:51:19 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
.function("setFillType", &SkPath::setFillType)
|
|
|
|
.function("getFillType", &SkPath::getFillType)
|
|
|
|
.function("getBounds", &SkPath::getBounds)
|
|
|
|
.function("computeTightBounds", &SkPath::computeTightBounds)
|
|
|
|
.function("equals", &Equals)
|
2018-11-26 16:47:54 +00:00
|
|
|
.function("copy", &CopyPath)
|
|
|
|
#ifdef SK_DEBUG
|
|
|
|
.function("dump", select_overload<void() const>(&SkPath::dump))
|
|
|
|
.function("dumpHex", select_overload<void() const>(&SkPath::dumpHex))
|
|
|
|
#endif
|
|
|
|
;
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2019-03-15 19:36:29 +00:00
|
|
|
class_<SkPathMeasure>("SkPathMeasure")
|
|
|
|
.constructor<const SkPath&, bool, SkScalar>()
|
|
|
|
.function("getLength", &SkPathMeasure::getLength)
|
|
|
|
.function("getPosTan", optional_override([](SkPathMeasure& self,
|
|
|
|
SkScalar distance) -> PosTan {
|
|
|
|
SkPoint p{0, 0};
|
|
|
|
SkVector v{0, 0};
|
|
|
|
if (!self.getPosTan(distance, &p, &v)) {
|
|
|
|
SkDebugf("zero-length path in getPosTan\n");
|
|
|
|
}
|
|
|
|
return PosTan{p.x(), p.y(), v.x(), v.y()};
|
|
|
|
}))
|
2019-11-11 15:06:08 +00:00
|
|
|
.function("getSegment", optional_override([](SkPathMeasure& self, SkScalar startD,
|
|
|
|
SkScalar stopD, bool startWithMoveTo) -> SkPath {
|
|
|
|
SkPath p;
|
|
|
|
bool ok = self.getSegment(startD, stopD, &p, startWithMoveTo);
|
|
|
|
if (ok) {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return SkPath();
|
|
|
|
}))
|
2019-03-15 19:36:29 +00:00
|
|
|
.function("isClosed", &SkPathMeasure::isClosed)
|
|
|
|
.function("nextContour", &SkPathMeasure::nextContour);
|
|
|
|
|
2019-04-05 17:00:01 +00:00
|
|
|
class_<SkPictureRecorder>("SkPictureRecorder")
|
|
|
|
.constructor<>()
|
|
|
|
.function("beginRecording", optional_override([](SkPictureRecorder& self,
|
|
|
|
const SkRect& bounds) -> SkCanvas* {
|
|
|
|
return self.beginRecording(bounds, nullptr, 0);
|
|
|
|
}), allow_raw_pointers())
|
|
|
|
.function("finishRecordingAsPicture", optional_override([](SkPictureRecorder& self)
|
|
|
|
-> sk_sp<SkPicture> {
|
|
|
|
return self.finishRecordingAsPicture(0);
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
|
|
|
|
class_<SkPicture>("SkPicture")
|
|
|
|
.smart_ptr<sk_sp<SkPicture>>("sk_sp<SkPicture>")
|
|
|
|
// 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.
|
|
|
|
.function("DEBUGONLY_serialize", optional_override([](SkPicture& self) -> sk_sp<SkData> {
|
|
|
|
// Emscripten doesn't play well with optional arguments, which we don't
|
|
|
|
// want to expose anyway.
|
|
|
|
return self.serialize();
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
class_<SkShader>("SkShader")
|
|
|
|
.smart_ptr<sk_sp<SkShader>>("sk_sp<SkShader>");
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
class_<SkSurface>("SkSurface")
|
|
|
|
.smart_ptr<sk_sp<SkSurface>>("sk_sp<SkSurface>")
|
2019-03-01 20:24:35 +00:00
|
|
|
.function("_flush", select_overload<void()>(&SkSurface::flush))
|
2019-03-08 15:04:28 +00:00
|
|
|
.function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers())
|
|
|
|
.function("height", &SkSurface::height)
|
2018-11-21 14:12:09 +00:00
|
|
|
.function("makeImageSnapshot", select_overload<sk_sp<SkImage>()>(&SkSurface::makeImageSnapshot))
|
|
|
|
.function("makeImageSnapshot", select_overload<sk_sp<SkImage>(const SkIRect& bounds)>(&SkSurface::makeImageSnapshot))
|
2019-03-08 15:04:28 +00:00
|
|
|
.function("makeSurface", optional_override([](SkSurface& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
|
|
|
|
return self.makeSurface(toSkImageInfo(sii));
|
|
|
|
}), allow_raw_pointers())
|
|
|
|
.function("width", &SkSurface::width);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2019-09-26 17:20:50 +00:00
|
|
|
#ifndef SK_NO_FONTS
|
2019-01-14 13:36:08 +00:00
|
|
|
class_<SkTextBlob>("SkTextBlob")
|
|
|
|
.smart_ptr<sk_sp<SkTextBlob>>("sk_sp<SkTextBlob>>")
|
2019-03-15 19:36:29 +00:00
|
|
|
.class_function("_MakeFromRSXform", optional_override([](uintptr_t /* char* */ sptr,
|
|
|
|
size_t strBtyes,
|
|
|
|
uintptr_t /* SkRSXform* */ xptr,
|
|
|
|
const SkFont& font,
|
|
|
|
SkTextEncoding encoding)->sk_sp<SkTextBlob> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const char* str = reinterpret_cast<const char*>(sptr);
|
|
|
|
const SkRSXform* xforms = reinterpret_cast<const SkRSXform*>(xptr);
|
|
|
|
|
|
|
|
return SkTextBlob::MakeFromRSXform(str, strBtyes, xforms, font, encoding);
|
|
|
|
}), allow_raw_pointers())
|
2019-03-08 15:04:28 +00:00
|
|
|
.class_function("_MakeFromText", optional_override([](uintptr_t /* char* */ sptr,
|
|
|
|
size_t len, const SkFont& font,
|
|
|
|
SkTextEncoding encoding)->sk_sp<SkTextBlob> {
|
2019-01-14 13:36:08 +00:00
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const char* str = reinterpret_cast<const char*>(sptr);
|
|
|
|
return SkTextBlob::MakeFromText(str, len, font, encoding);
|
|
|
|
}), allow_raw_pointers());
|
|
|
|
|
2018-12-12 15:35:13 +00:00
|
|
|
class_<SkTypeface>("SkTypeface")
|
|
|
|
.smart_ptr<sk_sp<SkTypeface>>("sk_sp<SkTypeface>");
|
2019-09-26 17:20:50 +00:00
|
|
|
#endif
|
2018-12-12 15:35:13 +00:00
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
class_<SkVertices>("SkVertices")
|
|
|
|
.smart_ptr<sk_sp<SkVertices>>("sk_sp<SkVertices>")
|
|
|
|
.function("_applyBones", optional_override([](SkVertices& self, uintptr_t /* Bone* */ bptr, int boneCount)->sk_sp<SkVertices> {
|
|
|
|
// See comment above for uintptr_t explanation
|
|
|
|
const Bone* bones = reinterpret_cast<const Bone*>(bptr);
|
|
|
|
return self.applyBones(bones, boneCount);
|
|
|
|
}))
|
|
|
|
.function("bounds", &SkVertices::bounds)
|
|
|
|
.function("mode", &SkVertices::mode)
|
|
|
|
.function("uniqueID", &SkVertices::uniqueID)
|
2018-11-28 17:51:56 +00:00
|
|
|
#ifdef SK_DEBUG
|
2018-11-03 11:51:19 +00:00
|
|
|
.function("dumpPositions", optional_override([](SkVertices& self)->void {
|
|
|
|
auto pos = self.positions();
|
|
|
|
for(int i = 0; i< self.vertexCount(); i++) {
|
|
|
|
SkDebugf("position[%d] = (%f, %f)\n", i, pos[i].x(), pos[i].y());
|
|
|
|
}
|
|
|
|
}))
|
2018-11-28 17:51:56 +00:00
|
|
|
#endif
|
2018-11-03 11:51:19 +00:00
|
|
|
.function("vertexCount", &SkVertices::vertexCount);
|
|
|
|
|
2019-06-03 18:38:05 +00:00
|
|
|
// Not intended to be called directly by clients
|
|
|
|
class_<SkVertices::Builder>("_SkVerticesBuilder")
|
|
|
|
.constructor<SkVertices::VertexMode, int, int, uint32_t>()
|
|
|
|
.function("boneIndices", optional_override([](SkVertices::Builder& self)->uintptr_t /* BoneIndices* */{
|
|
|
|
// Emscripten won't let us return bare pointers, but we can return ints just fine.
|
|
|
|
return reinterpret_cast<uintptr_t>(self.boneIndices());
|
|
|
|
}))
|
|
|
|
.function("boneWeights", optional_override([](SkVertices::Builder& self)->uintptr_t /* BoneWeights* */{
|
|
|
|
// Emscripten won't let us return bare pointers, but we can return ints just fine.
|
|
|
|
return reinterpret_cast<uintptr_t>(self.boneWeights());
|
|
|
|
}))
|
|
|
|
.function("colors", optional_override([](SkVertices::Builder& self)->uintptr_t /* SkColor* */{
|
|
|
|
// Emscripten won't let us return bare pointers, but we can return ints just fine.
|
|
|
|
return reinterpret_cast<uintptr_t>(self.colors());
|
|
|
|
}))
|
|
|
|
.function("detach", &SkVertices::Builder::detach)
|
|
|
|
.function("indices", optional_override([](SkVertices::Builder& self)->uintptr_t /* uint16_t* */{
|
|
|
|
// Emscripten won't let us return bare pointers, but we can return ints just fine.
|
|
|
|
return reinterpret_cast<uintptr_t>(self.indices());
|
|
|
|
}))
|
|
|
|
.function("positions", optional_override([](SkVertices::Builder& self)->uintptr_t /* SkPoint* */{
|
|
|
|
// Emscripten won't let us return bare pointers, but we can return ints just fine.
|
|
|
|
return reinterpret_cast<uintptr_t>(self.positions());
|
|
|
|
}))
|
|
|
|
.function("texCoords", optional_override([](SkVertices::Builder& self)->uintptr_t /* SkPoint* */{
|
|
|
|
// Emscripten won't let us return bare pointers, but we can return ints just fine.
|
|
|
|
return reinterpret_cast<uintptr_t>(self.texCoords());
|
|
|
|
}));
|
|
|
|
|
2018-11-30 19:05:58 +00:00
|
|
|
enum_<SkAlphaType>("AlphaType")
|
|
|
|
.value("Opaque", SkAlphaType::kOpaque_SkAlphaType)
|
|
|
|
.value("Premul", SkAlphaType::kPremul_SkAlphaType)
|
|
|
|
.value("Unpremul", SkAlphaType::kUnpremul_SkAlphaType);
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
enum_<SkBlendMode>("BlendMode")
|
|
|
|
.value("Clear", SkBlendMode::kClear)
|
|
|
|
.value("Src", SkBlendMode::kSrc)
|
|
|
|
.value("Dst", SkBlendMode::kDst)
|
|
|
|
.value("SrcOver", SkBlendMode::kSrcOver)
|
|
|
|
.value("DstOver", SkBlendMode::kDstOver)
|
|
|
|
.value("SrcIn", SkBlendMode::kSrcIn)
|
|
|
|
.value("DstIn", SkBlendMode::kDstIn)
|
|
|
|
.value("SrcOut", SkBlendMode::kSrcOut)
|
|
|
|
.value("DstOut", SkBlendMode::kDstOut)
|
|
|
|
.value("SrcATop", SkBlendMode::kSrcATop)
|
|
|
|
.value("DstATop", SkBlendMode::kDstATop)
|
|
|
|
.value("Xor", SkBlendMode::kXor)
|
|
|
|
.value("Plus", SkBlendMode::kPlus)
|
|
|
|
.value("Modulate", SkBlendMode::kModulate)
|
|
|
|
.value("Screen", SkBlendMode::kScreen)
|
|
|
|
.value("Overlay", SkBlendMode::kOverlay)
|
|
|
|
.value("Darken", SkBlendMode::kDarken)
|
|
|
|
.value("Lighten", SkBlendMode::kLighten)
|
|
|
|
.value("ColorDodge", SkBlendMode::kColorDodge)
|
|
|
|
.value("ColorBurn", SkBlendMode::kColorBurn)
|
|
|
|
.value("HardLight", SkBlendMode::kHardLight)
|
|
|
|
.value("SoftLight", SkBlendMode::kSoftLight)
|
|
|
|
.value("Difference", SkBlendMode::kDifference)
|
|
|
|
.value("Exclusion", SkBlendMode::kExclusion)
|
|
|
|
.value("Multiply", SkBlendMode::kMultiply)
|
|
|
|
.value("Hue", SkBlendMode::kHue)
|
|
|
|
.value("Saturation", SkBlendMode::kSaturation)
|
|
|
|
.value("Color", SkBlendMode::kColor)
|
|
|
|
.value("Luminosity", SkBlendMode::kLuminosity);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2018-11-27 18:26:59 +00:00
|
|
|
enum_<SkBlurStyle>("BlurStyle")
|
|
|
|
.value("Normal", SkBlurStyle::kNormal_SkBlurStyle)
|
|
|
|
.value("Solid", SkBlurStyle::kSolid_SkBlurStyle)
|
|
|
|
.value("Outer", SkBlurStyle::kOuter_SkBlurStyle)
|
|
|
|
.value("Inner", SkBlurStyle::kInner_SkBlurStyle);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2018-11-29 20:07:02 +00:00
|
|
|
enum_<SkClipOp>("ClipOp")
|
|
|
|
.value("Difference", SkClipOp::kDifference)
|
|
|
|
.value("Intersect", SkClipOp::kIntersect);
|
|
|
|
|
2018-11-30 19:05:58 +00:00
|
|
|
enum_<SkColorType>("ColorType")
|
|
|
|
.value("Alpha_8", SkColorType::kAlpha_8_SkColorType)
|
|
|
|
.value("RGB_565", SkColorType::kRGB_565_SkColorType)
|
|
|
|
.value("ARGB_4444", SkColorType::kARGB_4444_SkColorType)
|
|
|
|
.value("RGBA_8888", SkColorType::kRGBA_8888_SkColorType)
|
|
|
|
.value("RGB_888x", SkColorType::kRGB_888x_SkColorType)
|
|
|
|
.value("BGRA_8888", SkColorType::kBGRA_8888_SkColorType)
|
|
|
|
.value("RGBA_1010102", SkColorType::kRGBA_1010102_SkColorType)
|
|
|
|
.value("RGB_101010x", SkColorType::kRGB_101010x_SkColorType)
|
|
|
|
.value("Gray_8", SkColorType::kGray_8_SkColorType)
|
|
|
|
.value("RGBA_F16", SkColorType::kRGBA_F16_SkColorType)
|
2019-09-04 19:05:35 +00:00
|
|
|
.value("RGBA_F32", SkColorType::kRGBA_F32_SkColorType)
|
2019-09-19 20:05:48 +00:00
|
|
|
.value("R8G8_unorm", SkColorType::kR8G8_unorm_SkColorType)
|
|
|
|
.value("A16_unorm", SkColorType::kA16_unorm_SkColorType)
|
|
|
|
.value("R16G16_unorm", SkColorType::kR16G16_unorm_SkColorType)
|
|
|
|
.value("A16_float", SkColorType::kA16_float_SkColorType)
|
|
|
|
.value("R16G16_float", SkColorType::kR16G16_float_SkColorType)
|
|
|
|
.value("R16G16B16A16_unorm", SkColorType::kR16G16B16A16_unorm_SkColorType);
|
2018-11-30 19:05:58 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
enum_<SkPath::FillType>("FillType")
|
2018-11-03 11:51:19 +00:00
|
|
|
.value("Winding", SkPath::FillType::kWinding_FillType)
|
|
|
|
.value("EvenOdd", SkPath::FillType::kEvenOdd_FillType)
|
|
|
|
.value("InverseWinding", SkPath::FillType::kInverseWinding_FillType)
|
|
|
|
.value("InverseEvenOdd", SkPath::FillType::kInverseEvenOdd_FillType);
|
|
|
|
|
2018-12-03 17:31:04 +00:00
|
|
|
enum_<SkFilterQuality>("FilterQuality")
|
|
|
|
.value("None", SkFilterQuality::kNone_SkFilterQuality)
|
|
|
|
.value("Low", SkFilterQuality::kLow_SkFilterQuality)
|
|
|
|
.value("Medium", SkFilterQuality::kMedium_SkFilterQuality)
|
|
|
|
.value("High", SkFilterQuality::kHigh_SkFilterQuality);
|
|
|
|
|
2018-11-27 18:26:59 +00:00
|
|
|
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);
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
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);
|
|
|
|
|
2019-11-11 15:06:08 +00:00
|
|
|
enum_<SkCanvas::PointMode>("PointMode")
|
|
|
|
.value("Points", SkCanvas::PointMode::kPoints_PointMode)
|
|
|
|
.value("Lines", SkCanvas::PointMode::kLines_PointMode)
|
|
|
|
.value("Polygon", SkCanvas::PointMode::kPolygon_PointMode);
|
|
|
|
|
2018-11-26 16:47:54 +00:00
|
|
|
enum_<SkPaint::Cap>("StrokeCap")
|
|
|
|
.value("Butt", SkPaint::Cap::kButt_Cap)
|
|
|
|
.value("Round", SkPaint::Cap::kRound_Cap)
|
|
|
|
.value("Square", SkPaint::Cap::kSquare_Cap);
|
|
|
|
|
|
|
|
enum_<SkPaint::Join>("StrokeJoin")
|
|
|
|
.value("Miter", SkPaint::Join::kMiter_Join)
|
|
|
|
.value("Round", SkPaint::Join::kRound_Join)
|
|
|
|
.value("Bevel", SkPaint::Join::kBevel_Join);
|
|
|
|
|
2019-01-14 13:36:08 +00:00
|
|
|
enum_<SkTextEncoding>("TextEncoding")
|
|
|
|
.value("UTF8", SkTextEncoding::kUTF8)
|
|
|
|
.value("UTF16", SkTextEncoding::kUTF16)
|
|
|
|
.value("UTF32", SkTextEncoding::kUTF32)
|
|
|
|
.value("GlyphID", SkTextEncoding::kGlyphID);
|
2018-11-26 16:47:54 +00:00
|
|
|
|
2019-04-03 20:51:47 +00:00
|
|
|
enum_<SkTileMode>("TileMode")
|
|
|
|
.value("Clamp", SkTileMode::kClamp)
|
|
|
|
.value("Repeat", SkTileMode::kRepeat)
|
|
|
|
.value("Mirror", SkTileMode::kMirror)
|
|
|
|
.value("Decal", SkTileMode::kDecal);
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
enum_<SkVertices::VertexMode>("VertexMode")
|
|
|
|
.value("Triangles", SkVertices::VertexMode::kTriangles_VertexMode)
|
|
|
|
.value("TrianglesStrip", SkVertices::VertexMode::kTriangleStrip_VertexMode)
|
2018-11-26 16:47:54 +00:00
|
|
|
.value("TriangleFan", SkVertices::VertexMode::kTriangleFan_VertexMode);
|
2018-11-03 11:51:19 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
// A value object is much simpler than a class - it is returned as a JS
|
|
|
|
// object and does not require delete().
|
|
|
|
// https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
|
2019-02-22 15:04:06 +00:00
|
|
|
value_object<ShapedTextOpts>("ShapedTextOpts")
|
|
|
|
.field("font", &ShapedTextOpts::font)
|
|
|
|
.field("leftToRight", &ShapedTextOpts::leftToRight)
|
|
|
|
.field("text", &ShapedTextOpts::text)
|
|
|
|
.field("width", &ShapedTextOpts::width);
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
value_object<SkRect>("SkRect")
|
|
|
|
.field("fLeft", &SkRect::fLeft)
|
|
|
|
.field("fTop", &SkRect::fTop)
|
|
|
|
.field("fRight", &SkRect::fRight)
|
|
|
|
.field("fBottom", &SkRect::fBottom);
|
|
|
|
|
2019-09-03 16:59:06 +00:00
|
|
|
value_object<SimpleRRect>("SkRRect")
|
|
|
|
.field("rect", &SimpleRRect::rect)
|
2019-09-11 18:22:22 +00:00
|
|
|
.field("rx1", &SimpleRRect::rx1)
|
|
|
|
.field("ry1", &SimpleRRect::ry1)
|
|
|
|
.field("rx2", &SimpleRRect::rx2)
|
|
|
|
.field("ry2", &SimpleRRect::ry2)
|
|
|
|
.field("rx3", &SimpleRRect::rx3)
|
|
|
|
.field("ry3", &SimpleRRect::ry3)
|
|
|
|
.field("rx4", &SimpleRRect::rx4)
|
|
|
|
.field("ry4", &SimpleRRect::ry4);
|
2019-09-03 16:59:06 +00:00
|
|
|
|
2018-11-21 14:12:09 +00:00
|
|
|
value_object<SkIRect>("SkIRect")
|
|
|
|
.field("fLeft", &SkIRect::fLeft)
|
|
|
|
.field("fTop", &SkIRect::fTop)
|
|
|
|
.field("fRight", &SkIRect::fRight)
|
|
|
|
.field("fBottom", &SkIRect::fBottom);
|
|
|
|
|
2019-09-11 19:23:00 +00:00
|
|
|
value_object<TonalColors>("TonalColors")
|
|
|
|
.field("ambient", &TonalColors::ambientColor)
|
|
|
|
.field("spot", &TonalColors::spotColor);
|
|
|
|
|
2018-11-30 19:05:58 +00:00
|
|
|
value_object<SimpleImageInfo>("SkImageInfo")
|
|
|
|
.field("width", &SimpleImageInfo::width)
|
|
|
|
.field("height", &SimpleImageInfo::height)
|
|
|
|
.field("colorType", &SimpleImageInfo::colorType)
|
|
|
|
.field("alphaType", &SimpleImageInfo::alphaType);
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
// SkPoints can be represented by [x, y]
|
|
|
|
value_array<SkPoint>("SkPoint")
|
|
|
|
.element(&SkPoint::fX)
|
|
|
|
.element(&SkPoint::fY);
|
|
|
|
|
2018-11-27 18:26:59 +00:00
|
|
|
// SkPoint3s can be represented by [x, y, z]
|
|
|
|
value_array<SkPoint3>("SkPoint3")
|
|
|
|
.element(&SkPoint3::fX)
|
|
|
|
.element(&SkPoint3::fY)
|
|
|
|
.element(&SkPoint3::fZ);
|
|
|
|
|
2019-03-15 19:36:29 +00:00
|
|
|
// PosTan can be represented by [px, py, tx, ty]
|
|
|
|
value_array<PosTan>("PosTan")
|
|
|
|
.element(&PosTan::px)
|
|
|
|
.element(&PosTan::py)
|
|
|
|
.element(&PosTan::tx)
|
|
|
|
.element(&PosTan::ty);
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
// {"w": Number, "h", Number}
|
|
|
|
value_object<SkSize>("SkSize")
|
|
|
|
.field("w", &SkSize::fWidth)
|
|
|
|
.field("h", &SkSize::fHeight);
|
|
|
|
|
|
|
|
value_object<SkISize>("SkISize")
|
|
|
|
.field("w", &SkISize::fWidth)
|
|
|
|
.field("h", &SkISize::fHeight);
|
|
|
|
|
2019-01-14 13:36:08 +00:00
|
|
|
value_object<StrokeOpts>("StrokeOpts")
|
|
|
|
.field("width", &StrokeOpts::width)
|
|
|
|
.field("miter_limit", &StrokeOpts::miter_limit)
|
|
|
|
.field("join", &StrokeOpts::join)
|
|
|
|
.field("cap", &StrokeOpts::cap)
|
|
|
|
.field("precision", &StrokeOpts::precision);
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
// Allows clients to supply a 1D array of 9 elements and the bindings
|
|
|
|
// will automatically turn it into a 3x3 2D matrix.
|
|
|
|
// e.g. path.transform([0,1,2,3,4,5,6,7,8])
|
|
|
|
// This is likely simpler for the client than exposing SkMatrix
|
|
|
|
// directly and requiring them to do a lot of .delete().
|
|
|
|
value_array<SimpleMatrix>("SkMatrix")
|
|
|
|
.element(&SimpleMatrix::scaleX)
|
|
|
|
.element(&SimpleMatrix::skewX)
|
|
|
|
.element(&SimpleMatrix::transX)
|
|
|
|
|
|
|
|
.element(&SimpleMatrix::skewY)
|
|
|
|
.element(&SimpleMatrix::scaleY)
|
|
|
|
.element(&SimpleMatrix::transY)
|
|
|
|
|
|
|
|
.element(&SimpleMatrix::pers0)
|
|
|
|
.element(&SimpleMatrix::pers1)
|
|
|
|
.element(&SimpleMatrix::pers2);
|
|
|
|
|
2019-03-29 14:39:52 +00:00
|
|
|
constant("TRANSPARENT", SK_ColorTRANSPARENT);
|
|
|
|
constant("RED", SK_ColorRED);
|
|
|
|
constant("BLUE", SK_ColorBLUE);
|
|
|
|
constant("YELLOW", SK_ColorYELLOW);
|
|
|
|
constant("CYAN", SK_ColorCYAN);
|
|
|
|
constant("BLACK", SK_ColorBLACK);
|
|
|
|
constant("WHITE", SK_ColorWHITE);
|
2018-11-03 11:51:19 +00:00
|
|
|
// TODO(?)
|
|
|
|
|
2019-01-07 16:08:55 +00:00
|
|
|
constant("MOVE_VERB", MOVE);
|
|
|
|
constant("LINE_VERB", LINE);
|
|
|
|
constant("QUAD_VERB", QUAD);
|
|
|
|
constant("CONIC_VERB", CONIC);
|
|
|
|
constant("CUBIC_VERB", CUBIC);
|
|
|
|
constant("CLOSE_VERB", CLOSE);
|
2019-10-29 14:48:26 +00:00
|
|
|
|
|
|
|
constant("SaveLayerInitWithPrevious", SkCanvas::SaveLayerFlagsSet::kInitWithPrevious_SaveLayerFlag);
|
|
|
|
constant("SaveLayerF16ColorType", SkCanvas::SaveLayerFlagsSet::kF16ColorType);
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|