• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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