1 2 /* 3 * Copyright 2013 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 #include "ppapi/cpp/completion_callback.h" 10 #include "ppapi/cpp/graphics_2d.h" 11 #include "ppapi/cpp/image_data.h" 12 #include "ppapi/cpp/instance.h" 13 #include "ppapi/cpp/module.h" 14 #include "ppapi/cpp/point.h" 15 #include "ppapi/cpp/rect.h" 16 #include "ppapi/cpp/var.h" 17 18 #include "SkBase64.h" 19 #include "SkBitmap.h" 20 #include "SkCanvas.h" 21 #include "SkColor.h" 22 #include "SkDebugger.h" 23 #include "SkGraphics.h" 24 #include "SkStream.h" 25 #include "SkString.h" 26 27 class SkiaInstance; 28 29 // Used by SkDebugf 30 SkiaInstance* gPluginInstance; 31 32 void FlushCallback(void* data, int32_t result); 33 34 // Skia's subclass of pp::Instance, our interface with the browser. 35 class SkiaInstance : public pp::Instance { 36 public: 37 explicit SkiaInstance(PP_Instance instance) 38 : pp::Instance(instance) 39 , fCanvas(NULL) 40 , fFlushLoopRunning(false) 41 , fFlushPending(false) 42 43 { 44 gPluginInstance = this; 45 SkGraphics::Init(); 46 } 47 48 virtual ~SkiaInstance() { 49 SkGraphics::Term(); 50 gPluginInstance = NULL; 51 } 52 53 virtual void HandleMessage(const pp::Var& var_message) { 54 // Receive a message from javascript. 55 if (var_message.is_string()) { 56 SkString msg(var_message.AsString().c_str()); 57 if (msg.startsWith("init")) { 58 } else if (msg.startsWith("LoadSKP")) { 59 size_t startIndex = strlen("LoadSKP"); 60 size_t dataSize = msg.size()/sizeof(char) - startIndex; 61 SkBase64 decodedData; 62 decodedData.decode(msg.c_str() + startIndex, dataSize); 63 size_t decodedSize = 3 * (dataSize / 4); 64 SkDebugf("Got size: %d\n", decodedSize); 65 if (!decodedData.getData()) { 66 SkDebugf("Failed to decode SKP\n"); 67 return; 68 } 69 SkMemoryStream pictureStream(decodedData.getData(), decodedSize); 70 SkPicture* picture = SkPicture::CreateFromStream(&pictureStream); 71 if (NULL == picture) { 72 SkDebugf("Failed to create SKP.\n"); 73 return; 74 } 75 fDebugger.loadPicture(picture); 76 picture->unref(); 77 78 // Set up the command list. 79 SkTArray<SkString>* commands = fDebugger.getDrawCommandsAsStrings(); 80 PostMessage("ClearCommands"); 81 for (int i = 0; i < commands->count(); ++i) { 82 SkString addCommand("AddCommand:"); 83 addCommand.append((*commands)[i]); 84 PostMessage(addCommand.c_str()); 85 } 86 PostMessage("UpdateCommands"); 87 88 // Set the overview text. 89 SkString overviewText; 90 fDebugger.getOverviewText(NULL, 0.0, &overviewText, 1); 91 overviewText.prepend("SetOverview:"); 92 PostMessage(overviewText.c_str()); 93 94 // Draw the SKP. 95 if (!fFlushLoopRunning) { 96 Paint(); 97 } 98 } else if (msg.startsWith("CommandSelected:")) { 99 size_t startIndex = strlen("CommandSelected:"); 100 int index = atoi(msg.c_str() + startIndex); 101 fDebugger.setIndex(index); 102 if (!fFlushLoopRunning) { 103 Paint(); 104 } 105 } else if (msg.startsWith("Rewind")) { 106 fCanvas->clear(SK_ColorWHITE); 107 fDebugger.setIndex(0); 108 if (!fFlushLoopRunning) { 109 Paint(); 110 } 111 } else if (msg.startsWith("StepBack")) { 112 fCanvas->clear(SK_ColorWHITE); 113 int currentIndex = fDebugger.index(); 114 if (currentIndex > 1) { 115 fDebugger.setIndex(currentIndex - 1); 116 if (!fFlushLoopRunning) { 117 Paint(); 118 } 119 } 120 } else if (msg.startsWith("Pause")) { 121 // TODO(borenet) 122 } else if (msg.startsWith("StepForward")) { 123 int currentIndex = fDebugger.index(); 124 if (currentIndex < fDebugger.getSize() -1) { 125 fDebugger.setIndex(currentIndex + 1); 126 if (!fFlushLoopRunning) { 127 Paint(); 128 } 129 } 130 } else if (msg.startsWith("Play")) { 131 fDebugger.setIndex(fDebugger.getSize() - 1); 132 if (!fFlushLoopRunning) { 133 Paint(); 134 } 135 } 136 } 137 } 138 139 void Paint() { 140 if (!fImage.is_null()) { 141 fDebugger.draw(fCanvas); 142 fDeviceContext.PaintImageData(fImage, pp::Point(0, 0)); 143 if (!fFlushPending) { 144 fFlushPending = true; 145 fDeviceContext.Flush(pp::CompletionCallback(&FlushCallback, this)); 146 } else { 147 SkDebugf("A flush is pending... Skipping flush.\n"); 148 } 149 } else { 150 SkDebugf("No pixels to write to!\n"); 151 } 152 } 153 154 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) { 155 if (position.size().width() == fWidth && 156 position.size().height() == fHeight) { 157 return; // We don't care about the position, only the size. 158 } 159 fWidth = position.size().width(); 160 fHeight = position.size().height(); 161 162 fDeviceContext = pp::Graphics2D(this, pp::Size(fWidth, fHeight), false); 163 if (!BindGraphics(fDeviceContext)) { 164 SkDebugf("Couldn't bind the device context\n"); 165 return; 166 } 167 fImage = pp::ImageData(this, 168 PP_IMAGEDATAFORMAT_BGRA_PREMUL, 169 pp::Size(fWidth, fHeight), false); 170 const SkImageInfo info = SkImageInfo::MakeN32Premul(fWidth, fHeight); 171 fBitmap.installPixels(info, fImage.data(), info.minRowBytes()); 172 if (fCanvas) { 173 delete fCanvas; 174 } 175 fCanvas = new SkCanvas(fBitmap); 176 fCanvas->clear(SK_ColorWHITE); 177 if (!fFlushLoopRunning) { 178 Paint(); 179 } 180 } 181 182 void OnFlush() { 183 fFlushLoopRunning = true; 184 fFlushPending = false; 185 Paint(); 186 } 187 188 private: 189 pp::Graphics2D fDeviceContext; 190 pp::ImageData fImage; 191 int fWidth; 192 int fHeight; 193 194 SkBitmap fBitmap; 195 SkCanvas* fCanvas; 196 SkDebugger fDebugger; 197 198 bool fFlushLoopRunning; 199 bool fFlushPending; 200 }; 201 202 void FlushCallback(void* data, int32_t result) { 203 static_cast<SkiaInstance*>(data)->OnFlush(); 204 } 205 206 class SkiaModule : public pp::Module { 207 public: 208 SkiaModule() : pp::Module() {} 209 virtual ~SkiaModule() {} 210 211 virtual pp::Instance* CreateInstance(PP_Instance instance) { 212 return new SkiaInstance(instance); 213 } 214 }; 215 216 namespace pp { 217 Module* CreateModule() { 218 return new SkiaModule(); 219 } 220 } // namespace pp 221