1 /*
2 * This file defines SkpDebugPlayer, a class which loads a SKP or MSKP file and draws it
3 * to an SkSurface with annotation, and detailed playback controls. It holds as many DebugCanvases
4 * as there are frames in the file.
5 *
6 * It also defines emscripten bindings for SkpDebugPlayer and other classes necessary to us it.
7 *
8 * Copyright 2019 Google LLC
9 *
10 * Use of this source code is governed by a BSD-style license that can be
11 * found in the LICENSE file.
12 */
13
14 #include "include/core/SkPicture.h"
15 #include "include/core/SkString.h"
16 #include "include/core/SkSurface.h"
17 #include "include/utils/SkBase64.h"
18 #include "src/core/SkPicturePriv.h"
19 #include "src/utils/SkJSONWriter.h"
20 #include "src/utils/SkMultiPictureDocument.h"
21 #include "tools/SkSharingProc.h"
22 #include "tools/UrlDataManager.h"
23 #include "tools/debugger/DebugCanvas.h"
24 #include "tools/debugger/DebugLayerManager.h"
25
26 #include <memory>
27 #include <string>
28 #include <string_view>
29 #include <vector>
30 #include <map>
31 #include <emscripten.h>
32 #include <emscripten/bind.h>
33
34 #ifdef SK_GL
35 #include "include/gpu/GrBackendSurface.h"
36 #include "include/gpu/GrDirectContext.h"
37 #include "include/gpu/gl/GrGLInterface.h"
38 #include "include/gpu/gl/GrGLTypes.h"
39
40 #include <GL/gl.h>
41 #include <emscripten/html5.h>
42 #endif
43
44 using JSColor = int32_t;
45 using Uint8Array = emscripten::val;
46 using JSArray = emscripten::val;
47 using JSObject = emscripten::val;
48
49 // file signature for SkMultiPictureDocument
50 // TODO(nifong): make public and include from SkMultiPictureDocument.h
51 static constexpr char kMultiMagic[] = "Skia Multi-Picture Doc\n\n";
52
53 struct SimpleImageInfo {
54 int width;
55 int height;
56 SkColorType colorType;
57 SkAlphaType alphaType;
58 };
59
toSkImageInfo(const SimpleImageInfo & sii)60 SkImageInfo toSkImageInfo(const SimpleImageInfo& sii) {
61 return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType);
62 }
63
toSimpleImageInfo(const SkImageInfo & ii)64 SimpleImageInfo toSimpleImageInfo(const SkImageInfo& ii) {
65 return (SimpleImageInfo){ii.width(), ii.height(), ii.colorType(), ii.alphaType()};
66 }
67
MinVersion()68 uint32_t MinVersion() { return SkPicturePriv::kMin_Version; }
69
70 class SkpDebugPlayer {
71 public:
SkpDebugPlayer()72 SkpDebugPlayer() :
73 udm(UrlDataManager(SkString("/data"))){}
74
75 /* loadSkp deserializes a skp file that has been copied into the shared WASM memory.
76 * cptr - a pointer to the data to deserialize.
77 * length - length of the data in bytes.
78 * The caller must allocate the memory with M._malloc where M is the wasm module in javascript
79 * and copy the data into M.buffer at the pointer returned by malloc.
80 *
81 * uintptr_t is used here because emscripten will not allow binding of functions with pointers
82 * to primitive types. We can instead pass a number and cast it to whatever kind of
83 * pointer we're expecting.
84 *
85 * Returns an error string which is populated in the case that the file cannot be read.
86 */
loadSkp(uintptr_t cptr,int length)87 std::string loadSkp(uintptr_t cptr, int length) {
88 const uint8_t* data = reinterpret_cast<const uint8_t*>(cptr);
89 // Both traditional and multi-frame skp files have a magic word
90 SkMemoryStream stream(data, length);
91 SkDebugf("make stream at %p, with %d bytes\n",data, length);
92 const bool isMulti = memcmp(data, kMultiMagic, sizeof(kMultiMagic) - 1) == 0;
93
94
95 if (isMulti) {
96 SkDebugf("Try reading as a multi-frame skp\n");
97 const auto& error = loadMultiFrame(&stream);
98 if (!error.empty()) { return error; }
99 } else {
100 SkDebugf("Try reading as single-frame skp\n");
101 // TODO(nifong): Rely on SkPicture's return errors once it provides some.
102 frames.push_back(loadSingleFrame(&stream));
103 }
104 return "";
105 }
106
107 /* drawTo asks the debug canvas to draw from the beginning of the picture
108 * to the given command and flush the canvas.
109 */
drawTo(SkSurface * surface,int32_t index)110 void drawTo(SkSurface* surface, int32_t index) {
111 // Set the command within the frame or layer event being drawn.
112 if (fInspectedLayer >= 0) {
113 fLayerManager->setCommand(fInspectedLayer, fp, index);
114 } else {
115 index = constrainFrameCommand(index);
116 }
117
118 auto* canvas = surface->getCanvas();
119 canvas->clear(SK_ColorTRANSPARENT);
120 if (fInspectedLayer >= 0) {
121 // when it's a layer event we're viewing, we use the layer manager to render it.
122 fLayerManager->drawLayerEventTo(surface, fInspectedLayer, fp);
123 } else {
124 // otherwise, its a frame at the top level.
125 frames[fp]->drawTo(surface->getCanvas(), index);
126 }
127 surface->flush();
128 }
129
130 // Draws to the end of the current frame.
draw(SkSurface * surface)131 void draw(SkSurface* surface) {
132 auto* canvas = surface->getCanvas();
133 canvas->clear(SK_ColorTRANSPARENT);
134 frames[fp]->draw(surface->getCanvas());
135 surface->getCanvas()->flush();
136 }
137
138 // Gets the bounds for the given frame
139 // (or layer update, assuming there is one at that frame for fInspectedLayer)
getBoundsForFrame(int32_t frame)140 const SkIRect getBoundsForFrame(int32_t frame) {
141 if (fInspectedLayer < 0) {
142 return fBoundsArray[frame];
143 }
144 auto summary = fLayerManager->event(fInspectedLayer, fp);
145 return SkIRect::MakeWH(summary.layerWidth, summary.layerHeight);
146 }
147
148 // Gets the bounds for the current frame
getBounds()149 const SkIRect getBounds() {
150 return getBoundsForFrame(fp);
151 }
152
153 // returns the debugcanvas of the current frame, or the current draw event when inspecting
154 // a layer.
visibleCanvas()155 DebugCanvas* visibleCanvas() {
156 if (fInspectedLayer >=0) {
157 return fLayerManager->getEventDebugCanvas(fInspectedLayer, fp);
158 } else {
159 return frames[fp].get();
160 }
161 }
162
163 // The following three operations apply to every debugcanvas because they are overdraw features.
164 // There is only one toggle for them on the app, they are global settings.
165 // However, there's not a simple way to make the debugcanvases pull settings from a central
166 // location so we set it on all of them at once.
setOverdrawVis(bool on)167 void setOverdrawVis(bool on) {
168 for (int i=0; i < frames.size(); i++) {
169 frames[i]->setOverdrawViz(on);
170 }
171 fLayerManager->setOverdrawViz(on);
172 }
setGpuOpBounds(bool on)173 void setGpuOpBounds(bool on) {
174 for (int i=0; i < frames.size(); i++) {
175 frames[i]->setDrawGpuOpBounds(on);
176 }
177 fLayerManager->setDrawGpuOpBounds(on);
178 }
setClipVizColor(JSColor color)179 void setClipVizColor(JSColor color) {
180 for (int i=0; i < frames.size(); i++) {
181 frames[i]->setClipVizColor(SkColor(color));
182 }
183 fLayerManager->setClipVizColor(SkColor(color));
184 }
setAndroidClipViz(bool on)185 void setAndroidClipViz(bool on) {
186 for (int i=0; i < frames.size(); i++) {
187 frames[i]->setAndroidClipViz(on);
188 }
189 // doesn't matter in layers
190 }
setOriginVisible(bool on)191 void setOriginVisible(bool on) {
192 for (int i=0; i < frames.size(); i++) {
193 frames[i]->setOriginVisible(on);
194 }
195 }
196 // The two operations below only apply to the current frame, because they concern the command
197 // list, which is unique to each frame.
deleteCommand(int index)198 void deleteCommand(int index) {
199 visibleCanvas()->deleteDrawCommandAt(index);
200 }
setCommandVisibility(int index,bool visible)201 void setCommandVisibility(int index, bool visible) {
202 visibleCanvas()->toggleCommand(index, visible);
203 }
getSize() const204 int getSize() const {
205 if (fInspectedLayer >=0) {
206 return fLayerManager->event(fInspectedLayer, fp).commandCount;
207 } else {
208 return frames[fp]->getSize();
209 }
210 }
getFrameCount() const211 int getFrameCount() const {
212 return frames.size();
213 }
214
215 // Return the command list in JSON representation as a string
jsonCommandList(sk_sp<SkSurface> surface)216 std::string jsonCommandList(sk_sp<SkSurface> surface) {
217 SkDynamicMemoryWStream stream;
218 SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast);
219 writer.beginObject(); // root
220 visibleCanvas()->toJSON(writer, udm, surface->getCanvas());
221 writer.endObject(); // root
222 writer.flush();
223 auto skdata = stream.detachAsData();
224 // Convert skdata to string_view, which accepts a length
225 std::string_view data_view(reinterpret_cast<const char*>(skdata->data()), skdata->size());
226 // and string_view to string, which emscripten understands.
227 return std::string(data_view);
228 }
229
230 // Gets the clip and matrix of the last command drawn
lastCommandInfo()231 std::string lastCommandInfo() {
232 SkM44 vm = visibleCanvas()->getCurrentMatrix();
233 SkIRect clip = visibleCanvas()->getCurrentClip();
234
235 SkDynamicMemoryWStream stream;
236 SkJSONWriter writer(&stream, SkJSONWriter::Mode::kFast);
237 writer.beginObject(); // root
238
239 writer.appendName("ViewMatrix");
240 DrawCommand::MakeJsonMatrix44(writer, vm);
241 writer.appendName("ClipRect");
242 DrawCommand::MakeJsonIRect(writer, clip);
243
244 writer.endObject(); // root
245 writer.flush();
246 auto skdata = stream.detachAsData();
247 // Convert skdata to string_view, which accepts a length
248 std::string_view data_view(reinterpret_cast<const char*>(skdata->data()), skdata->size());
249 // and string_view to string, which emscripten understands.
250 return std::string(data_view);
251 }
252
changeFrame(int index)253 void changeFrame(int index) {
254 fp = index;
255 }
256
257 // Return the png file at the requested index in
258 // the skp file's vector of shared images. this is the set of images referred to by the
259 // filenames like "\\1" in DrawImage commands.
260 // Return type is the PNG data as a base64 encoded string with prepended URI.
getImageResource(int index)261 std::string getImageResource(int index) {
262 sk_sp<SkData> pngData = fImages[index]->encodeToData();
263 size_t len = SkBase64::Encode(pngData->data(), pngData->size(), nullptr);
264 SkString dst;
265 dst.resize(len);
266 SkBase64::Encode(pngData->data(), pngData->size(), dst.writable_str());
267 dst.prepend("data:image/png;base64,");
268 return std::string(dst.c_str());
269 }
270
getImageCount()271 int getImageCount() {
272 return fImages.size();
273 }
274
275 // Get the image info of one of the resource images.
getImageInfo(int index)276 SimpleImageInfo getImageInfo(int index) {
277 return toSimpleImageInfo(fImages[index]->imageInfo());
278 }
279
280 // return data on which commands each image is used in.
281 // (frame, -1) returns info for the given frame,
282 // (frame, nodeid) return info for a layer update
283 // { imageid: [commandid, commandid, ...], ... }
imageUseInfo(int framenumber,int nodeid)284 JSObject imageUseInfo(int framenumber, int nodeid) {
285 JSObject result = emscripten::val::object();
286 DebugCanvas* debugCanvas = frames[framenumber].get();
287 if (nodeid >= 0) {
288 debugCanvas = fLayerManager->getEventDebugCanvas(nodeid, framenumber);
289 }
290 const auto& map = debugCanvas->getImageIdToCommandMap(udm);
291 for (auto it = map.begin(); it != map.end(); ++it) {
292 JSArray list = emscripten::val::array();
293 for (const int commandId : it->second) {
294 list.call<void>("push", commandId);
295 }
296 result.set(std::to_string(it->first), list);
297 }
298 return result;
299 }
300
301 // Return information on every layer (offscreeen buffer) that is available for drawing at
302 // the current frame.
getLayerSummariesJs()303 JSArray getLayerSummariesJs() {
304 JSArray result = emscripten::val::array();
305 for (auto summary : fLayerManager->summarizeLayers(fp)) {
306 result.call<void>("push", summary);
307 }
308 return result;
309 }
310
getLayerKeys()311 JSArray getLayerKeys() {
312 JSArray result = emscripten::val::array();
313 for (auto key : fLayerManager->getKeys()) {
314 JSObject item = emscripten::val::object();
315 item.set("frame", key.frame);
316 item.set("nodeId", key.nodeId);
317 result.call<void>("push", item);
318 }
319 return result;
320 }
321
322 // When set to a valid layer index, causes this class to playback the layer draw event at nodeId
323 // on frame fp. No validation of nodeId or fp is performed, this must be valid values obtained
324 // from either fLayerManager.listNodesForFrame or fLayerManager.summarizeEvents
325 // Set to -1 to return to viewing the top level animation
setInspectedLayer(int nodeId)326 void setInspectedLayer(int nodeId) {
327 fInspectedLayer = nodeId;
328 }
329
330 // Finds a command that left the given pixel in it's current state.
331 // Note that this method may fail to find the absolute last command that leaves a pixel
332 // the given color, but there is probably only one candidate in most cases, and the log(n)
333 // makes it worth it.
findCommandByPixel(SkSurface * surface,int x,int y,int commandIndex)334 int findCommandByPixel(SkSurface* surface, int x, int y, int commandIndex) {
335 // What color is the pixel now?
336 SkColor finalColor = evaluateCommandColor(surface, commandIndex, x, y);
337
338 int lowerBound = 0;
339 int upperBound = commandIndex;
340
341 while (upperBound - lowerBound > 1) {
342 int command = (upperBound - lowerBound) / 2 + lowerBound;
343 auto c = evaluateCommandColor(surface, command, x, y);
344 if (c == finalColor) {
345 upperBound = command;
346 } else {
347 lowerBound = command;
348 }
349 }
350 // clean up after side effects
351 drawTo(surface, commandIndex);
352 return upperBound;
353 }
354
355 private:
356
357 // Helper for findCommandByPixel.
358 // Has side effect of flushing to surface.
359 // TODO(nifong) eliminate side effect.
evaluateCommandColor(SkSurface * surface,int command,int x,int y)360 SkColor evaluateCommandColor(SkSurface* surface, int command, int x, int y) {
361 drawTo(surface, command);
362
363 SkColor c;
364 SkImageInfo info = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
365 SkPixmap pixmap(info, &c, 4);
366 surface->readPixels(pixmap, x, y);
367 return c;
368 }
369
370 // Loads a single frame (traditional) skp file from the provided data stream and returns
371 // a newly allocated DebugCanvas initialized with the SkPicture that was in the file.
loadSingleFrame(SkMemoryStream * stream)372 std::unique_ptr<DebugCanvas> loadSingleFrame(SkMemoryStream* stream) {
373 // note overloaded = operator that actually does a move
374 sk_sp<SkPicture> picture = SkPicture::MakeFromStream(stream);
375 if (!picture) {
376 SkDebugf("Unable to deserialze frame.\n");
377 return nullptr;
378 }
379 SkDebugf("Parsed SKP file.\n");
380 // Make debug canvas using bounds from SkPicture
381 fBoundsArray.push_back(picture->cullRect().roundOut());
382 std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBoundsArray.back());
383
384 // Only draw picture to the debug canvas once.
385 debugCanvas->drawPicture(picture);
386 return debugCanvas;
387 }
388
loadMultiFrame(SkMemoryStream * stream)389 std::string loadMultiFrame(SkMemoryStream* stream) {
390 // Attempt to deserialize with an image sharing serial proc.
391 auto deserialContext = std::make_unique<SkSharingDeserialContext>();
392 SkDeserialProcs procs;
393 procs.fImageProc = SkSharingDeserialContext::deserializeImage;
394 procs.fImageCtx = deserialContext.get();
395
396 int page_count = SkMultiPictureDocumentReadPageCount(stream);
397 if (!page_count) {
398 // MSKP's have a version separate from the SKP subpictures they contain.
399 return "Not a MultiPictureDocument, MultiPictureDocument file version too old, or MultiPictureDocument contained 0 frames.";
400 }
401 SkDebugf("Expecting %d frames\n", page_count);
402
403 std::vector<SkDocumentPage> pages(page_count);
404 if (!SkMultiPictureDocumentRead(stream, pages.data(), page_count, &procs)) {
405 return "Reading frames from MultiPictureDocument failed";
406 }
407
408 fLayerManager = std::make_unique<DebugLayerManager>();
409
410 int i = 0;
411 for (const auto& page : pages) {
412 // Make debug canvas using bounds from SkPicture
413 fBoundsArray.push_back(page.fPicture->cullRect().roundOut());
414 std::unique_ptr<DebugCanvas> debugCanvas = std::make_unique<DebugCanvas>(fBoundsArray.back());
415 debugCanvas->setLayerManagerAndFrame(fLayerManager.get(), i);
416
417 // Only draw picture to the debug canvas once.
418 debugCanvas->drawPicture(page.fPicture);
419
420 if (debugCanvas->getSize() <=0 ){
421 SkDebugf("Skipped corrupted frame, had %d commands \n", debugCanvas->getSize());
422 continue;
423 }
424 // If you don't set these, they're undefined.
425 debugCanvas->setOverdrawViz(false);
426 debugCanvas->setDrawGpuOpBounds(false);
427 debugCanvas->setClipVizColor(SK_ColorTRANSPARENT);
428 debugCanvas->setAndroidClipViz(false);
429 frames.push_back(std::move(debugCanvas));
430 i++;
431 }
432 fImages = deserialContext->fImages;
433
434 udm.indexImages(fImages);
435 return "";
436 }
437
438 // constrains the draw command index to the frame's command list length.
constrainFrameCommand(int index)439 int constrainFrameCommand(int index) {
440 int cmdlen = frames[fp]->getSize();
441 if (index >= cmdlen) {
442 return cmdlen-1;
443 }
444 return index;
445 }
446
447 // A vector of DebugCanvas, each one initialized to a frame of the animation.
448 std::vector<std::unique_ptr<DebugCanvas>> frames;
449 // The index of the current frame (into the vector above)
450 int fp = 0;
451 // The width and height of every frame.
452 // frame sizes are known to change in Android Skia RenderEngine because it interleves pictures from different applications.
453 std::vector<SkIRect> fBoundsArray;
454 // image resources from a loaded file
455 std::vector<sk_sp<SkImage>> fImages;
456
457 // The URLDataManager here is a cache that accepts encoded data (pngs) and puts
458 // numbers on them. We have our own collection of images (fImages) that was populated by the
459 // SkSharingDeserialContext when mskp files are loaded which it can use for IDing images
460 // without having to serialize them.
461 UrlDataManager udm;
462
463 // A structure holding the picture information needed to draw any layers used in an mskp file
464 // individual frames hold a pointer to it, store draw events, and request images from it.
465 // it is stateful and is set to the current frame at all times.
466 std::unique_ptr<DebugLayerManager> fLayerManager;
467
468 // The node id of a layer being inspected, if any.
469 // -1 means we are viewing the top level animation, not a layer.
470 // the exact draw event being inspected depends also on the selected frame `fp`.
471 int fInspectedLayer = -1;
472 };
473
474 #ifdef SK_GL
MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)475 sk_sp<GrDirectContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
476 {
477 EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(context);
478 if (r < 0) {
479 SkDebugf("failed to make webgl context current %d\n", r);
480 return nullptr;
481 }
482 // setup interface
483 auto interface = GrGLMakeNativeInterface();
484 if (!interface) {
485 SkDebugf("failed to make GrGLMakeNativeInterface\n");
486 return nullptr;
487 }
488 // setup context
489 return GrDirectContext::MakeGL(interface);
490 }
491
MakeOnScreenGLSurface(sk_sp<GrDirectContext> dContext,int width,int height)492 sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrDirectContext> dContext, int width, int height) {
493 glClearColor(0, 0, 0, 0);
494 glClearStencil(0);
495 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
496
497
498 // Wrap the frame buffer object attached to the screen in a Skia render
499 // target so Skia can render to it
500 GrGLint buffer;
501 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer);
502 GrGLFramebufferInfo info;
503 info.fFBOID = (GrGLuint) buffer;
504 SkColorType colorType;
505
506 info.fFormat = GL_RGBA8;
507 colorType = kRGBA_8888_SkColorType;
508
509 GrBackendRenderTarget target(width, height, 0, 8, info);
510
511 sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(dContext.get(), target,
512 kBottomLeft_GrSurfaceOrigin,
513 colorType, nullptr, nullptr));
514 return surface;
515 }
516
MakeRenderTarget(sk_sp<GrDirectContext> dContext,int width,int height)517 sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrDirectContext> dContext, int width, int height) {
518 SkImageInfo info = SkImageInfo::MakeN32(width, height, SkAlphaType::kPremul_SkAlphaType);
519
520 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(dContext.get(),
521 SkBudgeted::kYes,
522 info, 0,
523 kBottomLeft_GrSurfaceOrigin,
524 nullptr, true));
525 return surface;
526 }
527
MakeRenderTarget(sk_sp<GrDirectContext> dContext,SimpleImageInfo sii)528 sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrDirectContext> dContext, SimpleImageInfo sii) {
529 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(dContext.get(),
530 SkBudgeted::kYes,
531 toSkImageInfo(sii), 0,
532 kBottomLeft_GrSurfaceOrigin,
533 nullptr, true));
534 return surface;
535 }
536 #endif
537
538 using namespace emscripten;
EMSCRIPTEN_BINDINGS(my_module)539 EMSCRIPTEN_BINDINGS(my_module) {
540
541 function("MinVersion", &MinVersion);
542
543 // The main class that the JavaScript in index.html uses
544 class_<SkpDebugPlayer>("SkpDebugPlayer")
545 .constructor<>()
546 .function("changeFrame", &SkpDebugPlayer::changeFrame)
547 .function("deleteCommand", &SkpDebugPlayer::deleteCommand)
548 .function("draw", &SkpDebugPlayer::draw, allow_raw_pointers())
549 .function("drawTo", &SkpDebugPlayer::drawTo, allow_raw_pointers())
550 .function("findCommandByPixel", &SkpDebugPlayer::findCommandByPixel, allow_raw_pointers())
551 .function("getBounds", &SkpDebugPlayer::getBounds)
552 .function("getBoundsForFrame", &SkpDebugPlayer::getBoundsForFrame)
553 .function("getFrameCount", &SkpDebugPlayer::getFrameCount)
554 .function("getImageResource", &SkpDebugPlayer::getImageResource)
555 .function("getImageCount", &SkpDebugPlayer::getImageCount)
556 .function("getImageInfo", &SkpDebugPlayer::getImageInfo)
557 .function("getLayerKeys", &SkpDebugPlayer::getLayerKeys)
558 .function("getLayerSummariesJs", &SkpDebugPlayer::getLayerSummariesJs)
559 .function("getSize", &SkpDebugPlayer::getSize)
560 .function("imageUseInfo", &SkpDebugPlayer::imageUseInfo)
561 .function("imageUseInfoForFrameJs", optional_override([](SkpDebugPlayer& self, const int frame)->JSObject {
562 // -1 as a node id is used throughout the application to mean no layer inspected.
563 return self.imageUseInfo(frame, -1);
564 }))
565 .function("jsonCommandList", &SkpDebugPlayer::jsonCommandList, allow_raw_pointers())
566 .function("lastCommandInfo", &SkpDebugPlayer::lastCommandInfo)
567 .function("loadSkp", &SkpDebugPlayer::loadSkp, allow_raw_pointers())
568 .function("setClipVizColor", &SkpDebugPlayer::setClipVizColor)
569 .function("setCommandVisibility", &SkpDebugPlayer::setCommandVisibility)
570 .function("setGpuOpBounds", &SkpDebugPlayer::setGpuOpBounds)
571 .function("setInspectedLayer", &SkpDebugPlayer::setInspectedLayer)
572 .function("setOriginVisible", &SkpDebugPlayer::setOriginVisible)
573 .function("setOverdrawVis", &SkpDebugPlayer::setOverdrawVis)
574 .function("setAndroidClipViz", &SkpDebugPlayer::setAndroidClipViz);
575
576 // Structs used as arguments or returns to the functions above
577 value_object<SkIRect>("SkIRect")
578 .field("fLeft", &SkIRect::fLeft)
579 .field("fTop", &SkIRect::fTop)
580 .field("fRight", &SkIRect::fRight)
581 .field("fBottom", &SkIRect::fBottom);
582 // emscripten provided the following convenience function for binding vector<T>
583 // https://emscripten.org/docs/api_reference/bind.h.html#_CPPv415register_vectorPKc
584 register_vector<DebugLayerManager::LayerSummary>("VectorLayerSummary");
585 value_object<DebugLayerManager::LayerSummary>("LayerSummary")
586 .field("nodeId", &DebugLayerManager::LayerSummary::nodeId)
587 .field("frameOfLastUpdate", &DebugLayerManager::LayerSummary::frameOfLastUpdate)
588 .field("fullRedraw", &DebugLayerManager::LayerSummary::fullRedraw)
589 .field("layerWidth", &DebugLayerManager::LayerSummary::layerWidth)
590 .field("layerHeight", &DebugLayerManager::LayerSummary::layerHeight);
591
592 // Symbols needed by cpu.js to perform surface creation and flushing.
593 enum_<SkColorType>("ColorType")
594 .value("RGBA_8888", SkColorType::kRGBA_8888_SkColorType);
595 enum_<SkAlphaType>("AlphaType")
596 .value("Opaque", SkAlphaType::kOpaque_SkAlphaType)
597 .value("Premul", SkAlphaType::kPremul_SkAlphaType)
598 .value("Unpremul", SkAlphaType::kUnpremul_SkAlphaType);
599 value_object<SimpleImageInfo>("SkImageInfo")
600 .field("width", &SimpleImageInfo::width)
601 .field("height", &SimpleImageInfo::height)
602 .field("colorType", &SimpleImageInfo::colorType)
603 .field("alphaType", &SimpleImageInfo::alphaType);
604 constant("TRANSPARENT", (JSColor) SK_ColorTRANSPARENT);
605 function("_getRasterDirectSurface", optional_override([](const SimpleImageInfo ii,
606 uintptr_t /* uint8_t* */ pPtr,
607 size_t rowBytes)->sk_sp<SkSurface> {
608 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
609 SkImageInfo imageInfo = toSkImageInfo(ii);
610 SkDebugf("Made raster direct surface.\n");
611 return SkSurface::MakeRasterDirect(imageInfo, pixels, rowBytes, nullptr);
612 }), allow_raw_pointers());
613 class_<SkSurface>("SkSurface")
614 .smart_ptr<sk_sp<SkSurface>>("sk_sp<SkSurface>")
615 .function("width", &SkSurface::width)
616 .function("height", &SkSurface::height)
617 .function("_flush", optional_override([](SkSurface& self) {
618 self.flushAndSubmit(false);
619 }))
620 .function("clear", optional_override([](SkSurface& self, JSColor color)->void {
621 self.getCanvas()->clear(SkColor(color));
622 }))
623 .function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers());
624 // TODO(nifong): remove
625 class_<SkCanvas>("SkCanvas")
626 .function("clear", optional_override([](SkCanvas& self, JSColor color)->void {
627 // JS side gives us a signed int instead of an unsigned int for color
628 // Add a optional_override to change it out.
629 self.clear(SkColor(color));
630 }));
631
632 #ifdef SK_GL
633 class_<GrDirectContext>("GrDirectContext")
634 .smart_ptr<sk_sp<GrDirectContext>>("sk_sp<GrDirectContext>");
635 function("currentContext", &emscripten_webgl_get_current_context);
636 function("setCurrentContext", &emscripten_webgl_make_context_current);
637 function("MakeGrContext", &MakeGrContext);
638 function("MakeOnScreenGLSurface", &MakeOnScreenGLSurface);
639 function("MakeRenderTarget", select_overload<sk_sp<SkSurface>(
640 sk_sp<GrDirectContext>, int, int)>(&MakeRenderTarget));
641 function("MakeRenderTarget", select_overload<sk_sp<SkSurface>(
642 sk_sp<GrDirectContext>, SimpleImageInfo)>(&MakeRenderTarget));
643 constant("gpu", true);
644 #endif
645 }
646