1 /*
2 * Copyright 2019 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 "SkDebugCanvas.h"
9 #include "SkPicture.h"
10 #include "SkSurface.h"
11 #include <emscripten.h>
12 #include <emscripten/bind.h>
13
14 using JSColor = int32_t;
15
16 struct SimpleImageInfo {
17 int width;
18 int height;
19 SkColorType colorType;
20 SkAlphaType alphaType;
21 };
22
toSkImageInfo(const SimpleImageInfo & sii)23 SkImageInfo toSkImageInfo(const SimpleImageInfo& sii) {
24 return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType);
25 }
26
27 class SkpDebugPlayer {
28 public:
SkpDebugPlayer()29 SkpDebugPlayer() {}
30
31
32 /* loadSkp deserializes a skp file that has been copied into the shared WASM memory.
33 * cptr - a pointer to the data to deserialize.
34 * length - length of the data in bytes.
35 * The caller must allocate the memory with M._malloc where M is the wasm module in javascript
36 * and copy the data into M.buffer at the pointer returned by malloc.
37 *
38 * uintptr_t is used here because emscripten will not allow binding of functions with pointers
39 * to primitive types. We can instead pass a number and cast it to whatever kind of
40 * pointer we're expecting.
41 */
loadSkp(uintptr_t cptr,int length)42 void loadSkp(uintptr_t cptr, int length) {
43 const auto* data = reinterpret_cast<const uint8_t*>(cptr);
44 // todo: pass in bounds
45 fDebugCanvas.reset(new SkDebugCanvas(720, 1280));
46 SkDebugf("SkDebugCanvas created.\n");
47 // note overloaded = operator that actually does a move
48 fPicture = SkPicture::MakeFromData(data, length);
49 if (!fPicture) {
50 SkDebugf("Unable to parse SKP file.\n");
51 return;
52 }
53 SkDebugf("Parsed SKP file.\n");
54 // Only draw picture to the debug canvas once.
55 fDebugCanvas->drawPicture(fPicture);
56 SkDebugf("Added picture with %d commands.\n", fDebugCanvas->getSize());
57 }
58
59 /* drawTo asks the debug canvas to draw from the beginning of the picture
60 * to the given command and flush the canvas.
61 */
drawTo(SkSurface * surface,int32_t index)62 void drawTo(SkSurface* surface, int32_t index) {
63 fDebugCanvas->drawTo(surface->getCanvas(), index);
64 surface->getCanvas()->flush();
65 }
66
67 private:
68 // admission of ignorance - don't know when to use unique pointer or sk_sp
69 std::unique_ptr<SkDebugCanvas> fDebugCanvas;
70 sk_sp<SkPicture> fPicture;
71 };
72
73 using namespace emscripten;
EMSCRIPTEN_BINDINGS(my_module)74 EMSCRIPTEN_BINDINGS(my_module) {
75
76 // The main class that the JavaScript in index.html uses
77 class_<SkpDebugPlayer>("SkpDebugPlayer")
78 .constructor<>()
79 .function("loadSkp", &SkpDebugPlayer::loadSkp, allow_raw_pointers())
80 .function("drawTo", &SkpDebugPlayer::drawTo, allow_raw_pointers());
81
82 // Symbols needed by cpu.js to perform surface creation and flushing.
83 enum_<SkColorType>("ColorType")
84 .value("RGBA_8888", SkColorType::kRGBA_8888_SkColorType);
85 enum_<SkAlphaType>("AlphaType")
86 .value("Unpremul", SkAlphaType::kUnpremul_SkAlphaType);
87 value_object<SimpleImageInfo>("SkImageInfo")
88 .field("width", &SimpleImageInfo::width)
89 .field("height", &SimpleImageInfo::height)
90 .field("colorType", &SimpleImageInfo::colorType)
91 .field("alphaType", &SimpleImageInfo::alphaType);
92 constant("TRANSPARENT", (JSColor) SK_ColorTRANSPARENT);
93 function("_getRasterDirectSurface", optional_override([](const SimpleImageInfo ii,
94 uintptr_t /* uint8_t* */ pPtr,
95 size_t rowBytes)->sk_sp<SkSurface> {
96 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
97 SkImageInfo imageInfo = toSkImageInfo(ii);
98 return SkSurface::MakeRasterDirect(imageInfo, pixels, rowBytes, nullptr);
99 }), allow_raw_pointers());
100 class_<SkSurface>("SkSurface")
101 .smart_ptr<sk_sp<SkSurface>>("sk_sp<SkSurface>")
102 .function("width", &SkSurface::width)
103 .function("height", &SkSurface::height)
104 .function("_flush", select_overload<void()>(&SkSurface::flush))
105 .function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers());
106 class_<SkCanvas>("SkCanvas")
107 .function("clear", optional_override([](SkCanvas& self, JSColor color)->void {
108 // JS side gives us a signed int instead of an unsigned int for color
109 // Add a optional_override to change it out.
110 self.clear(SkColor(color));
111 }));
112 }
113