1 /*
2 * Copyright 2018 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/android/SkAnimatedImage.h"
9 #include "include/codec/SkAndroidCodec.h"
10 #include "include/codec/SkCodec.h"
11 #include "include/codec/SkEncodedImageFormat.h"
12 #include "include/core/SkBBHFactory.h"
13 #include "include/core/SkBlendMode.h"
14 #include "include/core/SkBlender.h"
15 #include "include/core/SkBlurTypes.h"
16 #include "include/core/SkCanvas.h"
17 #include "include/core/SkColor.h"
18 #include "include/core/SkColorFilter.h"
19 #include "include/core/SkColorSpace.h"
20 #include "include/core/SkData.h"
21 #include "include/core/SkImage.h"
22 #include "include/core/SkImageFilter.h"
23 #include "include/core/SkImageGenerator.h"
24 #include "include/core/SkImageInfo.h"
25 #include "include/core/SkM44.h"
26 #include "include/core/SkMaskFilter.h"
27 #include "include/core/SkPaint.h"
28 #include "include/core/SkPath.h"
29 #include "include/core/SkPathEffect.h"
30 #include "include/core/SkPathMeasure.h"
31 #include "include/core/SkPathUtils.h"
32 #include "include/core/SkPicture.h"
33 #include "include/core/SkPictureRecorder.h"
34 #include "include/core/SkPoint3.h"
35 #include "include/core/SkRRect.h"
36 #include "include/core/SkSamplingOptions.h"
37 #include "include/core/SkScalar.h"
38 #include "include/core/SkSerialProcs.h"
39 #include "include/core/SkShader.h"
40 #include "include/core/SkStream.h"
41 #include "include/core/SkString.h"
42 #include "include/core/SkStrokeRec.h"
43 #include "include/core/SkSurface.h"
44 #include "include/core/SkTextBlob.h"
45 #include "include/core/SkTypeface.h"
46 #include "include/core/SkTypes.h"
47 #include "include/core/SkVertices.h"
48 #include "include/effects/Sk1DPathEffect.h"
49 #include "include/effects/Sk2DPathEffect.h"
50 #include "include/effects/SkCornerPathEffect.h"
51 #include "include/effects/SkDashPathEffect.h"
52 #include "include/effects/SkDiscretePathEffect.h"
53 #include "include/effects/SkGradientShader.h"
54 #include "include/effects/SkImageFilters.h"
55 #include "include/effects/SkLumaColorFilter.h"
56 #include "include/effects/SkPerlinNoiseShader.h"
57 #include "include/effects/SkRuntimeEffect.h"
58 #include "include/effects/SkTrimPathEffect.h"
59 #include "include/encode/SkJpegEncoder.h"
60 #include "include/encode/SkPngEncoder.h"
61 #include "include/encode/SkWebpEncoder.h"
62 #include "include/private/base/SkOnce.h"
63 #include "include/utils/SkParsePath.h"
64 #include "include/utils/SkShadowUtils.h"
65 #include "src/base/SkFloatBits.h"
66 #include "src/core/SkPathPriv.h"
67 #include "src/core/SkResourceCache.h"
68 #include "src/image/SkImage_Base.h"
69 #include "src/sksl/SkSLCompiler.h"
70
71 #include "modules/canvaskit/WasmCommon.h"
72 #include <emscripten.h>
73 #include <emscripten/bind.h>
74 #include <emscripten/html5.h>
75
76 #if defined(CK_ENABLE_WEBGL) || defined(CK_ENABLE_WEBGPU)
77 #define ENABLE_GPU
78 #endif
79
80 #ifdef ENABLE_GPU
81 #include "include/gpu/GpuTypes.h"
82 #include "include/gpu/ganesh/GrDirectContext.h"
83 #include "include/gpu/ganesh/GrExternalTextureGenerator.h"
84 #include "include/gpu/ganesh/SkImageGanesh.h"
85 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
86 #include "src/gpu/ganesh/GrCaps.h"
87 #endif // ENABLE_GPU
88
89 #ifdef CK_ENABLE_WEBGL
90 #include "include/gpu/ganesh/GrBackendSurface.h"
91 #include "include/gpu/ganesh/GrTypes.h"
92 #include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
93 #include "include/gpu/ganesh/gl/GrGLDirectContext.h"
94 #include "include/gpu/ganesh/gl/GrGLInterface.h"
95 #include "include/gpu/ganesh/gl/GrGLMakeWebGLInterface.h"
96 #include "include/gpu/ganesh/gl/GrGLTypes.h"
97 #include "src/gpu/RefCntedCallback.h"
98 #include "src/gpu/ganesh/GrProxyProvider.h"
99 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
100 #include "src/gpu/ganesh/gl/GrGLDefines.h"
101
102 #include <GLES2/gl2.h>
103 #endif // CK_ENABLE_WEBGL
104
105 #ifdef CK_ENABLE_WEBGPU
106 #include <emscripten/html5_webgpu.h>
107 #include <webgpu/webgpu.h>
108 #include <webgpu/webgpu_cpp.h>
109 #endif // CK_ENABLE_WEBGPU
110
111 #ifndef CK_NO_FONTS
112 #include "include/core/SkFont.h"
113 #include "include/core/SkFontMetrics.h"
114 #include "include/core/SkFontMgr.h"
115 #include "include/core/SkFontTypes.h"
116 #include "src/ports/SkTypeface_FreeType.h"
117 #ifdef CK_INCLUDE_PARAGRAPH
118 #include "modules/skparagraph/include/Paragraph.h"
119 #endif // CK_INCLUDE_PARAGRAPH
120 #endif // CK_NO_FONTS
121
122 #ifdef CK_INCLUDE_PATHOPS
123 #include "include/pathops/SkPathOps.h"
124 #endif
125
126 #if defined(CK_INCLUDE_RUNTIME_EFFECT)
127 #include "include/sksl/SkSLDebugTrace.h"
128 #endif
129
130 #ifndef CK_NO_FONTS
131 #include "include/ports/SkFontMgr_data.h"
132 #endif
133
134 #if defined(CK_EMBED_FONT)
135 struct SkEmbeddedResource { const uint8_t* data; size_t size; };
136 struct SkEmbeddedResourceHeader { const SkEmbeddedResource* entries; int count; };
137 extern "C" const SkEmbeddedResourceHeader SK_EMBEDDED_FONTS;
138 #endif
139
140 #if defined(GPU_TEST_UTILS)
141 #error "This define should not be set, as it brings in test-only things and bloats codesize."
142 #endif
143
144 #if defined(SK_CODEC_DECODES_BMP)
145 #include "include/codec/SkBmpDecoder.h"
146 #endif
147 #if defined(SK_CODEC_DECODES_GIF)
148 #include "include/codec/SkGifDecoder.h"
149 #endif
150 #if defined(SK_CODEC_DECODES_ICO)
151 #include "include/codec/SkIcoDecoder.h"
152 #endif
153 #if defined(SK_CODEC_DECODES_JPEG)
154 #include "include/codec/SkJpegDecoder.h"
155 #endif
156 #if defined(SK_CODEC_DECODES_PNG)
157 #include "include/codec/SkPngDecoder.h"
158 #endif
159 #if defined(SK_CODEC_DECODES_WBMP)
160 #include "include/codec/SkWbmpDecoder.h"
161 #endif
162 #if defined(SK_CODEC_DECODES_WEBP)
163 #include "include/codec/SkWebpDecoder.h"
164 #endif
165
166 // We'd like clients to be able to compile in as many or few codecs as they want (e.g. codesize)
DecodeImageData(sk_sp<SkData> data)167 std::unique_ptr<SkCodec> DecodeImageData(sk_sp<SkData> data) {
168 if (data == nullptr) {
169 return nullptr;
170 }
171 // These codecs are arbitrarily ordered in alphabetical order.
172 #if defined(SK_CODEC_DECODES_BMP)
173 if (SkBmpDecoder::IsBmp(data->data(), data->size())) {
174 return SkBmpDecoder::Decode(data, nullptr);
175 }
176 #endif
177 #if defined(SK_CODEC_DECODES_GIF)
178 if (SkGifDecoder::IsGif(data->data(), data->size())) {
179 return SkGifDecoder::Decode(data, nullptr);
180 }
181 #endif
182 #if defined(SK_CODEC_DECODES_ICO)
183 if (SkIcoDecoder::IsIco(data->data(), data->size())) {
184 return SkIcoDecoder::Decode(data, nullptr);
185 }
186 #endif
187 #if defined(SK_CODEC_DECODES_JPEG)
188 if (SkJpegDecoder::IsJpeg(data->data(), data->size())) {
189 return SkJpegDecoder::Decode(data, nullptr);
190 }
191 #endif
192 #if defined(SK_CODEC_DECODES_PNG)
193 if (SkPngDecoder::IsPng(data->data(), data->size())) {
194 return SkPngDecoder::Decode(data, nullptr);
195 }
196 #endif
197 #if defined(SK_CODEC_DECODES_WBMP)
198 if (SkWbmpDecoder::IsWbmp(data->data(), data->size())) {
199 return SkWbmpDecoder::Decode(data, nullptr);
200 }
201 #endif
202 #if defined(SK_CODEC_DECODES_WEBP)
203 if (SkWebpDecoder::IsWebp(data->data(), data->size())) {
204 return SkWebpDecoder::Decode(data, nullptr);
205 }
206 #endif
207 return nullptr;
208 }
209
210 struct OptionalMatrix : SkMatrix {
OptionalMatrixOptionalMatrix211 OptionalMatrix(WASMPointerF32 mPtr) {
212 if (mPtr) {
213 const SkScalar* nineMatrixValues = reinterpret_cast<const SkScalar*>(mPtr);
214 this->set9(nineMatrixValues);
215 }
216 }
217 };
218
ptrToSkColor4f(WASMPointerF32 cPtr)219 SkColor4f ptrToSkColor4f(WASMPointerF32 cPtr) {
220 float* fourFloats = reinterpret_cast<float*>(cPtr);
221 SkColor4f color;
222 memcpy(&color, fourFloats, 4 * sizeof(float));
223 return color;
224 }
225
ptrToSkRRect(WASMPointerF32 fPtr)226 SkRRect ptrToSkRRect(WASMPointerF32 fPtr) {
227 // In order, these floats should be 4 floats for the rectangle
228 // (left, top, right, bottom) and then 8 floats for the radii
229 // (upper left, upper right, lower right, lower left).
230 const SkScalar* twelveFloats = reinterpret_cast<const SkScalar*>(fPtr);
231 const SkRect rect = reinterpret_cast<const SkRect*>(twelveFloats)[0];
232 const SkVector* radiiValues = reinterpret_cast<const SkVector*>(twelveFloats + 4);
233
234 SkRRect rr;
235 rr.setRectRadii(rect, radiiValues);
236 return rr;
237 }
238
239 // Surface creation structs and helpers
240 struct SimpleImageInfo {
241 int width;
242 int height;
243 SkColorType colorType;
244 SkAlphaType alphaType;
245 sk_sp<SkColorSpace> colorSpace;
246 };
247
toSkImageInfo(const SimpleImageInfo & sii)248 SkImageInfo toSkImageInfo(const SimpleImageInfo& sii) {
249 return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType,
250 sii.colorSpace ? sii.colorSpace : SkColorSpace::MakeSRGB());
251 }
252
253 #ifdef CK_ENABLE_WEBGL
254
255 // Set the pixel format based on the colortype.
256 // These degrees of freedom are removed from canvaskit only to keep the interface simpler.
257 struct ColorSettings {
ColorSettingsColorSettings258 ColorSettings(sk_sp<SkColorSpace> colorSpace) {
259 if (colorSpace == nullptr || colorSpace->isSRGB()) {
260 colorType = kRGBA_8888_SkColorType;
261 pixFormat = GR_GL_RGBA8;
262 } else {
263 colorType = kRGBA_F16_SkColorType;
264 pixFormat = GR_GL_RGBA16F;
265 }
266 }
267 SkColorType colorType;
268 GrGLenum pixFormat;
269 };
270
MakeGrContext()271 sk_sp<GrDirectContext> MakeGrContext() {
272 // We assume that any calls we make to GL for the remainder of this function will go to the
273 // desired WebGL Context.
274 // setup interface.
275 auto interface = GrGLInterfaces::MakeWebGL();
276 // setup context
277 return GrDirectContexts::MakeGL(interface);
278 }
279
MakeOnScreenGLSurface(sk_sp<GrDirectContext> dContext,int width,int height,sk_sp<SkColorSpace> colorSpace,int sampleCnt,int stencil)280 sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrDirectContext> dContext, int width, int height,
281 sk_sp<SkColorSpace> colorSpace, int sampleCnt, int stencil) {
282 // WebGL should already be clearing the color and stencil buffers, but do it again here to
283 // ensure Skia receives them in the expected state.
284 glBindFramebuffer(GL_FRAMEBUFFER, 0);
285 glClearColor(0, 0, 0, 0);
286 glClearStencil(0);
287 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
288 dContext->resetContext(kRenderTarget_GrGLBackendState | kMisc_GrGLBackendState);
289
290 // The on-screen canvas is FBO 0. Wrap it in a Skia render target so Skia can render to it.
291 GrGLFramebufferInfo info;
292 info.fFBOID = 0;
293
294 if (!colorSpace) {
295 colorSpace = SkColorSpace::MakeSRGB();
296 }
297
298 const auto colorSettings = ColorSettings(colorSpace);
299 info.fFormat = colorSettings.pixFormat;
300 auto target = GrBackendRenderTargets::MakeGL(width, height, sampleCnt, stencil, info);
301 sk_sp<SkSurface> surface(SkSurfaces::WrapBackendRenderTarget(dContext.get(),
302 target,
303 kBottomLeft_GrSurfaceOrigin,
304 colorSettings.colorType,
305 colorSpace,
306 nullptr));
307 return surface;
308 }
309
MakeOnScreenGLSurface(sk_sp<GrDirectContext> dContext,int width,int height,sk_sp<SkColorSpace> colorSpace)310 sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrDirectContext> dContext, int width, int height,
311 sk_sp<SkColorSpace> colorSpace) {
312 GrGLint sampleCnt;
313 glGetIntegerv(GL_SAMPLES, &sampleCnt);
314
315 GrGLint stencil;
316 glGetIntegerv(GL_STENCIL_BITS, &stencil);
317
318 return MakeOnScreenGLSurface(dContext, width, height, colorSpace, sampleCnt, stencil);
319 }
320
MakeRenderTarget(sk_sp<GrDirectContext> dContext,int width,int height)321 sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrDirectContext> dContext, int width, int height) {
322 SkImageInfo info = SkImageInfo::MakeN32(
323 width, height, SkAlphaType::kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
324
325 sk_sp<SkSurface> surface(SkSurfaces::RenderTarget(dContext.get(),
326 skgpu::Budgeted::kYes,
327 info,
328 0,
329 kBottomLeft_GrSurfaceOrigin,
330 nullptr,
331 true));
332 return surface;
333 }
334
MakeRenderTarget(sk_sp<GrDirectContext> dContext,SimpleImageInfo sii)335 sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrDirectContext> dContext, SimpleImageInfo sii) {
336 sk_sp<SkSurface> surface(SkSurfaces::RenderTarget(dContext.get(),
337 skgpu::Budgeted::kYes,
338 toSkImageInfo(sii),
339 0,
340 kBottomLeft_GrSurfaceOrigin,
341 nullptr,
342 true));
343 return surface;
344 }
345 #endif // CK_ENABLE_WEBGL
346
347 #ifdef CK_ENABLE_WEBGPU
348
MakeGrContext()349 sk_sp<GrDirectContext> MakeGrContext() {
350 GrContextOptions options;
351 wgpu::Device device = wgpu::Device::Acquire(emscripten_webgpu_get_device());
352 return GrDirectContext::MakeDawn(device, options);
353 }
354
MakeGPUTextureSurface(sk_sp<GrDirectContext> dContext,uint32_t textureHandle,uint32_t textureFormat,int width,int height,sk_sp<SkColorSpace> colorSpace)355 sk_sp<SkSurface> MakeGPUTextureSurface(sk_sp<GrDirectContext> dContext,
356 uint32_t textureHandle, uint32_t textureFormat,
357 int width, int height, sk_sp<SkColorSpace> colorSpace) {
358 if (!colorSpace) {
359 colorSpace = SkColorSpace::MakeSRGB();
360 }
361
362 wgpu::TextureFormat format = static_cast<wgpu::TextureFormat>(textureFormat);
363 wgpu::Texture texture(emscripten_webgpu_import_texture(textureHandle));
364 emscripten_webgpu_release_js_handle(textureHandle);
365
366 // GrDawnRenderTargetInfo currently only supports a 1-mip TextureView.
367 constexpr uint32_t mipLevelCount = 1;
368 constexpr uint32_t sampleCount = 1;
369
370 GrDawnTextureInfo info;
371 info.fTexture = texture;
372 info.fFormat = format;
373 info.fLevelCount = mipLevelCount;
374
375 GrBackendTexture target(width, height, info);
376 return SkSurfaces::WrapBackendTexture(
377 dContext.get(),
378 target,
379 kTopLeft_GrSurfaceOrigin,
380 sampleCount,
381 colorSpace->isSRGB() ? kRGBA_8888_SkColorType : kRGBA_F16_SkColorType,
382 colorSpace,
383 nullptr);
384 }
385
ReplaceBackendTexture(SkSurface & surface,uint32_t textureHandle,uint32_t textureFormat,int width,int height)386 bool ReplaceBackendTexture(SkSurface& surface, uint32_t textureHandle, uint32_t textureFormat,
387 int width, int height) {
388 wgpu::TextureFormat format = static_cast<wgpu::TextureFormat>(textureFormat);
389 wgpu::Texture texture(emscripten_webgpu_import_texture(textureHandle));
390 emscripten_webgpu_release_js_handle(textureHandle);
391
392 GrDawnTextureInfo info;
393 info.fTexture = texture;
394 info.fFormat = format;
395 info.fLevelCount = 1;
396
397 // Use kDiscard_ContentChangeMode to discard the contents of the old backing texture. This not
398 // only avoids an unnecessary blit, we also don't support copying the contents of a swapchain
399 // texture due to the default GPUCanvasConfiguration usage bits we used when configuring the
400 // GPUCanvasContext in JS.
401 //
402 // The default usage bits only contain GPUTextureUsage.RENDER_ATTACHMENT. To support a copy we
403 // would need to also set GPUTextureUsage.TEXTURE_BINDING (to sample it in a shader) or
404 // GPUTextureUsage.COPY_SRC (for a copy command).
405 //
406 // See https://www.w3.org/TR/webgpu/#namespacedef-gputextureusage and
407 // https://www.w3.org/TR/webgpu/#dictdef-gpucanvasconfiguration.
408 GrBackendTexture target(width, height, info);
409 return surface.replaceBackendTexture(target, kTopLeft_GrSurfaceOrigin,
410 SkSurface::kDiscard_ContentChangeMode);
411 }
412
413 #endif // CK_ENABLE_WEBGPU
414
415 //========================================================================================
416 // Path things
417 //========================================================================================
418
419 // All these Apply* methods are simple wrappers to avoid returning an object.
420 // The default WASM bindings produce code that will leak if a return value
421 // isn't assigned to a JS variable and has delete() called on it.
422 // These Apply methods, combined with the smarter binding code allow for chainable
423 // commands that don't leak if the return value is ignored (i.e. when used intuitively).
ApplyAddPath(SkPath & orig,const SkPath & newPath,SkScalar scaleX,SkScalar skewX,SkScalar transX,SkScalar skewY,SkScalar scaleY,SkScalar transY,SkScalar pers0,SkScalar pers1,SkScalar pers2,bool extendPath)424 void ApplyAddPath(SkPath& orig, const SkPath& newPath,
425 SkScalar scaleX, SkScalar skewX, SkScalar transX,
426 SkScalar skewY, SkScalar scaleY, SkScalar transY,
427 SkScalar pers0, SkScalar pers1, SkScalar pers2,
428 bool extendPath) {
429 SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
430 skewY , scaleY, transY,
431 pers0 , pers1 , pers2);
432 orig.addPath(newPath, m, extendPath ? SkPath::kExtend_AddPathMode :
433 SkPath::kAppend_AddPathMode);
434 }
435
ApplyArcToTangent(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar radius)436 void ApplyArcToTangent(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
437 SkScalar radius) {
438 p.arcTo(x1, y1, x2, y2, radius);
439 }
440
ApplyArcToArcSize(SkPath & orig,SkScalar rx,SkScalar ry,SkScalar xAxisRotate,bool useSmallArc,bool ccw,SkScalar x,SkScalar y)441 void ApplyArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
442 bool useSmallArc, bool ccw, SkScalar x, SkScalar y) {
443 auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize;
444 auto sweep = ccw ? SkPathDirection::kCCW : SkPathDirection::kCW;
445 orig.arcTo(rx, ry, xAxisRotate, arcSize, sweep, x, y);
446 }
447
ApplyRArcToArcSize(SkPath & orig,SkScalar rx,SkScalar ry,SkScalar xAxisRotate,bool useSmallArc,bool ccw,SkScalar dx,SkScalar dy)448 void ApplyRArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
449 bool useSmallArc, bool ccw, SkScalar dx, SkScalar dy) {
450 auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize;
451 auto sweep = ccw ? SkPathDirection::kCCW : SkPathDirection::kCW;
452 orig.rArcTo(rx, ry, xAxisRotate, arcSize, sweep, dx, dy);
453 }
454
ApplyClose(SkPath & p)455 void ApplyClose(SkPath& p) {
456 p.close();
457 }
458
ApplyConicTo(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar w)459 void ApplyConicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
460 SkScalar w) {
461 p.conicTo(x1, y1, x2, y2, w);
462 }
463
ApplyRConicTo(SkPath & p,SkScalar dx1,SkScalar dy1,SkScalar dx2,SkScalar dy2,SkScalar w)464 void ApplyRConicTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
465 SkScalar w) {
466 p.rConicTo(dx1, dy1, dx2, dy2, w);
467 }
468
ApplyCubicTo(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)469 void ApplyCubicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
470 SkScalar x3, SkScalar y3) {
471 p.cubicTo(x1, y1, x2, y2, x3, y3);
472 }
473
ApplyRCubicTo(SkPath & p,SkScalar dx1,SkScalar dy1,SkScalar dx2,SkScalar dy2,SkScalar dx3,SkScalar dy3)474 void ApplyRCubicTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
475 SkScalar dx3, SkScalar dy3) {
476 p.rCubicTo(dx1, dy1, dx2, dy2, dx3, dy3);
477 }
478
ApplyLineTo(SkPath & p,SkScalar x,SkScalar y)479 void ApplyLineTo(SkPath& p, SkScalar x, SkScalar y) {
480 p.lineTo(x, y);
481 }
482
ApplyRLineTo(SkPath & p,SkScalar dx,SkScalar dy)483 void ApplyRLineTo(SkPath& p, SkScalar dx, SkScalar dy) {
484 p.rLineTo(dx, dy);
485 }
486
ApplyMoveTo(SkPath & p,SkScalar x,SkScalar y)487 void ApplyMoveTo(SkPath& p, SkScalar x, SkScalar y) {
488 p.moveTo(x, y);
489 }
490
ApplyRMoveTo(SkPath & p,SkScalar dx,SkScalar dy)491 void ApplyRMoveTo(SkPath& p, SkScalar dx, SkScalar dy) {
492 p.rMoveTo(dx, dy);
493 }
494
ApplyReset(SkPath & p)495 void ApplyReset(SkPath& p) {
496 p.reset();
497 }
498
ApplyRewind(SkPath & p)499 void ApplyRewind(SkPath& p) {
500 p.rewind();
501 }
502
ApplyQuadTo(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)503 void ApplyQuadTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
504 p.quadTo(x1, y1, x2, y2);
505 }
506
ApplyRQuadTo(SkPath & p,SkScalar dx1,SkScalar dy1,SkScalar dx2,SkScalar dy2)507 void ApplyRQuadTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2) {
508 p.rQuadTo(dx1, dy1, dx2, dy2);
509 }
510
ApplyTransform(SkPath & orig,SkScalar scaleX,SkScalar skewX,SkScalar transX,SkScalar skewY,SkScalar scaleY,SkScalar transY,SkScalar pers0,SkScalar pers1,SkScalar pers2)511 void ApplyTransform(SkPath& orig,
512 SkScalar scaleX, SkScalar skewX, SkScalar transX,
513 SkScalar skewY, SkScalar scaleY, SkScalar transY,
514 SkScalar pers0, SkScalar pers1, SkScalar pers2) {
515 SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
516 skewY , scaleY, transY,
517 pers0 , pers1 , pers2);
518 orig.transform(m);
519 }
520
521 #ifdef CK_INCLUDE_PATHOPS
ApplySimplify(SkPath & path)522 bool ApplySimplify(SkPath& path) {
523 return Simplify(path, &path);
524 }
525
ApplyPathOp(SkPath & pathOne,const SkPath & pathTwo,SkPathOp op)526 bool ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
527 return Op(pathOne, pathTwo, op, &pathOne);
528 }
529
MakePathFromOp(const SkPath & pathOne,const SkPath & pathTwo,SkPathOp op)530 SkPathOrNull MakePathFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
531 SkPath out;
532 if (Op(pathOne, pathTwo, op, &out)) {
533 return emscripten::val(out);
534 }
535 return emscripten::val::null();
536 }
537
MakeAsWinding(const SkPath & self)538 SkPathOrNull MakeAsWinding(const SkPath& self) {
539 SkPath out;
540 if (AsWinding(self, &out)) {
541 return emscripten::val(out);
542 }
543 return emscripten::val::null();
544 }
545 #endif
546
ToSVGString(const SkPath & path)547 JSString ToSVGString(const SkPath& path) {
548 return emscripten::val(SkParsePath::ToSVGString(path).c_str());
549 }
550
MakePathFromSVGString(std::string str)551 SkPathOrNull MakePathFromSVGString(std::string str) {
552 SkPath path;
553 if (SkParsePath::FromSVGString(str.c_str(), &path)) {
554 return emscripten::val(path);
555 }
556 return emscripten::val::null();
557 }
558
CanInterpolate(const SkPath & path1,const SkPath & path2)559 bool CanInterpolate(const SkPath& path1, const SkPath& path2) {
560 return path1.isInterpolatable(path2);
561 }
562
MakePathFromInterpolation(const SkPath & path1,const SkPath & path2,SkScalar weight)563 SkPathOrNull MakePathFromInterpolation(const SkPath& path1, const SkPath& path2, SkScalar weight) {
564 SkPath out;
565 bool succeed = path1.interpolate(path2, weight, &out);
566 if (succeed) {
567 return emscripten::val(out);
568 }
569 return emscripten::val::null();
570 }
571
CopyPath(const SkPath & a)572 SkPath CopyPath(const SkPath& a) {
573 SkPath copy(a);
574 return copy;
575 }
576
Equals(const SkPath & a,const SkPath & b)577 bool Equals(const SkPath& a, const SkPath& b) {
578 return a == b;
579 }
580
581 // =================================================================================
582 // Creating/Exporting Paths with cmd arrays
583 // =================================================================================
584
585 static const int MOVE = 0;
586 static const int LINE = 1;
587 static const int QUAD = 2;
588 static const int CONIC = 3;
589 static const int CUBIC = 4;
590 static const int CLOSE = 5;
591
ToCmds(const SkPath & path)592 Float32Array ToCmds(const SkPath& path) {
593 std::vector<SkScalar> cmds;
594 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
595 switch (verb) {
596 case SkPathVerb::kMove:
597 cmds.insert(cmds.end(), {MOVE, pts[0].x(), pts[0].y()});
598 break;
599 case SkPathVerb::kLine:
600 cmds.insert(cmds.end(), {LINE, pts[1].x(), pts[1].y()});
601 break;
602 case SkPathVerb::kQuad:
603 cmds.insert(cmds.end(), {QUAD, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y()});
604 break;
605 case SkPathVerb::kConic:
606 cmds.insert(cmds.end(), {CONIC,
607 pts[1].x(), pts[1].y(),
608 pts[2].x(), pts[2].y(), *w});
609 break;
610 case SkPathVerb::kCubic:
611 cmds.insert(cmds.end(), {CUBIC,
612 pts[1].x(), pts[1].y(),
613 pts[2].x(), pts[2].y(),
614 pts[3].x(), pts[3].y()});
615 break;
616 case SkPathVerb::kClose:
617 cmds.push_back(CLOSE);
618 break;
619 }
620 }
621 return MakeTypedArray(cmds.size(), (const float*)cmds.data());
622 }
623
MakePathFromCmds(WASMPointerF32 cptr,int numCmds)624 SkPathOrNull MakePathFromCmds(WASMPointerF32 cptr, int numCmds) {
625 const auto* cmds = reinterpret_cast<const float*>(cptr);
626 SkPath path;
627 float x1, y1, x2, y2, x3, y3;
628
629 // if there are not enough arguments, bail with the path we've constructed so far.
630 #define CHECK_NUM_ARGS(n) \
631 if ((i + n) > numCmds) { \
632 SkDebugf("Not enough args to match the verbs. Saw %d commands\n", numCmds); \
633 return emscripten::val::null(); \
634 }
635
636 for(int i = 0; i < numCmds;){
637 switch (sk_float_floor2int(cmds[i++])) {
638 case MOVE:
639 CHECK_NUM_ARGS(2)
640 x1 = cmds[i++]; y1 = cmds[i++];
641 path.moveTo(x1, y1);
642 break;
643 case LINE:
644 CHECK_NUM_ARGS(2)
645 x1 = cmds[i++]; y1 = cmds[i++];
646 path.lineTo(x1, y1);
647 break;
648 case QUAD:
649 CHECK_NUM_ARGS(4)
650 x1 = cmds[i++]; y1 = cmds[i++];
651 x2 = cmds[i++]; y2 = cmds[i++];
652 path.quadTo(x1, y1, x2, y2);
653 break;
654 case CONIC:
655 CHECK_NUM_ARGS(5)
656 x1 = cmds[i++]; y1 = cmds[i++];
657 x2 = cmds[i++]; y2 = cmds[i++];
658 x3 = cmds[i++]; // weight
659 path.conicTo(x1, y1, x2, y2, x3);
660 break;
661 case CUBIC:
662 CHECK_NUM_ARGS(6)
663 x1 = cmds[i++]; y1 = cmds[i++];
664 x2 = cmds[i++]; y2 = cmds[i++];
665 x3 = cmds[i++]; y3 = cmds[i++];
666 path.cubicTo(x1, y1, x2, y2, x3, y3);
667 break;
668 case CLOSE:
669 path.close();
670 break;
671 default:
672 SkDebugf(" path: UNKNOWN command %f, aborting dump...\n", cmds[i-1]);
673 return emscripten::val::null();
674 }
675 }
676
677 #undef CHECK_NUM_ARGS
678
679 return emscripten::val(path);
680 }
681
PathAddVerbsPointsWeights(SkPath & path,WASMPointerU8 verbsPtr,int numVerbs,WASMPointerF32 ptsPtr,int numPts,WASMPointerF32 wtsPtr,int numWts)682 void PathAddVerbsPointsWeights(SkPath& path, WASMPointerU8 verbsPtr, int numVerbs,
683 WASMPointerF32 ptsPtr, int numPts,
684 WASMPointerF32 wtsPtr, int numWts) {
685 const uint8_t* verbs = reinterpret_cast<const uint8_t*>(verbsPtr);
686 const float* pts = reinterpret_cast<const float*>(ptsPtr);
687 const float* weights = reinterpret_cast<const float*>(wtsPtr);
688
689 #define CHECK_NUM_POINTS(n) \
690 if ((ptIdx + n) > numPts) { \
691 SkDebugf("Not enough points to match the verbs. Saw %d points\n", numPts); \
692 return; \
693 }
694 #define CHECK_NUM_WEIGHTS(n) \
695 if ((wtIdx + n) > numWts) { \
696 SkDebugf("Not enough weights to match the verbs. Saw %d weights\n", numWts); \
697 return; \
698 }
699
700 path.incReserve(numPts);
701 int ptIdx = 0;
702 int wtIdx = 0;
703 for (int v = 0; v < numVerbs; ++v) {
704 switch (verbs[v]) {
705 case MOVE:
706 CHECK_NUM_POINTS(2)
707 path.moveTo(pts[ptIdx], pts[ptIdx+1]);
708 ptIdx += 2;
709 break;
710 case LINE:
711 CHECK_NUM_POINTS(2)
712 path.lineTo(pts[ptIdx], pts[ptIdx+1]);
713 ptIdx += 2;
714 break;
715 case QUAD:
716 CHECK_NUM_POINTS(4)
717 path.quadTo(pts[ptIdx], pts[ptIdx+1], pts[ptIdx+2], pts[ptIdx+3]);
718 ptIdx += 4;
719 break;
720 case CONIC:
721 CHECK_NUM_POINTS(4)
722 CHECK_NUM_WEIGHTS(1)
723 path.conicTo(pts[ptIdx], pts[ptIdx+1], pts[ptIdx+2], pts[ptIdx+3],
724 weights[wtIdx]);
725 ptIdx += 4;
726 wtIdx++;
727 break;
728 case CUBIC:
729 CHECK_NUM_POINTS(6)
730 path.cubicTo(pts[ptIdx ], pts[ptIdx+1],
731 pts[ptIdx+2], pts[ptIdx+3],
732 pts[ptIdx+4], pts[ptIdx+5]);
733 ptIdx += 6;
734 break;
735 case CLOSE:
736 path.close();
737 break;
738 }
739 }
740 #undef CHECK_NUM_POINTS
741 #undef CHECK_NUM_WEIGHTS
742 }
743
MakePathFromVerbsPointsWeights(WASMPointerU8 verbsPtr,int numVerbs,WASMPointerF32 ptsPtr,int numPts,WASMPointerF32 wtsPtr,int numWts)744 SkPath MakePathFromVerbsPointsWeights(WASMPointerU8 verbsPtr, int numVerbs,
745 WASMPointerF32 ptsPtr, int numPts,
746 WASMPointerF32 wtsPtr, int numWts) {
747 SkPath path;
748 PathAddVerbsPointsWeights(path, verbsPtr, numVerbs, ptsPtr, numPts, wtsPtr, numWts);
749 return path;
750 }
751
752 //========================================================================================
753 // Path Effects
754 //========================================================================================
755
ApplyDash(SkPath & path,SkScalar on,SkScalar off,SkScalar phase)756 bool ApplyDash(SkPath& path, SkScalar on, SkScalar off, SkScalar phase) {
757 SkScalar intervals[] = { on, off };
758 auto pe = SkDashPathEffect::Make(intervals, 2, phase);
759 if (!pe) {
760 SkDebugf("Invalid args to dash()\n");
761 return false;
762 }
763 SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
764 if (pe->filterPath(&path, path, &rec, nullptr)) {
765 return true;
766 }
767 SkDebugf("Could not make dashed path\n");
768 return false;
769 }
770
ApplyTrim(SkPath & path,SkScalar startT,SkScalar stopT,bool isComplement)771 bool ApplyTrim(SkPath& path, SkScalar startT, SkScalar stopT, bool isComplement) {
772 auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal;
773 auto pe = SkTrimPathEffect::Make(startT, stopT, mode);
774 if (!pe) {
775 SkDebugf("Invalid args to trim(): startT and stopT must be in [0,1]\n");
776 return false;
777 }
778 SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
779 if (pe->filterPath(&path, path, &rec, nullptr)) {
780 return true;
781 }
782 SkDebugf("Could not trim path\n");
783 return false;
784 }
785
786 struct StrokeOpts {
787 // Default values are set in interface.js which allows clients
788 // to set any number of them. Otherwise, the binding code complains if
789 // any are omitted.
790 SkScalar width;
791 SkScalar miter_limit;
792 SkPaint::Join join;
793 SkPaint::Cap cap;
794 float precision;
795 };
796
ApplyStroke(SkPath & path,StrokeOpts opts)797 bool ApplyStroke(SkPath& path, StrokeOpts opts) {
798 SkPaint p;
799 p.setStyle(SkPaint::kStroke_Style);
800 p.setStrokeCap(opts.cap);
801 p.setStrokeJoin(opts.join);
802 p.setStrokeWidth(opts.width);
803 p.setStrokeMiter(opts.miter_limit);
804
805 return skpathutils::FillPathWithPaint(path, p, &path, nullptr, opts.precision);
806 }
807
808 // This function is private, we call it in interface.js
computeTonalColors(WASMPointerF32 cPtrAmbi,WASMPointerF32 cPtrSpot)809 void computeTonalColors(WASMPointerF32 cPtrAmbi, WASMPointerF32 cPtrSpot) {
810 // private methods accepting colors take pointers to floats already copied into wasm memory.
811 float* ambiFloats = reinterpret_cast<float*>(cPtrAmbi);
812 float* spotFloats = reinterpret_cast<float*>(cPtrSpot);
813 SkColor4f ambiColor = { ambiFloats[0], ambiFloats[1], ambiFloats[2], ambiFloats[3]};
814 SkColor4f spotColor = { spotFloats[0], spotFloats[1], spotFloats[2], spotFloats[3]};
815
816 // This function takes SkColor
817 SkColor resultAmbi, resultSpot;
818 SkShadowUtils::ComputeTonalColors(
819 ambiColor.toSkColor(), spotColor.toSkColor(),
820 &resultAmbi, &resultSpot);
821
822 // Convert back to color4f
823 const SkColor4f ambi4f = SkColor4f::FromColor(resultAmbi);
824 const SkColor4f spot4f = SkColor4f::FromColor(resultSpot);
825
826 // Re-use the caller's allocated memory to hold the result.
827 memcpy(ambiFloats, ambi4f.vec(), 4 * sizeof(SkScalar));
828 memcpy(spotFloats, spot4f.vec(), 4 * sizeof(SkScalar));
829 }
830
831 #ifdef CK_INCLUDE_RUNTIME_EFFECT
832 struct RuntimeEffectUniform {
833 int columns;
834 int rows;
835 int slot; // the index into the uniforms array that this uniform begins.
836 bool isInteger;
837 };
838
fromUniform(const SkRuntimeEffect::Uniform & u)839 RuntimeEffectUniform fromUniform(const SkRuntimeEffect::Uniform& u) {
840 RuntimeEffectUniform su;
841 su.rows = u.count; // arrayLength
842 su.columns = 1;
843 su.isInteger = false;
844 using Type = SkRuntimeEffect::Uniform::Type;
845 switch (u.type) {
846 case Type::kFloat: break;
847 case Type::kFloat2: su.columns = 2; break;
848 case Type::kFloat3: su.columns = 3; break;
849 case Type::kFloat4: su.columns = 4; break;
850 case Type::kFloat2x2: su.columns = 2; su.rows *= 2; break;
851 case Type::kFloat3x3: su.columns = 3; su.rows *= 3; break;
852 case Type::kFloat4x4: su.columns = 4; su.rows *= 4; break;
853 case Type::kInt: su.isInteger = true; break;
854 case Type::kInt2: su.columns = 2; su.isInteger = true; break;
855 case Type::kInt3: su.columns = 3; su.isInteger = true; break;
856 case Type::kInt4: su.columns = 4; su.isInteger = true; break;
857 }
858 su.slot = u.offset / sizeof(float);
859 return su;
860 }
861
castUniforms(void * data,size_t dataLen,const SkRuntimeEffect & effect)862 void castUniforms(void* data, size_t dataLen, const SkRuntimeEffect& effect) {
863 if (dataLen != effect.uniformSize()) {
864 // Incorrect number of uniforms. Our code below could read/write off the end of the buffer.
865 // However, shader creation is going to fail anyway, so just do nothing.
866 return;
867 }
868
869 float* fltData = reinterpret_cast<float*>(data);
870 for (const auto& u : effect.uniforms()) {
871 RuntimeEffectUniform reu = fromUniform(u);
872 if (reu.isInteger) {
873 // The SkSL is expecting integers in the uniform data
874 for (int i = 0; i < reu.columns * reu.rows; ++i) {
875 int numAsInt = static_cast<int>(fltData[reu.slot + i]);
876 fltData[reu.slot + i] = SkBits2Float(numAsInt);
877 }
878 }
879 }
880 }
881 #endif
882
alwaysSaveTypefaceBytes(SkTypeface * face,void *)883 sk_sp<SkData> alwaysSaveTypefaceBytes(SkTypeface* face, void*) {
884 return face->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
885 }
886
887 // These objects have private destructors / delete methods - I don't think
888 // we need to do anything other than tell emscripten to do nothing.
889 namespace emscripten {
890 namespace internal {
891 template<typename ClassType>
892 void raw_destructor(ClassType*);
893
894 template<>
raw_destructor(SkContourMeasure * ptr)895 void raw_destructor<SkContourMeasure>(SkContourMeasure* ptr) {
896 }
897
898 template<>
raw_destructor(SkVertices * ptr)899 void raw_destructor<SkVertices>(SkVertices* ptr) {
900 }
901
902 #ifndef CK_NO_FONTS
903 template<>
raw_destructor(SkTextBlob * ptr)904 void raw_destructor<SkTextBlob>(SkTextBlob* ptr) {
905 }
906
907 template<>
raw_destructor(SkTypeface * ptr)908 void raw_destructor<SkTypeface>(SkTypeface* ptr) {
909 }
910 #endif
911 }
912 }
913
914 // toBytes returns a Uint8Array that has a copy of the data in the given SkData.
toBytes(sk_sp<SkData> data)915 Uint8Array toBytes(sk_sp<SkData> data) {
916 // By making the copy using the JS transliteration, we don't risk the SkData object being
917 // cleaned up before we make the copy.
918 return emscripten::val(
919 // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#memory-views
920 typed_memory_view(data->size(), data->bytes())
921 ).call<Uint8Array>("slice"); // slice with no args makes a copy of the memory view.
922 }
923
924 #ifdef CK_ENABLE_WEBGL
925 // We need to call into the JS side of things to free webGL contexts. This object will be called
926 // with _setTextureCleanup after CanvasKit loads. The object will have one attribute,
927 // a function called deleteTexture that takes two ints.
928 JSObject textureCleanup = emscripten::val::null();
929
930 struct TextureReleaseContext {
931 // This refers to which webgl context, i.e. which surface, owns the texture. We need this
932 // to route the deleteTexture to the right context.
933 uint32_t webglHandle;
934 // This refers to the index of the texture in the complete list of textures.
935 uint32_t texHandle;
936 };
937
deleteJSTexture(SkImages::ReleaseContext rc)938 void deleteJSTexture(SkImages::ReleaseContext rc) {
939 auto ctx = reinterpret_cast<TextureReleaseContext*>(rc);
940 textureCleanup.call<void>("deleteTexture", ctx->webglHandle, ctx->texHandle);
941 delete ctx;
942 }
943
944 class ExternalWebGLTexture : public GrExternalTexture {
945 public:
ExternalWebGLTexture(GrBackendTexture backendTexture,uint32_t textureHandle,EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)946 ExternalWebGLTexture(GrBackendTexture backendTexture, uint32_t textureHandle, EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context) :
947 fBackendTexture(backendTexture), fWebglHandle(context), fTextureHandle(textureHandle) {}
948
getBackendTexture()949 GrBackendTexture getBackendTexture() override {
950 return fBackendTexture;
951 }
952
dispose()953 void dispose() override {
954 textureCleanup.call<void>("deleteTexture", fWebglHandle, fTextureHandle);
955 }
956 private:
957 GrBackendTexture fBackendTexture;
958
959 // This refers to which webgl context, i.e. which surface, owns the texture. We need this
960 // to route the deleteTexture to the right context.
961 uint32_t fWebglHandle;
962 // This refers to the index of the texture in the complete list of textures.
963 uint32_t fTextureHandle;
964 };
965
966 class WebGLTextureImageGenerator : public GrExternalTextureGenerator {
967 public:
WebGLTextureImageGenerator(SkImageInfo ii,JSObject callbackObj)968 WebGLTextureImageGenerator(SkImageInfo ii, JSObject callbackObj):
969 GrExternalTextureGenerator(ii),
970 fCallback(callbackObj) {}
971
~WebGLTextureImageGenerator()972 ~WebGLTextureImageGenerator() override {
973 // This cleans up the associated TextureSource that is used to make the texture
974 // (i.e. "makeTexture" below). We expect this destructor to be called when the
975 // SkImage that this Generator belongs to is destroyed.
976 fCallback.call<void>("freeSrc");
977 }
978
generateExternalTexture(GrRecordingContext * ctx,skgpu::Mipmapped)979 std::unique_ptr<GrExternalTexture> generateExternalTexture(GrRecordingContext* ctx,
980 skgpu::Mipmapped) override {
981 GrGLTextureInfo glInfo;
982
983 // This callback is defined in webgl.js
984 glInfo.fID = fCallback.call<uint32_t>("makeTexture");
985
986 // The format and target should match how we make the texture on the JS side
987 // See the implementation of the makeTexture function.
988 glInfo.fFormat = GR_GL_RGBA8;
989 glInfo.fTarget = GR_GL_TEXTURE_2D;
990
991 // These textures are unlikely to actually have mipmaps generated (we might even be on
992 // WebGL 1, where Skia doesn't support mipmapping at all). Therefore, we ignore any request
993 // for mipmapping here. See: b/338095525
994 auto backendTexture = GrBackendTextures::MakeGL(
995 fInfo.width(), fInfo.height(), skgpu::Mipmapped::kNo, glInfo);
996
997 // In order to bind the image source to the texture, makeTexture has changed which
998 // texture is "in focus" for the WebGL context.
999 GrAsDirectContext(ctx)->resetContext(kTextureBinding_GrGLBackendState);
1000 return std::make_unique<ExternalWebGLTexture>(
1001 backendTexture, glInfo.fID, emscripten_webgl_get_current_context());
1002 }
1003
1004 private:
1005 JSObject fCallback;
1006 };
1007
1008 // callbackObj has two functions in it, one to create a texture "makeTexture" and one to clean up
1009 // the underlying texture source "freeSrc". This way, we can create WebGL textures for each
1010 // surface/WebGLContext that the image is used on (we cannot share WebGLTextures across contexts).
MakeImageFromGenerator(SimpleImageInfo ii,JSObject callbackObj)1011 sk_sp<SkImage> MakeImageFromGenerator(SimpleImageInfo ii, JSObject callbackObj) {
1012 auto gen = std::make_unique<WebGLTextureImageGenerator>(toSkImageInfo(ii), callbackObj);
1013 return SkImages::DeferredFromTextureGenerator(std::move(gen));
1014 }
1015 #endif // CK_ENABLE_WEBGL
1016
1017
encodeImage(GrDirectContext * dContext,sk_sp<SkImage> img,SkEncodedImageFormat fmt,int quality)1018 static Uint8Array encodeImage(GrDirectContext* dContext,
1019 sk_sp<SkImage> img,
1020 SkEncodedImageFormat fmt,
1021 int quality) {
1022 sk_sp<SkData> data = nullptr;
1023 if (fmt == SkEncodedImageFormat::kJPEG) {
1024 SkJpegEncoder::Options opts;
1025 opts.fQuality = quality;
1026 data = SkJpegEncoder::Encode(dContext, img.get(), opts);
1027 } else if (fmt == SkEncodedImageFormat::kPNG) {
1028 data = SkPngEncoder::Encode(dContext, img.get(), {});
1029 } else {
1030 SkWebpEncoder::Options opts;
1031 if (quality >= 100) {
1032 opts.fCompression = SkWebpEncoder::Compression::kLossless;
1033 opts.fQuality = 75; // This is effort to compress
1034 } else {
1035 opts.fCompression = SkWebpEncoder::Compression::kLossy;
1036 opts.fQuality = quality;
1037 }
1038 data = SkWebpEncoder::Encode(dContext, img.get(), opts);
1039 }
1040 if (!data) {
1041 return emscripten::val::null();
1042 }
1043 return toBytes(data);
1044 }
1045
EMSCRIPTEN_BINDINGS(Skia)1046 EMSCRIPTEN_BINDINGS(Skia) {
1047 #ifdef ENABLE_GPU
1048 constant("gpu", true);
1049 function("_MakeGrContext", &MakeGrContext);
1050 #endif // ENABLE_GPU
1051
1052 #ifdef CK_ENABLE_WEBGL
1053 constant("webgl", true);
1054 function("_MakeOnScreenGLSurface", select_overload<sk_sp<SkSurface>(sk_sp<GrDirectContext>, int, int, sk_sp<SkColorSpace>)>(&MakeOnScreenGLSurface));
1055 function("_MakeOnScreenGLSurface", select_overload<sk_sp<SkSurface>(sk_sp<GrDirectContext>, int, int, sk_sp<SkColorSpace>, int, int)>(&MakeOnScreenGLSurface));
1056 function("_MakeRenderTargetWH", select_overload<sk_sp<SkSurface>(sk_sp<GrDirectContext>, int, int)>(&MakeRenderTarget));
1057 function("_MakeRenderTargetII", select_overload<sk_sp<SkSurface>(sk_sp<GrDirectContext>, SimpleImageInfo)>(&MakeRenderTarget));
1058 #endif // CK_ENABLE_WEBGL
1059
1060 #ifdef CK_ENABLE_WEBGPU
1061 constant("webgpu", true);
1062 function("_MakeGPUTextureSurface", &MakeGPUTextureSurface);
1063 #endif // CK_ENABLE_WEBGPU
1064
1065 function("getDecodeCacheLimitBytes", &SkResourceCache::GetTotalByteLimit);
1066 function("setDecodeCacheLimitBytes", &SkResourceCache::SetTotalByteLimit);
1067 function("getDecodeCacheUsedBytes" , &SkResourceCache::GetTotalBytesUsed);
1068
1069 function("_computeTonalColors", &computeTonalColors);
1070 function("_decodeAnimatedImage", optional_override([](WASMPointerU8 iptr,
1071 size_t length)->sk_sp<SkAnimatedImage> {
1072 uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
1073 auto bytes = SkData::MakeFromMalloc(imgData, length);
1074 auto codec = DecodeImageData(bytes);
1075 if (codec == nullptr) {
1076 return nullptr;
1077 }
1078 auto aCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
1079 if (aCodec == nullptr) {
1080 return nullptr;
1081 }
1082
1083 return SkAnimatedImage::Make(std::move(aCodec));
1084 }), allow_raw_pointers());
1085 function("_decodeImage", optional_override([](WASMPointerU8 iptr,
1086 size_t length)->sk_sp<SkImage> {
1087 uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
1088 auto bytes = SkData::MakeFromMalloc(imgData, length);
1089 auto codec = DecodeImageData(bytes);
1090 if (codec == nullptr) {
1091 return nullptr;
1092 }
1093 return std::get<0>(codec->getImage());
1094 }), allow_raw_pointers());
1095
1096 // These won't be called directly, there are corresponding JS helpers to deal with arrays.
1097 function("_MakeImage", optional_override([](SimpleImageInfo ii,
1098 WASMPointerU8 pPtr, int plen,
1099 size_t rowBytes)->sk_sp<SkImage> {
1100 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
1101 SkImageInfo info = toSkImageInfo(ii);
1102 sk_sp<SkData> pixelData = SkData::MakeFromMalloc(pixels, plen);
1103
1104 return SkImages::RasterFromData(info, pixelData, rowBytes);
1105 }), allow_raw_pointers());
1106
1107 function("_getShadowLocalBounds", optional_override([](
1108 WASMPointerF32 ctmPtr, const SkPath& path,
1109 WASMPointerF32 zPlaneParamPtr, WASMPointerF32 lightPosPtr,
1110 SkScalar lightRadius, uint32_t flags, WASMPointerF32 outPtr) -> bool {
1111 SkMatrix ctm;
1112 const SkScalar* nineMatrixValues = reinterpret_cast<const SkScalar*>(ctmPtr);
1113 ctm.set9(nineMatrixValues);
1114 const SkVector3* zPlaneParams = reinterpret_cast<const SkVector3*>(zPlaneParamPtr);
1115 const SkVector3* lightPos = reinterpret_cast<const SkVector3*>(lightPosPtr);
1116 SkRect* outputBounds = reinterpret_cast<SkRect*>(outPtr);
1117 return SkShadowUtils::GetLocalBounds(ctm, path, *zPlaneParams, *lightPos, lightRadius,
1118 flags, outputBounds);
1119 }));
1120
1121 #ifdef CK_SERIALIZE_SKP
1122 function("_MakePicture", optional_override([](WASMPointerU8 dPtr,
1123 size_t bytes)->sk_sp<SkPicture> {
1124 uint8_t* d = reinterpret_cast<uint8_t*>(dPtr);
1125 sk_sp<SkData> data = SkData::MakeFromMalloc(d, bytes);
1126
1127 #ifndef CK_NO_FONTS
1128 // Be sure we can process the data stored when serializing the SkPicture.
1129 static SkOnce once;
1130 once([] {
1131 SkTypeface::Register(SkTypeface_FreeType::FactoryId,
1132 SkTypeface_FreeType::MakeFromStream );
1133 });
1134 #endif
1135
1136 SkDeserialProcs dp;
1137 dp.fImageDataProc = [](sk_sp<SkData> bytes, std::optional<SkAlphaType> at, void* ctx) -> sk_sp<SkImage> {
1138 auto codec = DecodeImageData(bytes);
1139 if (codec == nullptr) {
1140 return nullptr;
1141 }
1142 SkImageInfo info = codec->getInfo();
1143 if (at.has_value()) {
1144 info = info.makeAlphaType(*at);
1145 } else if (kUnpremul_SkAlphaType == info.alphaType()) {
1146 // Otherwise, prefer premul over unpremul (this produces better filtering in general)
1147 info = info.makeAlphaType(kPremul_SkAlphaType);
1148 }
1149 return std::get<0>(codec->getImage(info));
1150 };
1151
1152 return SkPicture::MakeFromData(data.get(), nullptr);
1153 }), allow_raw_pointers());
1154 #endif
1155
1156 #ifdef ENABLE_GPU
1157 class_<GrDirectContext>("GrDirectContext")
1158 .smart_ptr<sk_sp<GrDirectContext>>("sk_sp<GrDirectContext>")
1159 .function("_getResourceCacheLimitBytes",
1160 optional_override([](GrDirectContext& self)->size_t {
1161 int maxResources = 0;// ignored
1162 size_t currMax = 0;
1163 self.getResourceCacheLimits(&maxResources, &currMax);
1164 return currMax;
1165 }))
1166 .function("_getResourceCacheUsageBytes",
1167 optional_override([](GrDirectContext& self)->size_t {
1168 int usedResources = 0;// ignored
1169 size_t currUsage = 0;
1170 self.getResourceCacheUsage(&usedResources, &currUsage);
1171 return currUsage;
1172 }))
1173 .function("_releaseResourcesAndAbandonContext",
1174 &GrDirectContext::releaseResourcesAndAbandonContext)
1175 .function("_setResourceCacheLimitBytes",
1176 optional_override([](GrDirectContext& self, size_t maxResourceBytes)->void {
1177 int maxResources = 0;
1178 size_t currMax = 0; // ignored
1179 self.getResourceCacheLimits(&maxResources, &currMax);
1180 self.setResourceCacheLimits(maxResources, maxResourceBytes);
1181 }));
1182 #endif // ENABLE_GPU
1183 #ifdef CK_ENABLE_WEBGL
1184 // This allows us to give the C++ code a JS callback to delete textures that
1185 // have been passed in via makeImageFromTexture and makeImageFromTextureSource.
1186 function("_setTextureCleanup", optional_override([](JSObject callbackObj)->void {
1187 textureCleanup = callbackObj;
1188 }));
1189 #endif
1190
1191 class_<SkAnimatedImage>("AnimatedImage")
1192 .smart_ptr<sk_sp<SkAnimatedImage>>("sk_sp<AnimatedImage>")
1193 .function("currentFrameDuration", &SkAnimatedImage::currentFrameDuration)
1194 .function("decodeNextFrame", &SkAnimatedImage::decodeNextFrame)
1195 .function("getFrameCount", &SkAnimatedImage::getFrameCount)
1196 .function("getRepetitionCount", &SkAnimatedImage::getRepetitionCount)
1197 .function("height", optional_override([](SkAnimatedImage& self)->int32_t {
1198 // getBounds returns an SkRect, but internally, the width and height are ints.
1199 return SkScalarFloorToInt(self.getBounds().height());
1200 }))
1201 .function("makeImageAtCurrentFrame", &SkAnimatedImage::getCurrentFrame)
1202 .function("reset", &SkAnimatedImage::reset)
1203 .function("width", optional_override([](SkAnimatedImage& self)->int32_t {
1204 return SkScalarFloorToInt(self.getBounds().width());
1205 }));
1206
1207 class_<SkBlender>("Blender")
1208 .smart_ptr<sk_sp<SkBlender>>("sk_sp<Blender>")
1209 .class_function("Mode", &SkBlender::Mode);
1210
1211 class_<SkCanvas>("Canvas")
1212 .constructor<>()
1213 .constructor<SkScalar,SkScalar>()
1214 .function("_clear", optional_override([](SkCanvas& self, WASMPointerF32 cPtr) {
1215 self.clear(ptrToSkColor4f(cPtr));
1216 }))
1217 .function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
1218 .function("_clipRRect", optional_override([](SkCanvas& self, WASMPointerF32 fPtr, SkClipOp op, bool doAntiAlias) {
1219 self.clipRRect(ptrToSkRRect(fPtr), op, doAntiAlias);
1220 }))
1221 .function("_clipRect", optional_override([](SkCanvas& self, WASMPointerF32 fPtr, SkClipOp op, bool doAntiAlias) {
1222 const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
1223 self.clipRect(*rect, op, doAntiAlias);
1224 }))
1225 .function("_concat", optional_override([](SkCanvas& self, WASMPointerF32 mPtr) {
1226 //TODO(skbug.com/10108): make the JS side be column major.
1227 const SkScalar* sixteenMatrixValues = reinterpret_cast<const SkScalar*>(mPtr);
1228 SkM44 m = SkM44::RowMajor(sixteenMatrixValues);
1229 self.concat(m);
1230 }))
1231 .function("_drawArc", optional_override([](SkCanvas& self, WASMPointerF32 fPtr,
1232 SkScalar startAngle, SkScalar sweepAngle,
1233 bool useCenter, const SkPaint& paint) {
1234 const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
1235 self.drawArc(*oval, startAngle, sweepAngle, useCenter, paint);
1236 }))
1237 .function("_drawAtlasOptions", optional_override([](SkCanvas& self,
1238 const sk_sp<SkImage>& atlas, WASMPointerF32 xptr,
1239 WASMPointerF32 rptr, WASMPointerU32 cptr, int count,
1240 SkBlendMode mode, SkFilterMode filter, SkMipmapMode mipmap,
1241 const SkPaint* paint)->void {
1242 const SkRSXform* dstXforms = reinterpret_cast<const SkRSXform*>(xptr);
1243 const SkRect* srcRects = reinterpret_cast<const SkRect*>(rptr);
1244 const SkColor* colors = nullptr;
1245 if (cptr) {
1246 colors = reinterpret_cast<const SkColor*>(cptr);
1247 }
1248 SkSamplingOptions sampling(filter, mipmap);
1249 self.drawAtlas(atlas.get(), dstXforms, srcRects, colors, count, mode, sampling,
1250 nullptr, paint);
1251 }), allow_raw_pointers())
1252 .function("_drawAtlasCubic", optional_override([](SkCanvas& self,
1253 const sk_sp<SkImage>& atlas, WASMPointerF32 xptr,
1254 WASMPointerF32 rptr, WASMPointerU32 cptr, int count,
1255 SkBlendMode mode, float B, float C, const SkPaint* paint)->void {
1256 const SkRSXform* dstXforms = reinterpret_cast<const SkRSXform*>(xptr);
1257 const SkRect* srcRects = reinterpret_cast<const SkRect*>(rptr);
1258 const SkColor* colors = nullptr;
1259 if (cptr) {
1260 colors = reinterpret_cast<const SkColor*>(cptr);
1261 }
1262 SkSamplingOptions sampling({B, C});
1263 self.drawAtlas(atlas.get(), dstXforms, srcRects, colors, count, mode, sampling,
1264 nullptr, paint);
1265 }), allow_raw_pointers())
1266 .function("_drawCircle", select_overload<void (SkScalar, SkScalar, SkScalar, const SkPaint& paint)>(&SkCanvas::drawCircle))
1267 .function("_drawColor", optional_override([](SkCanvas& self, WASMPointerF32 cPtr) {
1268 self.drawColor(ptrToSkColor4f(cPtr));
1269 }))
1270 .function("_drawColor", optional_override([](SkCanvas& self, WASMPointerF32 cPtr, SkBlendMode mode) {
1271 self.drawColor(ptrToSkColor4f(cPtr), mode);
1272 }))
1273 .function("_drawColorInt", optional_override([](SkCanvas& self, SkColor color, SkBlendMode mode) {
1274 self.drawColor(color, mode);
1275 }))
1276 .function("_drawDRRect", optional_override([](SkCanvas& self, WASMPointerF32 outerPtr,
1277 WASMPointerF32 innerPtr, const SkPaint& paint) {
1278 self.drawDRRect(ptrToSkRRect(outerPtr), ptrToSkRRect(innerPtr), paint);
1279 }))
1280 .function("_drawGlyphs", optional_override([](SkCanvas& self,
1281 int count,
1282 WASMPointerU16 glyphs,
1283 WASMPointerF32 positions,
1284 float x, float y,
1285 const SkFont& font,
1286 const SkPaint& paint)->void {
1287 self.drawGlyphs(count,
1288 reinterpret_cast<const uint16_t*>(glyphs),
1289 reinterpret_cast<const SkPoint*>(positions),
1290 {x, y}, font, paint);
1291 }))
1292 // TODO: deprecate this version, and require sampling
1293 .function("_drawImage", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
1294 SkScalar x, SkScalar y, const SkPaint* paint) {
1295 self.drawImage(image.get(), x, y, SkSamplingOptions(), paint);
1296 }), allow_raw_pointers())
1297 .function("_drawImageCubic", optional_override([](SkCanvas& self, const sk_sp<SkImage>& img,
1298 SkScalar left, SkScalar top,
1299 float B, float C, // See SkSamplingOptions.h for docs.
1300 const SkPaint* paint)->void {
1301 self.drawImage(img.get(), left, top, SkSamplingOptions({B, C}), paint);
1302 }), allow_raw_pointers())
1303 .function("_drawImageOptions", optional_override([](SkCanvas& self, const sk_sp<SkImage>& img,
1304 SkScalar left, SkScalar top,
1305 SkFilterMode filter, SkMipmapMode mipmap,
1306 const SkPaint* paint)->void {
1307 self.drawImage(img.get(), left, top, {filter, mipmap}, paint);
1308 }), allow_raw_pointers())
1309
1310 .function("_drawImageNine", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
1311 WASMPointerU32 centerPtr, WASMPointerF32 dstPtr,
1312 SkFilterMode filter, const SkPaint* paint)->void {
1313 const SkIRect* center = reinterpret_cast<const SkIRect*>(centerPtr);
1314 const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
1315
1316 self.drawImageNine(image.get(), *center, *dst, filter, paint);
1317 }), allow_raw_pointers())
1318 // TODO: deprecate this version, and require sampling
1319 .function("_drawImageRect", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
1320 WASMPointerF32 srcPtr, WASMPointerF32 dstPtr,
1321 const SkPaint* paint, bool fastSample)->void {
1322 const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
1323 const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
1324 self.drawImageRect(image, *src, *dst, SkSamplingOptions(), paint,
1325 fastSample ? SkCanvas::kFast_SrcRectConstraint:
1326 SkCanvas::kStrict_SrcRectConstraint);
1327 }), allow_raw_pointers())
1328 .function("_drawImageRectCubic", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
1329 WASMPointerF32 srcPtr, WASMPointerF32 dstPtr,
1330 float B, float C, // See SkSamplingOptions.h for docs.
1331 const SkPaint* paint)->void {
1332 const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
1333 const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
1334 auto constraint = SkCanvas::kStrict_SrcRectConstraint; // TODO: get from caller
1335 self.drawImageRect(image.get(), *src, *dst, SkSamplingOptions({B, C}), paint, constraint);
1336 }), allow_raw_pointers())
1337 .function("_drawImageRectOptions", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
1338 WASMPointerF32 srcPtr, WASMPointerF32 dstPtr,
1339 SkFilterMode filter, SkMipmapMode mipmap,
1340 const SkPaint* paint)->void {
1341 const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
1342 const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
1343 auto constraint = SkCanvas::kStrict_SrcRectConstraint; // TODO: get from caller
1344 self.drawImageRect(image.get(), *src, *dst, {filter, mipmap}, paint, constraint);
1345 }), allow_raw_pointers())
1346 .function("_drawLine", select_overload<void (SkScalar, SkScalar, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawLine))
1347 .function("_drawOval", optional_override([](SkCanvas& self, WASMPointerF32 fPtr,
1348 const SkPaint& paint)->void {
1349 const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
1350 self.drawOval(*oval, paint);
1351 }))
1352 .function("_drawPaint", &SkCanvas::drawPaint)
1353 #ifdef CK_INCLUDE_PARAGRAPH
1354 .function("_drawParagraph", optional_override([](SkCanvas& self, skia::textlayout::Paragraph* p,
1355 SkScalar x, SkScalar y) {
1356 p->paint(&self, x, y);
1357 }), allow_raw_pointers())
1358 #endif
1359 .function("_drawPath", &SkCanvas::drawPath)
1360 .function("_drawPatch", optional_override([](SkCanvas& self,
1361 WASMPointerF32 cubics,
1362 WASMPointerU32 colors,
1363 WASMPointerF32 texs,
1364 SkBlendMode mode,
1365 const SkPaint& paint)->void {
1366 self.drawPatch(reinterpret_cast<const SkPoint*>(cubics),
1367 reinterpret_cast<const SkColor*>(colors),
1368 reinterpret_cast<const SkPoint*>(texs),
1369 mode, paint);
1370 }))
1371 // Of note, picture is *not* what is colloquially thought of as a "picture", what we call
1372 // a bitmap. An SkPicture is a series of draw commands.
1373 .function("_drawPicture", select_overload<void (const sk_sp<SkPicture>&)>(&SkCanvas::drawPicture))
1374 .function("_drawPoints", optional_override([](SkCanvas& self, SkCanvas::PointMode mode,
1375 WASMPointerF32 pptr,
1376 int count, SkPaint& paint)->void {
1377 const SkPoint* pts = reinterpret_cast<const SkPoint*>(pptr);
1378 self.drawPoints(mode, count, pts, paint);
1379 }))
1380 .function("_drawRRect",optional_override([](SkCanvas& self, WASMPointerF32 fPtr, const SkPaint& paint) {
1381 self.drawRRect(ptrToSkRRect(fPtr), paint);
1382 }))
1383 .function("_drawRect", optional_override([](SkCanvas& self, WASMPointerF32 fPtr,
1384 const SkPaint& paint)->void {
1385 const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
1386 self.drawRect(*rect, paint);
1387 }))
1388 .function("_drawRect4f", optional_override([](SkCanvas& self, SkScalar left, SkScalar top,
1389 SkScalar right, SkScalar bottom,
1390 const SkPaint& paint)->void {
1391 const SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
1392 self.drawRect(rect, paint);
1393 }))
1394 .function("_drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
1395 WASMPointerF32 zPlaneParamPtr,
1396 WASMPointerF32 lightPosPtr,
1397 SkScalar lightRadius,
1398 WASMPointerF32 ambientColorPtr,
1399 WASMPointerF32 spotColorPtr,
1400 uint32_t flags) {
1401 const SkVector3* zPlaneParams = reinterpret_cast<const SkVector3*>(zPlaneParamPtr);
1402 const SkVector3* lightPos = reinterpret_cast<const SkVector3*>(lightPosPtr);
1403
1404 SkShadowUtils::DrawShadow(&self, path, *zPlaneParams, *lightPos, lightRadius,
1405 ptrToSkColor4f(ambientColorPtr).toSkColor(),
1406 ptrToSkColor4f(spotColorPtr).toSkColor(),
1407 flags);
1408 }))
1409 #ifndef CK_NO_FONTS
1410 .function("_drawSimpleText", optional_override([](SkCanvas& self, WASMPointerU8 sptr,
1411 size_t len, SkScalar x, SkScalar y, const SkFont& font,
1412 const SkPaint& paint) {
1413 const char* str = reinterpret_cast<const char*>(sptr);
1414
1415 self.drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
1416 }))
1417 .function("_drawTextBlob", select_overload<void (const sk_sp<SkTextBlob>&, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawTextBlob))
1418 #endif
1419 .function("_drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
1420
1421 .function("_getDeviceClipBounds", optional_override([](const SkCanvas& self, WASMPointerI32 iPtr) {
1422 SkIRect* outputRect = reinterpret_cast<SkIRect*>(iPtr);
1423 if (!outputRect) {
1424 return; // output pointer cannot be null
1425 }
1426 self.getDeviceClipBounds(outputRect);
1427 }))
1428
1429 .function("_quickReject", optional_override([](const SkCanvas& self, WASMPointerF32 fPtr)->bool {
1430 const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
1431 return self.quickReject(*rect);
1432 }))
1433
1434 // 4x4 matrix functions
1435 // Just like with getTotalMatrix, we allocate the buffer for the 16 floats to go in from
1436 // interface.js, so it can also free them when its done.
1437 .function("_getLocalToDevice", optional_override([](const SkCanvas& self, WASMPointerF32 mPtr) {
1438 SkScalar* sixteenMatrixValues = reinterpret_cast<SkScalar*>(mPtr);
1439 if (!sixteenMatrixValues) {
1440 return; // matrix cannot be null
1441 }
1442 SkM44 m = self.getLocalToDevice();
1443 m.getRowMajor(sixteenMatrixValues);
1444 }))
1445 .function("getSaveCount", &SkCanvas::getSaveCount)
1446 // We allocate room for the matrix from the JS side and free it there so as to not have
1447 // an awkward moment where we malloc something here and "just know" to free it on the
1448 // JS side.
1449 .function("_getTotalMatrix", optional_override([](const SkCanvas& self, WASMPointerU8 mPtr) {
1450 SkScalar* nineMatrixValues = reinterpret_cast<SkScalar*>(mPtr);
1451 if (!nineMatrixValues) {
1452 return; // matrix cannot be null
1453 }
1454 SkMatrix m = self.getTotalMatrix();
1455 m.get9(nineMatrixValues);
1456 }))
1457 .function("_makeSurface", optional_override([](SkCanvas& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
1458 return self.makeSurface(toSkImageInfo(sii), nullptr);
1459 }), allow_raw_pointers())
1460
1461 .function("_readPixels", optional_override([](SkCanvas& self, SimpleImageInfo di,
1462 WASMPointerU8 pPtr,
1463 size_t dstRowBytes, int srcX, int srcY) {
1464 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
1465 SkImageInfo dstInfo = toSkImageInfo(di);
1466
1467 return self.readPixels(dstInfo, pixels, dstRowBytes, srcX, srcY);
1468 }), allow_raw_pointers())
1469 .function("restore", &SkCanvas::restore)
1470 .function("restoreToCount", &SkCanvas::restoreToCount)
1471 .function("rotate", select_overload<void (SkScalar, SkScalar, SkScalar)>(&SkCanvas::rotate))
1472 .function("save", &SkCanvas::save)
1473 .function("_saveLayer", optional_override([](SkCanvas& self, const SkPaint* p, WASMPointerF32 fPtr,
1474 const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags,
1475 SkTileMode backdropFilterTileMode)->int {
1476 SkRect* bounds = reinterpret_cast<SkRect*>(fPtr);
1477 return self.saveLayer(SkCanvas::SaveLayerRec(bounds, p, backdrop, backdropFilterTileMode, nullptr, flags));
1478 }), allow_raw_pointers())
1479 .function("saveLayerPaint", optional_override([](SkCanvas& self, const SkPaint p)->int {
1480 return self.saveLayer(SkCanvas::SaveLayerRec(nullptr, &p, 0));
1481 }))
1482 .function("scale", &SkCanvas::scale)
1483 .function("skew", &SkCanvas::skew)
1484 .function("translate", &SkCanvas::translate)
1485 .function("_writePixels", optional_override([](SkCanvas& self, SimpleImageInfo di,
1486 WASMPointerU8 pPtr,
1487 size_t srcRowBytes, int dstX, int dstY) {
1488 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
1489 SkImageInfo dstInfo = toSkImageInfo(di);
1490
1491 return self.writePixels(dstInfo, pixels, srcRowBytes, dstX, dstY);
1492 }));
1493
1494 class_<SkColorFilter>("ColorFilter")
1495 .smart_ptr<sk_sp<SkColorFilter>>("sk_sp<ColorFilter>>")
1496 .class_function("_MakeBlend", optional_override([](WASMPointerF32 cPtr, SkBlendMode mode,
1497 sk_sp<SkColorSpace> colorSpace)->sk_sp<SkColorFilter> {
1498 return SkColorFilters::Blend(ptrToSkColor4f(cPtr), colorSpace, mode);
1499 }))
1500 .class_function("MakeCompose", &SkColorFilters::Compose)
1501 .class_function("MakeLerp", &SkColorFilters::Lerp)
1502 .class_function("MakeLinearToSRGBGamma", &SkColorFilters::LinearToSRGBGamma)
1503 .class_function("_makeMatrix", optional_override([](WASMPointerF32 fPtr) {
1504 float* twentyFloats = reinterpret_cast<float*>(fPtr);
1505 return SkColorFilters::Matrix(twentyFloats);
1506 }))
1507 .class_function("MakeSRGBToLinearGamma", &SkColorFilters::SRGBToLinearGamma)
1508 .class_function("MakeLuma", &SkLumaColorFilter::Make);
1509
1510 class_<SkContourMeasureIter>("ContourMeasureIter")
1511 .constructor<const SkPath&, bool, SkScalar>()
1512 .function("next", &SkContourMeasureIter::next);
1513
1514 class_<SkContourMeasure>("ContourMeasure")
1515 .smart_ptr<sk_sp<SkContourMeasure>>("sk_sp<ContourMeasure>>")
1516 .function("_getPosTan", optional_override([](SkContourMeasure& self,
1517 SkScalar distance,
1518 WASMPointerF32 oPtr) -> void {
1519 SkPoint* pointAndVector = reinterpret_cast<SkPoint*>(oPtr);
1520 if (!self.getPosTan(distance, pointAndVector, pointAndVector + 1)) {
1521 SkDebugf("zero-length path in getPosTan\n");
1522 }
1523 }))
1524 .function("getSegment", optional_override([](SkContourMeasure& self, SkScalar startD,
1525 SkScalar stopD, bool startWithMoveTo) -> SkPath {
1526 SkPath p;
1527 bool ok = self.getSegment(startD, stopD, &p, startWithMoveTo);
1528 if (ok) {
1529 return p;
1530 }
1531 return SkPath();
1532 }))
1533 .function("isClosed", &SkContourMeasure::isClosed)
1534 .function("length", &SkContourMeasure::length);
1535
1536 #ifndef CK_NO_FONTS
1537 class_<SkFont>("Font")
1538 .constructor<>()
1539 .constructor<sk_sp<SkTypeface>>()
1540 .constructor<sk_sp<SkTypeface>, SkScalar>()
1541 .constructor<sk_sp<SkTypeface>, SkScalar, SkScalar, SkScalar>()
1542 .function("_getGlyphWidthBounds", optional_override([](SkFont& self, WASMPointerU16 gPtr,
1543 int numGlyphs, WASMPointerF32 wPtr,
1544 WASMPointerF32 rPtr,
1545 SkPaint* paint) {
1546 const SkGlyphID* glyphs = reinterpret_cast<const SkGlyphID*>(gPtr);
1547 // On the JS side only one of these is set at a time for easier ergonomics.
1548 SkRect* outputRects = reinterpret_cast<SkRect*>(rPtr);
1549 SkScalar* outputWidths = reinterpret_cast<SkScalar*>(wPtr);
1550 self.getWidthsBounds(glyphs, numGlyphs, outputWidths, outputRects, paint);
1551 }), allow_raw_pointers())
1552 .function("_getGlyphIDs", optional_override([](SkFont& self, WASMPointerU8 sptr,
1553 size_t strLen, size_t expectedCodePoints,
1554 WASMPointerU16 iPtr) -> int {
1555 char* str = reinterpret_cast<char*>(sptr);
1556 SkGlyphID* glyphIDs = reinterpret_cast<SkGlyphID*>(iPtr);
1557
1558 int actualCodePoints = self.textToGlyphs(str, strLen, SkTextEncoding::kUTF8,
1559 glyphIDs, expectedCodePoints);
1560 return actualCodePoints;
1561 }))
1562 .function("getMetrics", optional_override([](SkFont& self) -> JSObject {
1563 SkFontMetrics fm;
1564 self.getMetrics(&fm);
1565
1566 JSObject j = emscripten::val::object();
1567 j.set("ascent", fm.fAscent);
1568 j.set("descent", fm.fDescent);
1569 j.set("leading", fm.fLeading);
1570 if (!(fm.fFlags & SkFontMetrics::kBoundsInvalid_Flag)) {
1571 const float rect[] = {
1572 fm.fXMin, fm.fTop, fm.fXMax, fm.fBottom
1573 };
1574 j.set("bounds", MakeTypedArray(4, rect));
1575 }
1576 return j;
1577 }))
1578 .function("_getGlyphIntercepts", optional_override([](SkFont& self,
1579 WASMPointerU16 gPtr, size_t numGlyphs, bool ownGlyphs,
1580 WASMPointerF32 pPtr, size_t numPos, bool ownPos,
1581 float top, float bottom) -> Float32Array {
1582 JSSpan<uint16_t> glyphs(gPtr, numGlyphs, ownGlyphs);
1583 JSSpan<float> pos (pPtr, numPos, ownPos);
1584 if (glyphs.size() > (pos.size() >> 1)) {
1585 return emscripten::val("Not enough x,y position pairs for glyphs");
1586 }
1587 auto sects = self.getIntercepts(glyphs.data(), SkToInt(glyphs.size()),
1588 (const SkPoint*)pos.data(), top, bottom);
1589 return MakeTypedArray(sects.size(), (const float*)sects.data());
1590 }), allow_raw_pointers())
1591 .function("getScaleX", &SkFont::getScaleX)
1592 .function("getSize", &SkFont::getSize)
1593 .function("getSkewX", &SkFont::getSkewX)
1594 .function("isEmbolden", &SkFont::isEmbolden)
1595 .function("getTypeface", &SkFont::getTypeface, allow_raw_pointers())
1596 .function("setEdging", &SkFont::setEdging)
1597 .function("setEmbeddedBitmaps", &SkFont::setEmbeddedBitmaps)
1598 .function("setHinting", &SkFont::setHinting)
1599 .function("setLinearMetrics", &SkFont::setLinearMetrics)
1600 .function("setScaleX", &SkFont::setScaleX)
1601 .function("setSize", &SkFont::setSize)
1602 .function("setSkewX", &SkFont::setSkewX)
1603 .function("setEmbolden", &SkFont::setEmbolden)
1604 .function("setSubpixel", &SkFont::setSubpixel)
1605 .function("setTypeface", &SkFont::setTypeface, allow_raw_pointers());
1606
1607 class_<SkFontMgr>("FontMgr")
1608 .smart_ptr<sk_sp<SkFontMgr>>("sk_sp<FontMgr>")
1609 .class_function("_fromData", optional_override([](WASMPointerU32 dPtr,
1610 WASMPointerU32 sPtr,
1611 int numFonts)->sk_sp<SkFontMgr> {
1612 auto datas = reinterpret_cast<const uint8_t**>(dPtr);
1613 auto sizes = reinterpret_cast<const size_t*>(sPtr);
1614
1615 std::unique_ptr<sk_sp<SkData>[]> skdatas(new sk_sp<SkData>[numFonts]);
1616 for (int i = 0; i < numFonts; ++i) {
1617 skdatas[i] = SkData::MakeFromMalloc(datas[i], sizes[i]);
1618 }
1619
1620 return SkFontMgr_New_Custom_Data(SkSpan(skdatas.get(), numFonts));
1621 }), allow_raw_pointers())
1622 .function("countFamilies", &SkFontMgr::countFamilies)
1623 .function("getFamilyName", optional_override([](SkFontMgr& self, int index)->JSString {
1624 if (index < 0 || index >= self.countFamilies()) {
1625 return emscripten::val::null();
1626 }
1627 SkString s;
1628 self.getFamilyName(index, &s);
1629 return emscripten::val(s.c_str());
1630 }))
1631 .function("matchFamilyStyle", optional_override([](SkFontMgr& self, std::string name, emscripten::val jsFontStyle)->sk_sp<SkTypeface> {
1632 auto weight = SkFontStyle::Weight(jsFontStyle["weight"].isUndefined() ? SkFontStyle::kNormal_Weight : jsFontStyle["weight"].as<int>());
1633 auto width = SkFontStyle::Width(jsFontStyle["width"].isUndefined() ? SkFontStyle::kNormal_Width : jsFontStyle["width"].as<int>());
1634 auto slant = SkFontStyle::Slant(jsFontStyle["slant"].isUndefined() ? SkFontStyle::kUpright_Slant : static_cast<SkFontStyle::Slant>(jsFontStyle["slant"].as<int>()));
1635
1636 SkFontStyle style(weight, width, slant);
1637
1638 return self.matchFamilyStyle(name.c_str(), style);
1639 }), allow_raw_pointers())
1640 #ifdef SK_DEBUG
1641 .function("dumpFamilies", optional_override([](SkFontMgr& self) {
1642 int numFam = self.countFamilies();
1643 SkDebugf("There are %d font families\n", numFam);
1644 for (int i = 0 ; i< numFam; i++) {
1645 SkString s;
1646 self.getFamilyName(i, &s);
1647 SkDebugf("\t%s\n", s.c_str());
1648 }
1649 }))
1650 #endif
1651 .function("_makeTypefaceFromData", optional_override([](SkFontMgr& self,
1652 WASMPointerU8 fPtr,
1653 int flen)->sk_sp<SkTypeface> {
1654 uint8_t* font = reinterpret_cast<uint8_t*>(fPtr);
1655 sk_sp<SkData> fontData = SkData::MakeFromMalloc(font, flen);
1656
1657 return self.makeFromData(fontData);
1658 }), allow_raw_pointers());
1659 #endif // CK_NO_FONTS
1660
1661 class_<SkImage>("Image")
1662 .smart_ptr<sk_sp<SkImage>>("sk_sp<Image>")
1663 #ifdef CK_ENABLE_WEBGL
1664 .class_function("_makeFromGenerator", &MakeImageFromGenerator)
1665 #endif
1666 // Note that this needs to be cleaned up with delete().
1667 .function("getColorSpace", optional_override([](sk_sp<SkImage> self)->sk_sp<SkColorSpace> {
1668 return self->imageInfo().refColorSpace();
1669 }), allow_raw_pointers())
1670 .function("getImageInfo", optional_override([](sk_sp<SkImage> self)->JSObject {
1671 // We cannot return a SimpleImageInfo because the colorspace object would be leaked.
1672 JSObject result = emscripten::val::object();
1673 SkImageInfo ii = self->imageInfo();
1674 result.set("alphaType", ii.alphaType());
1675 result.set("colorType", ii.colorType());
1676 result.set("height", ii.height());
1677 result.set("width", ii.width());
1678 return result;
1679 }))
1680 .function("height", &SkImage::height)
1681 .function("_encodeToBytes", optional_override([](sk_sp<SkImage> self,
1682 SkEncodedImageFormat fmt,
1683 int quality) -> Uint8Array {
1684 return encodeImage(nullptr, self, fmt, quality);
1685 }))
1686 #if defined(ENABLE_GPU)
1687 .function("_encodeToBytes", optional_override([](sk_sp<SkImage> self,
1688 SkEncodedImageFormat fmt,
1689 int quality,
1690 GrDirectContext* dContext) -> Uint8Array {
1691 return encodeImage(dContext, self, fmt, quality);
1692 }), allow_raw_pointers())
1693 #endif
1694 .function("makeCopyWithDefaultMipmaps", optional_override([](sk_sp<SkImage> self)->sk_sp<SkImage> {
1695 return self->withDefaultMipmaps();
1696 }))
1697 .function("_makeShaderCubic", optional_override([](sk_sp<SkImage> self,
1698 SkTileMode tx, SkTileMode ty,
1699 float B, float C, // See SkSamplingOptions.h for docs.
1700 WASMPointerF32 mPtr)->sk_sp<SkShader> {
1701 OptionalMatrix localMatrix(mPtr);
1702 return self->makeShader(tx, ty, SkSamplingOptions({B, C}), mPtr ? &localMatrix
1703 : nullptr);
1704 }), allow_raw_pointers())
1705 .function("_makeShaderOptions", optional_override([](sk_sp<SkImage> self,
1706 SkTileMode tx, SkTileMode ty,
1707 SkFilterMode filter, SkMipmapMode mipmap,
1708 WASMPointerF32 mPtr)->sk_sp<SkShader> {
1709 OptionalMatrix localMatrix(mPtr);
1710 return self->makeShader(tx, ty, {filter, mipmap}, mPtr ? &localMatrix : nullptr);
1711 }), allow_raw_pointers())
1712 #if defined(ENABLE_GPU)
1713 .function("_readPixels", optional_override([](sk_sp<SkImage> self,
1714 SimpleImageInfo sii, WASMPointerU8 pPtr,
1715 size_t dstRowBytes, int srcX, int srcY,
1716 GrDirectContext* dContext)->bool {
1717 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
1718 SkImageInfo ii = toSkImageInfo(sii);
1719 return self->readPixels(dContext, ii, pixels, dstRowBytes, srcX, srcY);
1720 }), allow_raw_pointers())
1721 #endif
1722 .function("_readPixels", optional_override([](sk_sp<SkImage> self,
1723 SimpleImageInfo sii, WASMPointerU8 pPtr,
1724 size_t dstRowBytes, int srcX, int srcY)->bool {
1725 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
1726 SkImageInfo ii = toSkImageInfo(sii);
1727 return self->readPixels(nullptr, ii, pixels, dstRowBytes, srcX, srcY);
1728 }), allow_raw_pointers())
1729 .function("width", &SkImage::width);
1730
1731 class_<SkImageFilter>("ImageFilter")
1732 .smart_ptr<sk_sp<SkImageFilter>>("sk_sp<ImageFilter>")
1733 .function("_getOutputBounds", optional_override([](const SkImageFilter& self, WASMPointerF32 bPtr, WASMPointerF32 mPtr, WASMPointerU32 oPtr)->void {
1734 SkRect* rect = reinterpret_cast<SkRect*>(bPtr);
1735 OptionalMatrix ctm(mPtr);
1736 SkIRect* output = reinterpret_cast<SkIRect*>(oPtr);
1737 output[0] = self.filterBounds(ctm.mapRect(*rect).roundOut(), ctm, SkImageFilter::kForward_MapDirection);
1738 }))
1739 .class_function("MakeBlend", optional_override([](SkBlendMode mode, sk_sp<SkImageFilter> background,
1740 sk_sp<SkImageFilter> foreground)->sk_sp<SkImageFilter> {
1741 return SkImageFilters::Blend(mode, background, foreground);
1742 }))
1743 .class_function("MakeBlur", optional_override([](SkScalar sigmaX, SkScalar sigmaY,
1744 SkTileMode tileMode, sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1745 return SkImageFilters::Blur(sigmaX, sigmaY, tileMode, input);
1746 }))
1747 .class_function("MakeColorFilter", optional_override([](sk_sp<SkColorFilter> cf,
1748 sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1749 return SkImageFilters::ColorFilter(cf, input);
1750 }))
1751 .class_function("MakeCompose", &SkImageFilters::Compose)
1752 .class_function("MakeDilate", optional_override([](SkScalar radiusX, SkScalar radiusY,
1753 sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1754 return SkImageFilters::Dilate(radiusX, radiusY, input);
1755 }))
1756 .class_function("MakeDisplacementMap", optional_override([](SkColorChannel xChannelSelector,
1757 SkColorChannel yChannelSelector,
1758 SkScalar scale, sk_sp<SkImageFilter> displacement,
1759 sk_sp<SkImageFilter> color)->sk_sp<SkImageFilter> {
1760 return SkImageFilters::DisplacementMap(xChannelSelector, yChannelSelector,
1761 scale, displacement, color);
1762 }))
1763 .class_function("MakeShader", optional_override([](sk_sp<SkShader> shader)->sk_sp<SkImageFilter> {
1764 return SkImageFilters::Shader(shader);
1765 }))
1766 .class_function("_MakeDropShadow", optional_override([](SkScalar dx, SkScalar dy,
1767 SkScalar sigmaX, SkScalar sigmaY,
1768 WASMPointerF32 cPtr, sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1769 SkColor4f c = ptrToSkColor4f(cPtr);
1770 return SkImageFilters::DropShadow(dx, dy, sigmaX, sigmaY, c.toSkColor(), input);
1771 }))
1772 .class_function("_MakeDropShadowOnly", optional_override([](SkScalar dx, SkScalar dy,
1773 SkScalar sigmaX, SkScalar sigmaY,
1774 WASMPointerF32 cPtr, sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1775 SkColor4f c = ptrToSkColor4f(cPtr);
1776 return SkImageFilters::DropShadowOnly(dx, dy, sigmaX, sigmaY, c.toSkColor(), input);
1777 }))
1778 .class_function("MakeErode", optional_override([](SkScalar radiusX, SkScalar radiusY,
1779 sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1780 return SkImageFilters::Erode(radiusX, radiusY, input);
1781 }))
1782 .class_function("_MakeImageCubic", optional_override([](sk_sp<SkImage> image,
1783 float B, float C,
1784 WASMPointerF32 srcPtr,
1785 WASMPointerF32 dstPtr
1786 )->sk_sp<SkImageFilter> {
1787 const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
1788 const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
1789 if (src && dst) {
1790 return SkImageFilters::Image(image, *src, *dst, SkSamplingOptions({B, C}));
1791 }
1792 return SkImageFilters::Image(image, SkSamplingOptions({B, C}));
1793 }))
1794 .class_function("_MakeImageOptions", optional_override([](sk_sp<SkImage> image,
1795 SkFilterMode fm,
1796 SkMipmapMode mm,
1797 WASMPointerF32 srcPtr,
1798 WASMPointerF32 dstPtr
1799 )->sk_sp<SkImageFilter> {
1800 const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
1801 const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
1802 if (src && dst) {
1803 return SkImageFilters::Image(image, *src, *dst, SkSamplingOptions(fm, mm));
1804 }
1805 return SkImageFilters::Image(image, SkSamplingOptions(fm, mm));
1806 }))
1807 .class_function("_MakeMatrixTransformCubic",
1808 optional_override([](WASMPointerF32 mPtr, float B, float C,
1809 sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1810 OptionalMatrix matr(mPtr);
1811 return SkImageFilters::MatrixTransform(matr, SkSamplingOptions({B, C}), input);
1812 }))
1813 .class_function("_MakeMatrixTransformOptions",
1814 optional_override([](WASMPointerF32 mPtr, SkFilterMode fm, SkMipmapMode mm,
1815 sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1816 OptionalMatrix matr(mPtr);
1817 return SkImageFilters::MatrixTransform(matr, SkSamplingOptions(fm, mm), input);
1818 }))
1819 .class_function("MakeOffset", optional_override([](SkScalar dx, SkScalar dy,
1820 sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
1821 return SkImageFilters::Offset(dx, dy, input);
1822 }));
1823
1824 class_<SkMaskFilter>("MaskFilter")
1825 .smart_ptr<sk_sp<SkMaskFilter>>("sk_sp<MaskFilter>")
1826 .class_function("MakeBlur", optional_override([](SkBlurStyle style, SkScalar sigma, bool respectCTM)->sk_sp<SkMaskFilter> {
1827 // Adds a little helper because emscripten doesn't expose default params.
1828 return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
1829 }), allow_raw_pointers());
1830
1831 class_<SkPaint>("Paint")
1832 .constructor<>()
1833 .function("copy", optional_override([](const SkPaint& self)->SkPaint {
1834 SkPaint p(self);
1835 return p;
1836 }))
1837 // provide an allocated place to put the returned color
1838 .function("_getColor", optional_override([](SkPaint& self, WASMPointerF32 cPtr)->void {
1839 const SkColor4f& c = self.getColor4f();
1840 float* fourFloats = reinterpret_cast<float*>(cPtr);
1841 memcpy(fourFloats, c.vec(), 4 * sizeof(SkScalar));
1842 }))
1843 .function("getStrokeCap", &SkPaint::getStrokeCap)
1844 .function("getStrokeJoin", &SkPaint::getStrokeJoin)
1845 .function("getStrokeMiter", &SkPaint::getStrokeMiter)
1846 .function("getStrokeWidth", &SkPaint::getStrokeWidth)
1847 .function("setAntiAlias", &SkPaint::setAntiAlias)
1848 .function("setAlphaf", &SkPaint::setAlphaf)
1849 .function("setBlendMode", &SkPaint::setBlendMode)
1850 .function("setBlender", &SkPaint::setBlender)
1851 .function("_setColor", optional_override([](SkPaint& self, WASMPointerF32 cPtr,
1852 sk_sp<SkColorSpace> colorSpace) {
1853 self.setColor(ptrToSkColor4f(cPtr), colorSpace.get());
1854 }))
1855 .function("setColorInt", optional_override([](SkPaint& self, SkColor color) {
1856 self.setColor(SkColor4f::FromColor(color), nullptr);
1857 }))
1858 .function("setColorInt", optional_override([](SkPaint& self, SkColor color,
1859 sk_sp<SkColorSpace> colorSpace) {
1860 self.setColor(SkColor4f::FromColor(color), colorSpace.get());
1861 }))
1862 .function("setColorFilter", &SkPaint::setColorFilter)
1863 .function("setDither", &SkPaint::setDither)
1864 .function("setImageFilter", &SkPaint::setImageFilter)
1865 .function("setMaskFilter", &SkPaint::setMaskFilter)
1866 .function("setPathEffect", &SkPaint::setPathEffect)
1867 .function("setShader", &SkPaint::setShader)
1868 .function("setStrokeCap", &SkPaint::setStrokeCap)
1869 .function("setStrokeJoin", &SkPaint::setStrokeJoin)
1870 .function("setStrokeMiter", &SkPaint::setStrokeMiter)
1871 .function("setStrokeWidth", &SkPaint::setStrokeWidth)
1872 .function("setStyle", &SkPaint::setStyle);
1873
1874 class_<SkColorSpace>("ColorSpace")
1875 .smart_ptr<sk_sp<SkColorSpace>>("sk_sp<ColorSpace>")
1876 .class_function("Equals", optional_override([](sk_sp<SkColorSpace> a, sk_sp<SkColorSpace> b)->bool {
1877 return SkColorSpace::Equals(a.get(), b.get());
1878 }))
1879 // These are private because they are to be called once in interface.js to
1880 // avoid clients having to delete the returned objects.
1881 .class_function("_MakeSRGB", &SkColorSpace::MakeSRGB)
1882 .class_function("_MakeDisplayP3", optional_override([]()->sk_sp<SkColorSpace> {
1883 return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
1884 }))
1885 .class_function("_MakeAdobeRGB", optional_override([]()->sk_sp<SkColorSpace> {
1886 return SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
1887 }));
1888
1889 class_<SkPathEffect>("PathEffect")
1890 .smart_ptr<sk_sp<SkPathEffect>>("sk_sp<PathEffect>")
1891 .class_function("MakeCorner", &SkCornerPathEffect::Make)
1892 .class_function("_MakeDash", optional_override([](WASMPointerF32 cptr, int count,
1893 SkScalar phase)->sk_sp<SkPathEffect> {
1894 const float* intervals = reinterpret_cast<const float*>(cptr);
1895 return SkDashPathEffect::Make(intervals, count, phase);
1896 }), allow_raw_pointers())
1897 .class_function("MakeDiscrete", &SkDiscretePathEffect::Make)
1898 .class_function("_MakeLine2D", optional_override([](SkScalar width,
1899 WASMPointerF32 mPtr)->sk_sp<SkPathEffect> {
1900 SkMatrix matrix;
1901 const SkScalar* nineMatrixValues = reinterpret_cast<const SkScalar*>(mPtr);
1902 matrix.set9(nineMatrixValues);
1903 return SkLine2DPathEffect::Make(width, matrix);
1904 }), allow_raw_pointers())
1905 .class_function("MakePath1D", &SkPath1DPathEffect::Make)
1906 .class_function("_MakePath2D", optional_override([](WASMPointerF32 mPtr,
1907 SkPath path)->sk_sp<SkPathEffect> {
1908 SkMatrix matrix;
1909 const SkScalar* nineMatrixValues = reinterpret_cast<const SkScalar*>(mPtr);
1910 matrix.set9(nineMatrixValues);
1911 return SkPath2DPathEffect::Make(matrix, path);
1912 }), allow_raw_pointers());
1913
1914 // TODO(kjlubick, reed) Make SkPath immutable and only creatable via a factory/builder.
1915 class_<SkPath>("Path")
1916 .constructor<>()
1917 #ifdef CK_INCLUDE_PATHOPS
1918 .class_function("MakeFromOp", &MakePathFromOp)
1919 #endif
1920 .class_function("MakeFromSVGString", &MakePathFromSVGString)
1921 .class_function("MakeFromPathInterpolation", &MakePathFromInterpolation)
1922 .class_function("CanInterpolate", &CanInterpolate)
1923 .class_function("_MakeFromCmds", &MakePathFromCmds)
1924 .class_function("_MakeFromVerbsPointsWeights", &MakePathFromVerbsPointsWeights)
1925 .function("_addArc", optional_override([](SkPath& self,
1926 WASMPointerF32 fPtr,
1927 SkScalar startAngle, SkScalar sweepAngle)->void {
1928 const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
1929 self.addArc(*oval, startAngle, sweepAngle);
1930 }))
1931 .function("_addOval", optional_override([](SkPath& self,
1932 WASMPointerF32 fPtr,
1933 bool ccw, unsigned start)->void {
1934 const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
1935 self.addOval(*oval, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW, start);
1936 }))
1937 .function("_addCircle", optional_override([](SkPath& self,
1938 SkScalar x,
1939 SkScalar y,
1940 SkScalar r,
1941 bool ccw)->void {
1942 self.addCircle(x, y, r, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
1943 }))
1944 // interface.js has 3 overloads of addPath
1945 .function("_addPath", &ApplyAddPath)
1946 .function("_addPoly", optional_override([](SkPath& self,
1947 WASMPointerF32 fPtr,
1948 int count, bool close)->void {
1949 const SkPoint* pts = reinterpret_cast<const SkPoint*>(fPtr);
1950 self.addPoly(pts, count, close);
1951 }))
1952 .function("_addRect", optional_override([](SkPath& self,
1953 WASMPointerF32 fPtr,
1954 bool ccw)->void {
1955 const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
1956 self.addRect(*rect, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
1957 }))
1958 .function("_addRRect", optional_override([](SkPath& self,
1959 WASMPointerF32 fPtr,
1960 bool ccw)->void {
1961 self.addRRect(ptrToSkRRect(fPtr), ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
1962 }))
1963 .function("_addVerbsPointsWeights", &PathAddVerbsPointsWeights)
1964 .function("_arcToOval", optional_override([](SkPath& self,
1965 WASMPointerF32 fPtr, SkScalar startAngle,
1966 SkScalar sweepAngle, bool forceMoveTo)->void {
1967 const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
1968 self.arcTo(*oval, startAngle, sweepAngle, forceMoveTo);
1969 }))
1970 .function("_arcToRotated", &ApplyArcToArcSize)
1971 .function("_arcToTangent", ApplyArcToTangent)
1972 .function("_close", &ApplyClose)
1973 .function("_conicTo", &ApplyConicTo)
1974 .function("countPoints", &SkPath::countPoints)
1975 .function("contains", &SkPath::contains)
1976 .function("_cubicTo", &ApplyCubicTo)
1977 .function("_getPoint", optional_override([](SkPath& self, int index,
1978 WASMPointerF32 oPtr)->void {
1979 SkPoint* output = reinterpret_cast<SkPoint*>(oPtr);
1980 *output = self.getPoint(index);
1981 }))
1982 .function("isEmpty", &SkPath::isEmpty)
1983 .function("isVolatile", &SkPath::isVolatile)
1984 .function("_lineTo", &ApplyLineTo)
1985 .function("_moveTo", &ApplyMoveTo)
1986 .function("_quadTo", &ApplyQuadTo)
1987 .function("_rArcTo", &ApplyRArcToArcSize)
1988 .function("_rConicTo", &ApplyRConicTo)
1989 .function("_rCubicTo", &ApplyRCubicTo)
1990 .function("_rLineTo", &ApplyRLineTo)
1991 .function("_rMoveTo", &ApplyRMoveTo)
1992 .function("_rQuadTo", &ApplyRQuadTo)
1993 .function("reset", &ApplyReset)
1994 .function("rewind", &ApplyRewind)
1995 .function("setIsVolatile", &SkPath::setIsVolatile)
1996 .function("_transform", select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&ApplyTransform))
1997
1998 // PathEffects
1999 .function("_dash", &ApplyDash)
2000 .function("_trim", &ApplyTrim)
2001 .function("_stroke", &ApplyStroke)
2002
2003 #ifdef CK_INCLUDE_PATHOPS
2004 // PathOps
2005 .function("_simplify", &ApplySimplify)
2006 .function("_op", &ApplyPathOp)
2007 .function("makeAsWinding", &MakeAsWinding)
2008 #endif
2009 // Exporting
2010 .function("toSVGString", &ToSVGString)
2011 .function("toCmds", &ToCmds)
2012
2013 .function("setFillType", select_overload<void(SkPathFillType)>(&SkPath::setFillType))
2014 .function("getFillType", &SkPath::getFillType)
2015 .function("_getBounds", optional_override([](SkPath& self,
2016 WASMPointerF32 fPtr)->void {
2017 SkRect* output = reinterpret_cast<SkRect*>(fPtr);
2018 output[0] = self.getBounds();
2019 }))
2020 .function("_computeTightBounds", optional_override([](SkPath& self,
2021 WASMPointerF32 fPtr)->void {
2022 SkRect* output = reinterpret_cast<SkRect*>(fPtr);
2023 output[0] = self.computeTightBounds();
2024 }))
2025 .function("equals", &Equals)
2026 .function("copy", &CopyPath)
2027 #ifdef SK_DEBUG
2028 .function("dump", select_overload<void() const>(&SkPath::dump))
2029 .function("dumpHex", select_overload<void() const>(&SkPath::dumpHex))
2030 #endif
2031 ;
2032
2033 static SkRTreeFactory bbhFactory;
2034 class_<SkPictureRecorder>("PictureRecorder")
2035 .constructor<>()
2036 .function("_beginRecording", optional_override([](SkPictureRecorder& self,
2037 WASMPointerF32 fPtr,
2038 bool computeBounds) -> SkCanvas* {
2039 SkRect* bounds = reinterpret_cast<SkRect*>(fPtr);
2040 return self.beginRecording(*bounds, computeBounds ? &bbhFactory : nullptr);
2041 }), allow_raw_pointers())
2042 .function("finishRecordingAsPicture", optional_override([](SkPictureRecorder& self)
2043 -> sk_sp<SkPicture> {
2044 return self.finishRecordingAsPicture();
2045 }), allow_raw_pointers());
2046
2047 class_<SkPicture>("Picture")
2048 .smart_ptr<sk_sp<SkPicture>>("sk_sp<Picture>")
2049 .function("_makeShader", optional_override([](SkPicture& self,
2050 SkTileMode tmx, SkTileMode tmy, SkFilterMode mode,
2051 WASMPointerF32 mPtr, WASMPointerF32 rPtr) -> sk_sp<SkShader> {
2052 OptionalMatrix localMatrix(mPtr);
2053 SkRect* tileRect = reinterpret_cast<SkRect*>(rPtr);
2054 return self.makeShader(tmx, tmy, mode, mPtr ? &localMatrix : nullptr, tileRect);
2055 }), allow_raw_pointers())
2056 .function("_cullRect", optional_override([](SkPicture& self,
2057 WASMPointerF32 fPtr)->void {
2058 SkRect* output = reinterpret_cast<SkRect*>(fPtr);
2059 output[0] = self.cullRect();
2060 }))
2061 .function("approximateBytesUsed", &SkPicture::approximateBytesUsed)
2062 #ifdef CK_SERIALIZE_SKP
2063 // The serialized format of an SkPicture (informally called an "skp"), is not something
2064 // that clients should ever rely on. The format may change at anytime and no promises
2065 // are made for backwards or forward compatibility.
2066 .function("serialize", optional_override([](SkPicture& self) -> Uint8Array {
2067 // We want to make sure we always save the underlying data of the Typeface to the
2068 // SkPicture. By default, the data for "system" fonts is not saved, just an identifier
2069 // (e.g. the family name and style). We do not want the user to have to supply a
2070 // FontMgr with the correct fonts by name when deserializing, so we choose to always
2071 // serialize the underlying data. This makes the SKPs a bit bigger, but easier to use.
2072 SkSerialProcs sp;
2073 sp.fTypefaceProc = &alwaysSaveTypefaceBytes;
2074 sp.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
2075 return SkPngEncoder::Encode(nullptr, img, SkPngEncoder::Options{});
2076 };
2077
2078 sk_sp<SkData> data = self.serialize(&sp);
2079 if (!data) {
2080 return emscripten::val::null();
2081 }
2082 return toBytes(data);
2083 }), allow_raw_pointers())
2084 #endif
2085 ;
2086
2087 class_<SkShader>("Shader")
2088 .smart_ptr<sk_sp<SkShader>>("sk_sp<Shader>")
2089 .class_function("MakeBlend", select_overload<sk_sp<SkShader>(SkBlendMode, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Blend))
2090 .class_function("_MakeColor",
2091 optional_override([](WASMPointerF32 cPtr, sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
2092 return SkShaders::Color(ptrToSkColor4f(cPtr), colorSpace);
2093 })
2094 )
2095 .class_function("MakeFractalNoise", optional_override([](
2096 SkScalar baseFreqX, SkScalar baseFreqY,
2097 int numOctaves, SkScalar seed,
2098 int tileW, int tileH)->sk_sp<SkShader> {
2099 // if tileSize is empty (e.g. tileW <= 0 or tileH <= 0, it will be ignored.
2100 SkISize tileSize = SkISize::Make(tileW, tileH);
2101 return SkShaders::MakeFractalNoise(baseFreqX, baseFreqY, numOctaves, seed, &tileSize);
2102 }))
2103 // Here and in other gradient functions, cPtr is a pointer to an array of data
2104 // representing colors. whether this is an array of SkColor or SkColor4f is indicated
2105 // by the colorType argument. Only RGBA_8888 and RGBA_F32 are accepted.
2106 .class_function("_MakeLinearGradient", optional_override([](
2107 WASMPointerF32 fourFloatsPtr,
2108 WASMPointerF32 cPtr, SkColorType colorType,
2109 WASMPointerF32 pPtr,
2110 int count, SkTileMode mode, uint32_t flags,
2111 WASMPointerF32 mPtr,
2112 sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
2113 const SkPoint* points = reinterpret_cast<const SkPoint*>(fourFloatsPtr);
2114 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
2115 OptionalMatrix localMatrix(mPtr);
2116
2117 if (colorType == SkColorType::kRGBA_F32_SkColorType) {
2118 const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
2119 return SkGradientShader::MakeLinear(points, colors, colorSpace, positions, count,
2120 mode, flags,
2121 mPtr ? &localMatrix : nullptr);
2122 } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
2123 const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
2124 return SkGradientShader::MakeLinear(points, colors, positions, count,
2125 mode, flags,
2126 mPtr ? &localMatrix : nullptr);
2127 }
2128 SkDebugf("%d is not an accepted colorType\n", colorType);
2129 return nullptr;
2130 }), allow_raw_pointers())
2131 .class_function("_MakeRadialGradient", optional_override([](
2132 SkScalar cx, SkScalar cy, SkScalar radius,
2133 WASMPointerF32 cPtr, SkColorType colorType,
2134 WASMPointerF32 pPtr,
2135 int count, SkTileMode mode, uint32_t flags,
2136 WASMPointerF32 mPtr,
2137 sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
2138 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
2139 OptionalMatrix localMatrix(mPtr);
2140 if (colorType == SkColorType::kRGBA_F32_SkColorType) {
2141 const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
2142 return SkGradientShader::MakeRadial({cx, cy}, radius, colors, colorSpace,
2143 positions, count, mode, flags,
2144 mPtr ? &localMatrix : nullptr);
2145 } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
2146 const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
2147 return SkGradientShader::MakeRadial({cx, cy}, radius, colors, positions,
2148 count, mode, flags,
2149 mPtr ? &localMatrix : nullptr);
2150 }
2151 SkDebugf("%d is not an accepted colorType\n", colorType);
2152 return nullptr;
2153 }), allow_raw_pointers())
2154 .class_function("_MakeSweepGradient", optional_override([](SkScalar cx, SkScalar cy,
2155 WASMPointerF32 cPtr, SkColorType colorType,
2156 WASMPointerF32 pPtr,
2157 int count, SkTileMode mode,
2158 SkScalar startAngle, SkScalar endAngle,
2159 uint32_t flags,
2160 WASMPointerF32 mPtr,
2161 sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
2162 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
2163 OptionalMatrix localMatrix(mPtr);
2164 if (colorType == SkColorType::kRGBA_F32_SkColorType) {
2165 const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
2166 return SkGradientShader::MakeSweep(cx, cy, colors, colorSpace, positions, count,
2167 mode, startAngle, endAngle, flags,
2168 mPtr ? &localMatrix : nullptr);
2169 } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
2170 const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
2171 return SkGradientShader::MakeSweep(cx, cy, colors, positions, count,
2172 mode, startAngle, endAngle, flags,
2173 mPtr ? &localMatrix : nullptr);
2174 }
2175 SkDebugf("%d is not an accepted colorType\n", colorType);
2176 return nullptr;
2177 }), allow_raw_pointers())
2178 .class_function("MakeTurbulence", optional_override([](
2179 SkScalar baseFreqX, SkScalar baseFreqY,
2180 int numOctaves, SkScalar seed,
2181 int tileW, int tileH)->sk_sp<SkShader> {
2182 // if tileSize is empty (e.g. tileW <= 0 or tileH <= 0, it will be ignored.
2183 SkISize tileSize = SkISize::Make(tileW, tileH);
2184 return SkShaders::MakeTurbulence(baseFreqX, baseFreqY, numOctaves, seed, &tileSize);
2185 }))
2186 .class_function("_MakeTwoPointConicalGradient", optional_override([](
2187 WASMPointerF32 fourFloatsPtr,
2188 SkScalar startRadius, SkScalar endRadius,
2189 WASMPointerF32 cPtr, SkColorType colorType,
2190 WASMPointerF32 pPtr,
2191 int count, SkTileMode mode, uint32_t flags,
2192 WASMPointerF32 mPtr,
2193 sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
2194 const SkPoint* startAndEnd = reinterpret_cast<const SkPoint*>(fourFloatsPtr);
2195 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
2196 OptionalMatrix localMatrix(mPtr);
2197
2198 if (colorType == SkColorType::kRGBA_F32_SkColorType) {
2199 const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
2200 return SkGradientShader::MakeTwoPointConical(startAndEnd[0], startRadius,
2201 startAndEnd[1], endRadius,
2202 colors, colorSpace, positions, count, mode,
2203 flags,
2204 mPtr ? &localMatrix : nullptr);
2205 } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
2206 const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
2207 return SkGradientShader::MakeTwoPointConical(startAndEnd[0],
2208 startRadius,
2209 startAndEnd[1],
2210 endRadius,
2211 colors,
2212 positions,
2213 count,
2214 mode,
2215 flags,
2216 mPtr ? &localMatrix : nullptr);
2217 }
2218 SkDebugf("%d is not an accepted colorType\n", colorType);
2219 return nullptr;
2220 }), allow_raw_pointers());
2221
2222 #ifdef CK_INCLUDE_RUNTIME_EFFECT
2223 class_<SkSL::DebugTrace>("DebugTrace")
2224 .smart_ptr<sk_sp<SkSL::DebugTrace>>("sk_sp<DebugTrace>")
2225 .function("writeTrace", optional_override([](SkSL::DebugTrace& self) -> std::string {
2226 SkDynamicMemoryWStream wstream;
2227 self.writeTrace(&wstream);
2228 sk_sp<SkData> trace = wstream.detachAsData();
2229 return std::string(reinterpret_cast<const char*>(trace->bytes()), trace->size());
2230 }));
2231
2232 value_object<SkRuntimeEffect::TracedShader>("TracedShader")
2233 .field("shader", &SkRuntimeEffect::TracedShader::shader)
2234 .field("debugTrace", &SkRuntimeEffect::TracedShader::debugTrace);
2235
2236 class_<SkRuntimeEffect>("RuntimeEffect")
2237 .smart_ptr<sk_sp<SkRuntimeEffect>>("sk_sp<RuntimeEffect>")
2238 .class_function("_Make", optional_override([](std::string sksl,
2239 emscripten::val errHandler
2240 )->sk_sp<SkRuntimeEffect> {
2241 SkString s(sksl.c_str(), sksl.length());
2242 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(s);
2243 if (!effect) {
2244 errHandler.call<void>("onError", val(errorText.c_str()));
2245 return nullptr;
2246 }
2247 return effect;
2248 }))
2249 .class_function("_MakeForBlender", optional_override([](std::string sksl,
2250 emscripten::val errHandler
2251 )->sk_sp<SkRuntimeEffect> {
2252 SkString s(sksl.c_str(), sksl.length());
2253 auto [effect, errorText] = SkRuntimeEffect::MakeForBlender(s);
2254 if (!effect) {
2255 errHandler.call<void>("onError", val(errorText.c_str()));
2256 return nullptr;
2257 }
2258 return effect;
2259 }))
2260 .class_function("MakeTraced", optional_override([](
2261 sk_sp<SkShader> shader,
2262 int traceCoordX,
2263 int traceCoordY) -> SkRuntimeEffect::TracedShader {
2264 return SkRuntimeEffect::MakeTraced(shader, SkIPoint::Make(traceCoordX, traceCoordY));
2265 }))
2266 .function("_makeShader", optional_override([](SkRuntimeEffect& self,
2267 WASMPointerF32 fPtr,
2268 size_t fLen,
2269 bool shouldOwnUniforms,
2270 WASMPointerF32 mPtr)->sk_sp<SkShader> {
2271 void* uniformData = reinterpret_cast<void*>(fPtr);
2272 castUniforms(uniformData, fLen, self);
2273 sk_sp<SkData> uniforms;
2274 if (shouldOwnUniforms) {
2275 uniforms = SkData::MakeFromMalloc(uniformData, fLen);
2276 } else {
2277 uniforms = SkData::MakeWithoutCopy(uniformData, fLen);
2278 }
2279
2280 OptionalMatrix localMatrix(mPtr);
2281 return self.makeShader(uniforms, nullptr, 0, mPtr ? &localMatrix : nullptr);
2282 }))
2283 .function("_makeShaderWithChildren", optional_override([](SkRuntimeEffect& self,
2284 WASMPointerF32 fPtr,
2285 size_t fLen,
2286 bool shouldOwnUniforms,
2287 WASMPointerU32 cPtrs,
2288 size_t cLen,
2289 WASMPointerF32 mPtr)->sk_sp<SkShader> {
2290 void* uniformData = reinterpret_cast<void*>(fPtr);
2291 castUniforms(uniformData, fLen, self);
2292 sk_sp<SkData> uniforms;
2293 if (shouldOwnUniforms) {
2294 uniforms = SkData::MakeFromMalloc(uniformData, fLen);
2295 } else {
2296 uniforms = SkData::MakeWithoutCopy(uniformData, fLen);
2297 }
2298
2299 sk_sp<SkShader>* children = new sk_sp<SkShader>[cLen];
2300 SkShader** childrenPtrs = reinterpret_cast<SkShader**>(cPtrs);
2301 for (size_t i = 0; i < cLen; i++) {
2302 // This bare pointer was already part of an sk_sp (owned outside of here),
2303 // so we want to ref the new sk_sp so makeShader doesn't clean it up.
2304 children[i] = sk_ref_sp<SkShader>(childrenPtrs[i]);
2305 }
2306 OptionalMatrix localMatrix(mPtr);
2307 auto s = self.makeShader(uniforms, children, cLen, mPtr ? &localMatrix : nullptr);
2308 delete[] children;
2309 return s;
2310 }))
2311 .function("_makeBlender", optional_override([](SkRuntimeEffect& self,
2312 WASMPointerF32 fPtr,
2313 size_t fLen,
2314 bool shouldOwnUniforms)->sk_sp<SkBlender> {
2315 void* uniformData = reinterpret_cast<void*>(fPtr);
2316 castUniforms(uniformData, fLen, self);
2317 sk_sp<SkData> uniforms;
2318 if (shouldOwnUniforms) {
2319 uniforms = SkData::MakeFromMalloc(uniformData, fLen);
2320 } else {
2321 uniforms = SkData::MakeWithoutCopy(uniformData, fLen);
2322 }
2323
2324 return self.makeBlender(uniforms, {});
2325 }))
2326 .function("getUniformCount", optional_override([](SkRuntimeEffect& self)->int {
2327 return self.uniforms().size();
2328 }))
2329 .function("getUniformFloatCount", optional_override([](SkRuntimeEffect& self)->int {
2330 return self.uniformSize() / sizeof(float);
2331 }))
2332 .function("getUniformName", optional_override([](SkRuntimeEffect& self, int i)->JSString {
2333 auto it = self.uniforms().begin() + i;
2334 return emscripten::val(std::string(it->name).c_str());
2335 }))
2336 .function("getUniform", optional_override([](SkRuntimeEffect& self, int i)->RuntimeEffectUniform {
2337 auto it = self.uniforms().begin() + i;
2338 RuntimeEffectUniform su = fromUniform(*it);
2339 return su;
2340 }));
2341
2342 value_object<RuntimeEffectUniform>("RuntimeEffectUniform")
2343 .field("columns", &RuntimeEffectUniform::columns)
2344 .field("rows", &RuntimeEffectUniform::rows)
2345 .field("slot", &RuntimeEffectUniform::slot)
2346 .field("isInteger", &RuntimeEffectUniform::isInteger);
2347
2348 constant("rt_effect", true);
2349 #endif
2350
2351 class_<SkSurface>("Surface")
2352 .smart_ptr<sk_sp<SkSurface>>("sk_sp<Surface>")
2353 .class_function("_makeRasterDirect", optional_override([](const SimpleImageInfo ii,
2354 WASMPointerU8 pPtr,
2355 size_t rowBytes)->sk_sp<SkSurface> {
2356 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
2357 SkImageInfo imageInfo = toSkImageInfo(ii);
2358 return SkSurfaces::WrapPixels(imageInfo, pixels, rowBytes, nullptr);
2359 }), allow_raw_pointers())
2360 .function("_flush", optional_override([](SkSurface& self) {
2361 #ifdef CK_ENABLE_WEBGL
2362 skgpu::ganesh::FlushAndSubmit(&self);
2363 #endif
2364 }))
2365 .function("_getCanvas", &SkSurface::getCanvas, allow_raw_pointers())
2366 .function("imageInfo", optional_override([](SkSurface& self)->SimpleImageInfo {
2367 const auto& ii = self.imageInfo();
2368 return {ii.width(), ii.height(), ii.colorType(), ii.alphaType(), ii.refColorSpace()};
2369 }))
2370 .function("height", &SkSurface::height)
2371 #ifdef CK_ENABLE_WEBGL
2372 .function("_makeImageFromTexture", optional_override([](SkSurface& self,
2373 uint32_t webglHandle, uint32_t texHandle,
2374 SimpleImageInfo ii)->sk_sp<SkImage> {
2375 auto releaseCtx = new TextureReleaseContext{webglHandle, texHandle};
2376 GrGLTextureInfo gti = {GR_GL_TEXTURE_2D, texHandle,
2377 GR_GL_RGBA8}; // TODO(kjlubick) look at ii for this
2378 auto gbt = GrBackendTextures::MakeGL(ii.width, ii.height, skgpu::Mipmapped::kNo, gti);
2379 auto dContext = GrAsDirectContext(self.getCanvas()->recordingContext());
2380
2381 return SkImages::BorrowTextureFrom(dContext,
2382 gbt,
2383 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
2384 ii.colorType,
2385 ii.alphaType,
2386 ii.colorSpace,
2387 deleteJSTexture,
2388 releaseCtx);
2389 }))
2390 #endif // CK_ENABLE_WEBGL
2391 #ifdef CK_ENABLE_WEBGPU
2392 .function("_replaceBackendTexture", optional_override([](SkSurface& self,
2393 uint32_t texHandle, uint32_t texFormat,
2394 int width, int height) {
2395 return ReplaceBackendTexture(self, texHandle, texFormat, width, height);
2396 }))
2397 #endif // CK_ENABLE_WEBGPU
2398 .function("_makeImageSnapshot", optional_override([](SkSurface& self, WASMPointerU32 iPtr)->sk_sp<SkImage> {
2399 SkIRect* bounds = reinterpret_cast<SkIRect*>(iPtr);
2400 if (!bounds) {
2401 return self.makeImageSnapshot();
2402 }
2403 return self.makeImageSnapshot(*bounds);
2404 }))
2405 .function("_makeSurface", optional_override([](SkSurface& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
2406 return self.makeSurface(toSkImageInfo(sii));
2407 }), allow_raw_pointers())
2408 #ifdef ENABLE_GPU
2409 .function("reportBackendTypeIsGPU", optional_override([](SkSurface& self) -> bool {
2410 return self.getCanvas()->recordingContext() != nullptr;
2411 }))
2412 .function("sampleCnt", optional_override([](SkSurface& self)->int {
2413 auto backendRT = SkSurfaces::GetBackendRenderTarget(
2414 &self, SkSurfaces::BackendHandleAccess::kFlushRead);
2415 return (backendRT.isValid()) ? backendRT.sampleCnt() : 0;
2416 }))
2417 .function("_resetContext",optional_override([](SkSurface& self)->void {
2418 GrAsDirectContext(self.recordingContext())->resetContext(kTextureBinding_GrGLBackendState);
2419 }))
2420 #else
2421 .function("reportBackendTypeIsGPU", optional_override([](SkSurface& self) -> bool {
2422 return false;
2423 }))
2424 #endif
2425 .function("width", &SkSurface::width);
2426
2427 #ifndef CK_NO_FONTS
2428 class_<SkTextBlob>("TextBlob")
2429 .smart_ptr<sk_sp<SkTextBlob>>("sk_sp<TextBlob>")
2430 .class_function("_MakeFromRSXform", optional_override([](WASMPointerU8 sptr,
2431 size_t strBtyes,
2432 WASMPointerF32 xptr,
2433 const SkFont& font)->sk_sp<SkTextBlob> {
2434 const char* str = reinterpret_cast<const char*>(sptr);
2435 const SkRSXform* xforms = reinterpret_cast<const SkRSXform*>(xptr);
2436
2437 return SkTextBlob::MakeFromRSXform(str, strBtyes, xforms, font, SkTextEncoding::kUTF8);
2438 }), allow_raw_pointers())
2439 .class_function("_MakeFromRSXformGlyphs", optional_override([](WASMPointerU16 gPtr,
2440 size_t byteLen,
2441 WASMPointerF32 xptr,
2442 const SkFont& font)->sk_sp<SkTextBlob> {
2443 const SkGlyphID* glyphs = reinterpret_cast<const SkGlyphID*>(gPtr);
2444 const SkRSXform* xforms = reinterpret_cast<const SkRSXform*>(xptr);
2445
2446 return SkTextBlob::MakeFromRSXform(glyphs, byteLen, xforms, font, SkTextEncoding::kGlyphID);
2447 }), allow_raw_pointers())
2448 .class_function("_MakeFromText", optional_override([](WASMPointerU8 sptr,
2449 size_t len, const SkFont& font)->sk_sp<SkTextBlob> {
2450 const char* str = reinterpret_cast<const char*>(sptr);
2451 return SkTextBlob::MakeFromText(str, len, font, SkTextEncoding::kUTF8);
2452 }), allow_raw_pointers())
2453 .class_function("_MakeFromGlyphs", optional_override([](WASMPointerU16 gPtr,
2454 size_t byteLen, const SkFont& font)->sk_sp<SkTextBlob> {
2455 const SkGlyphID* glyphs = reinterpret_cast<const SkGlyphID*>(gPtr);
2456 return SkTextBlob::MakeFromText(glyphs, byteLen, font, SkTextEncoding::kGlyphID);
2457 }), allow_raw_pointers());
2458
2459 class_<SkTypeface>("Typeface")
2460 .smart_ptr<sk_sp<SkTypeface>>("sk_sp<Typeface>")
2461 .class_function("GetDefault", optional_override([]()->sk_sp<SkTypeface> {
2462 #if defined(CK_EMBED_FONT)
2463 if (SK_EMBEDDED_FONTS.count == 0) {
2464 return nullptr;
2465 }
2466 static sk_sp<SkTypeface> default_face;
2467 static SkOnce once;
2468 once([] {
2469 const SkEmbeddedResource& fontEntry = SK_EMBEDDED_FONTS.entries[0];
2470 auto stream = std::make_unique<SkMemoryStream>(fontEntry.data, fontEntry.size, false);
2471 default_face = SkTypeface_FreeType::MakeFromStream(std::move(stream), SkFontArguments());
2472 });
2473 return default_face;
2474 #else
2475 return nullptr;
2476 #endif
2477 }), allow_raw_pointers())
2478 .class_function("_MakeTypefaceFromData", optional_override([](
2479 WASMPointerU8 fPtr, int flen)->sk_sp<SkTypeface> {
2480 uint8_t* font = reinterpret_cast<uint8_t*>(fPtr);
2481 std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream());
2482 stream->setMemoryOwned(font, flen);
2483 return SkTypeface_FreeType::MakeFromStream(std::move(stream), SkFontArguments());
2484 }), allow_raw_pointers())
2485 .function("_getGlyphIDs", optional_override([](SkTypeface& self, WASMPointerU8 sptr,
2486 size_t strLen, size_t expectedCodePoints,
2487 WASMPointerU16 iPtr) -> int {
2488 char* str = reinterpret_cast<char*>(sptr);
2489 SkGlyphID* glyphIDs = reinterpret_cast<SkGlyphID*>(iPtr);
2490
2491 int actualCodePoints = self.textToGlyphs(str, strLen, SkTextEncoding::kUTF8,
2492 glyphIDs, expectedCodePoints);
2493 return actualCodePoints;
2494 }));
2495 #endif
2496
2497 class_<SkVertices>("Vertices")
2498 .smart_ptr<sk_sp<SkVertices>>("sk_sp<Vertices>")
2499 .function("_bounds", optional_override([](SkVertices& self,
2500 WASMPointerF32 fPtr)->void {
2501 SkRect* output = reinterpret_cast<SkRect*>(fPtr);
2502 output[0] = self.bounds();
2503 }))
2504 .function("uniqueID", &SkVertices::uniqueID);
2505
2506 // Not intended to be called directly by clients
2507 class_<SkVertices::Builder>("_VerticesBuilder")
2508 .constructor<SkVertices::VertexMode, int, int, uint32_t>()
2509 .function("colors", optional_override([](SkVertices::Builder& self)->WASMPointerF32{
2510 // Emscripten won't let us return bare pointers, but we can return ints just fine.
2511 return reinterpret_cast<WASMPointerF32>(self.colors());
2512 }))
2513 .function("detach", &SkVertices::Builder::detach)
2514 .function("indices", optional_override([](SkVertices::Builder& self)->WASMPointerU16{
2515 // Emscripten won't let us return bare pointers, but we can return ints just fine.
2516 return reinterpret_cast<WASMPointerU16>(self.indices());
2517 }))
2518 .function("positions", optional_override([](SkVertices::Builder& self)->WASMPointerF32{
2519 // Emscripten won't let us return bare pointers, but we can return ints just fine.
2520 return reinterpret_cast<WASMPointerF32>(self.positions());
2521 }))
2522 .function("texCoords", optional_override([](SkVertices::Builder& self)->WASMPointerF32{
2523 // Emscripten won't let us return bare pointers, but we can return ints just fine.
2524 return reinterpret_cast<WASMPointerF32>(self.texCoords());
2525 }));
2526
2527 enum_<SkAlphaType>("AlphaType")
2528 .value("Opaque", SkAlphaType::kOpaque_SkAlphaType)
2529 .value("Premul", SkAlphaType::kPremul_SkAlphaType)
2530 .value("Unpremul", SkAlphaType::kUnpremul_SkAlphaType);
2531
2532 enum_<SkBlendMode>("BlendMode")
2533 .value("Clear", SkBlendMode::kClear)
2534 .value("Src", SkBlendMode::kSrc)
2535 .value("Dst", SkBlendMode::kDst)
2536 .value("SrcOver", SkBlendMode::kSrcOver)
2537 .value("DstOver", SkBlendMode::kDstOver)
2538 .value("SrcIn", SkBlendMode::kSrcIn)
2539 .value("DstIn", SkBlendMode::kDstIn)
2540 .value("SrcOut", SkBlendMode::kSrcOut)
2541 .value("DstOut", SkBlendMode::kDstOut)
2542 .value("SrcATop", SkBlendMode::kSrcATop)
2543 .value("DstATop", SkBlendMode::kDstATop)
2544 .value("Xor", SkBlendMode::kXor)
2545 .value("Plus", SkBlendMode::kPlus)
2546 .value("Modulate", SkBlendMode::kModulate)
2547 .value("Screen", SkBlendMode::kScreen)
2548 .value("Overlay", SkBlendMode::kOverlay)
2549 .value("Darken", SkBlendMode::kDarken)
2550 .value("Lighten", SkBlendMode::kLighten)
2551 .value("ColorDodge", SkBlendMode::kColorDodge)
2552 .value("ColorBurn", SkBlendMode::kColorBurn)
2553 .value("HardLight", SkBlendMode::kHardLight)
2554 .value("SoftLight", SkBlendMode::kSoftLight)
2555 .value("Difference", SkBlendMode::kDifference)
2556 .value("Exclusion", SkBlendMode::kExclusion)
2557 .value("Multiply", SkBlendMode::kMultiply)
2558 .value("Hue", SkBlendMode::kHue)
2559 .value("Saturation", SkBlendMode::kSaturation)
2560 .value("Color", SkBlendMode::kColor)
2561 .value("Luminosity", SkBlendMode::kLuminosity);
2562
2563 enum_<SkBlurStyle>("BlurStyle")
2564 .value("Normal", SkBlurStyle::kNormal_SkBlurStyle)
2565 .value("Solid", SkBlurStyle::kSolid_SkBlurStyle)
2566 .value("Outer", SkBlurStyle::kOuter_SkBlurStyle)
2567 .value("Inner", SkBlurStyle::kInner_SkBlurStyle);
2568
2569 enum_<SkClipOp>("ClipOp")
2570 .value("Difference", SkClipOp::kDifference)
2571 .value("Intersect", SkClipOp::kIntersect);
2572
2573 enum_<SkColorChannel>("ColorChannel")
2574 .value("Red", SkColorChannel::kR)
2575 .value("Green", SkColorChannel::kG)
2576 .value("Blue", SkColorChannel::kB)
2577 .value("Alpha", SkColorChannel::kA);
2578
2579 enum_<SkColorType>("ColorType")
2580 .value("Alpha_8", SkColorType::kAlpha_8_SkColorType)
2581 .value("RGB_565", SkColorType::kRGB_565_SkColorType)
2582 .value("RGBA_8888", SkColorType::kRGBA_8888_SkColorType)
2583 .value("BGRA_8888", SkColorType::kBGRA_8888_SkColorType)
2584 .value("RGBA_1010102", SkColorType::kRGBA_1010102_SkColorType)
2585 .value("RGB_101010x", SkColorType::kRGB_101010x_SkColorType)
2586 .value("Gray_8", SkColorType::kGray_8_SkColorType)
2587 .value("RGBA_F16", SkColorType::kRGBA_F16_SkColorType)
2588 .value("RGB_F16F16F16x", SkColorType::kRGB_F16F16F16x_SkColorType)
2589 .value("RGBA_F32", SkColorType::kRGBA_F32_SkColorType);
2590
2591 enum_<SkPathFillType>("FillType")
2592 .value("Winding", SkPathFillType::kWinding)
2593 .value("EvenOdd", SkPathFillType::kEvenOdd);
2594
2595 enum_<SkFilterMode>("FilterMode")
2596 .value("Nearest", SkFilterMode::kNearest)
2597 .value("Linear", SkFilterMode::kLinear);
2598
2599 // Only used to control the encode function.
2600 // TODO(kjlubick): compile these out when the appropriate encoder is disabled.
2601 enum_<SkEncodedImageFormat>("ImageFormat")
2602 .value("PNG", SkEncodedImageFormat::kPNG)
2603 .value("JPEG", SkEncodedImageFormat::kJPEG)
2604 .value("WEBP", SkEncodedImageFormat::kWEBP);
2605
2606 enum_<SkMipmapMode>("MipmapMode")
2607 .value("None", SkMipmapMode::kNone)
2608 .value("Nearest", SkMipmapMode::kNearest)
2609 .value("Linear", SkMipmapMode::kLinear);
2610
2611 enum_<SkPaint::Style>("PaintStyle")
2612 .value("Fill", SkPaint::Style::kFill_Style)
2613 .value("Stroke", SkPaint::Style::kStroke_Style);
2614
2615 enum_<SkPath1DPathEffect::Style>("Path1DEffect")
2616 .value("Translate", SkPath1DPathEffect::Style::kTranslate_Style)
2617 .value("Rotate", SkPath1DPathEffect::Style::kRotate_Style)
2618 .value("Morph", SkPath1DPathEffect::Style::kMorph_Style);
2619
2620 #ifdef CK_INCLUDE_PATHOPS
2621 enum_<SkPathOp>("PathOp")
2622 .value("Difference", SkPathOp::kDifference_SkPathOp)
2623 .value("Intersect", SkPathOp::kIntersect_SkPathOp)
2624 .value("Union", SkPathOp::kUnion_SkPathOp)
2625 .value("XOR", SkPathOp::kXOR_SkPathOp)
2626 .value("ReverseDifference", SkPathOp::kReverseDifference_SkPathOp);
2627 #endif
2628
2629 enum_<SkCanvas::PointMode>("PointMode")
2630 .value("Points", SkCanvas::PointMode::kPoints_PointMode)
2631 .value("Lines", SkCanvas::PointMode::kLines_PointMode)
2632 .value("Polygon", SkCanvas::PointMode::kPolygon_PointMode);
2633
2634 enum_<SkPaint::Cap>("StrokeCap")
2635 .value("Butt", SkPaint::Cap::kButt_Cap)
2636 .value("Round", SkPaint::Cap::kRound_Cap)
2637 .value("Square", SkPaint::Cap::kSquare_Cap);
2638
2639 enum_<SkPaint::Join>("StrokeJoin")
2640 .value("Miter", SkPaint::Join::kMiter_Join)
2641 .value("Round", SkPaint::Join::kRound_Join)
2642 .value("Bevel", SkPaint::Join::kBevel_Join);
2643
2644 #ifndef CK_NO_FONTS
2645 enum_<SkFontHinting>("FontHinting")
2646 .value("None", SkFontHinting::kNone)
2647 .value("Slight", SkFontHinting::kSlight)
2648 .value("Normal", SkFontHinting::kNormal)
2649 .value("Full", SkFontHinting::kFull);
2650
2651 enum_<SkFont::Edging>("FontEdging")
2652 #ifndef CK_NO_ALIAS_FONT
2653 .value("Alias", SkFont::Edging::kAlias)
2654 #endif
2655 .value("AntiAlias", SkFont::Edging::kAntiAlias)
2656 .value("SubpixelAntiAlias", SkFont::Edging::kSubpixelAntiAlias);
2657 #endif
2658
2659 enum_<SkTileMode>("TileMode")
2660 .value("Clamp", SkTileMode::kClamp)
2661 .value("Repeat", SkTileMode::kRepeat)
2662 .value("Mirror", SkTileMode::kMirror)
2663 .value("Decal", SkTileMode::kDecal);
2664
2665 enum_<SkVertices::VertexMode>("VertexMode")
2666 .value("Triangles", SkVertices::VertexMode::kTriangles_VertexMode)
2667 .value("TrianglesStrip", SkVertices::VertexMode::kTriangleStrip_VertexMode)
2668 .value("TriangleFan", SkVertices::VertexMode::kTriangleFan_VertexMode);
2669
2670 // A value object is much simpler than a class - it is returned as a JS
2671 // object and does not require delete().
2672 // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
2673
2674 value_object<SimpleImageInfo>("ImageInfo")
2675 .field("width", &SimpleImageInfo::width)
2676 .field("height", &SimpleImageInfo::height)
2677 .field("colorType", &SimpleImageInfo::colorType)
2678 .field("alphaType", &SimpleImageInfo::alphaType)
2679 .field("colorSpace", &SimpleImageInfo::colorSpace);
2680
2681 value_object<StrokeOpts>("StrokeOpts")
2682 .field("width", &StrokeOpts::width)
2683 .field("miter_limit", &StrokeOpts::miter_limit)
2684 .field("join", &StrokeOpts::join)
2685 .field("cap", &StrokeOpts::cap)
2686 .field("precision", &StrokeOpts::precision);
2687
2688 constant("MOVE_VERB", MOVE);
2689 constant("LINE_VERB", LINE);
2690 constant("QUAD_VERB", QUAD);
2691 constant("CONIC_VERB", CONIC);
2692 constant("CUBIC_VERB", CUBIC);
2693 constant("CLOSE_VERB", CLOSE);
2694
2695 constant("SaveLayerInitWithPrevious", (int)SkCanvas::SaveLayerFlagsSet::kInitWithPrevious_SaveLayerFlag);
2696 constant("SaveLayerF16ColorType", (int)SkCanvas::SaveLayerFlagsSet::kF16ColorType);
2697
2698 constant("ShadowTransparentOccluder", (int)SkShadowFlags::kTransparentOccluder_ShadowFlag);
2699 constant("ShadowGeometricOnly", (int)SkShadowFlags::kGeometricOnly_ShadowFlag);
2700 constant("ShadowDirectionalLight", (int)SkShadowFlags::kDirectionalLight_ShadowFlag);
2701
2702 #ifdef CK_INCLUDE_PARAGRAPH
2703 constant("_GlyphRunFlags_isWhiteSpace", (int)skia::textlayout::Paragraph::kWhiteSpace_VisitorFlag);
2704 #endif
2705 }
2706