|  |  | 
|  | /* | 
|  | * Copyright 2013 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "ppapi/cpp/completion_callback.h" | 
|  | #include "ppapi/cpp/graphics_2d.h" | 
|  | #include "ppapi/cpp/image_data.h" | 
|  | #include "ppapi/cpp/instance.h" | 
|  | #include "ppapi/cpp/module.h" | 
|  | #include "ppapi/cpp/point.h" | 
|  | #include "ppapi/cpp/rect.h" | 
|  | #include "ppapi/cpp/var.h" | 
|  |  | 
|  | #include "SkBase64.h" | 
|  | #include "SkBitmap.h" | 
|  | #include "SkCanvas.h" | 
|  | #include "SkColor.h" | 
|  | #include "SkDebugger.h" | 
|  | #include "SkGraphics.h" | 
|  | #include "SkStream.h" | 
|  | #include "SkString.h" | 
|  |  | 
|  | class SkiaInstance; | 
|  |  | 
|  | // Used by SkDebugf | 
|  | SkiaInstance* gPluginInstance; | 
|  |  | 
|  | void FlushCallback(void* data, int32_t result); | 
|  |  | 
|  | // Skia's subclass of pp::Instance, our interface with the browser. | 
|  | class SkiaInstance : public pp::Instance { | 
|  | public: | 
|  | explicit SkiaInstance(PP_Instance instance) | 
|  | : pp::Instance(instance) | 
|  | , fCanvas(NULL) | 
|  | , fFlushLoopRunning(false) | 
|  | , fFlushPending(false) | 
|  |  | 
|  | { | 
|  | gPluginInstance = this; | 
|  | SkGraphics::Init(); | 
|  | } | 
|  |  | 
|  | virtual ~SkiaInstance() { | 
|  | SkGraphics::Term(); | 
|  | gPluginInstance = NULL; | 
|  | } | 
|  |  | 
|  | virtual void HandleMessage(const pp::Var& var_message) { | 
|  | // Receive a message from javascript. | 
|  | if (var_message.is_string()) { | 
|  | SkString msg(var_message.AsString().c_str()); | 
|  | if (msg.startsWith("init")) { | 
|  | } else if (msg.startsWith("LoadSKP")) { | 
|  | size_t startIndex = strlen("LoadSKP"); | 
|  | size_t dataSize = msg.size()/sizeof(char) - startIndex; | 
|  | SkBase64 decodedData; | 
|  | decodedData.decode(msg.c_str() + startIndex, dataSize); | 
|  | size_t decodedSize = 3 * (dataSize / 4); | 
|  | SkDebugf("Got size: %d\n", decodedSize); | 
|  | if (!decodedData.getData()) { | 
|  | SkDebugf("Failed to decode SKP\n"); | 
|  | return; | 
|  | } | 
|  | SkMemoryStream pictureStream(decodedData.getData(), decodedSize); | 
|  | SkPicture* picture = SkPicture::CreateFromStream(&pictureStream); | 
|  | if (NULL == picture) { | 
|  | SkDebugf("Failed to create SKP.\n"); | 
|  | return; | 
|  | } | 
|  | fDebugger.loadPicture(picture); | 
|  | picture->unref(); | 
|  |  | 
|  | // Set up the command list. | 
|  | SkTArray<SkString>* commands = fDebugger.getDrawCommandsAsStrings(); | 
|  | PostMessage("ClearCommands"); | 
|  | for (int i = 0; i < commands->count(); ++i) { | 
|  | SkString addCommand("AddCommand:"); | 
|  | addCommand.append((*commands)[i]); | 
|  | PostMessage(addCommand.c_str()); | 
|  | } | 
|  | PostMessage("UpdateCommands"); | 
|  |  | 
|  | // Set the overview text. | 
|  | SkString overviewText; | 
|  | fDebugger.getOverviewText(NULL, 0.0, &overviewText, 1); | 
|  | overviewText.prepend("SetOverview:"); | 
|  | PostMessage(overviewText.c_str()); | 
|  |  | 
|  | // Draw the SKP. | 
|  | if (!fFlushLoopRunning) { | 
|  | Paint(); | 
|  | } | 
|  | } else if (msg.startsWith("CommandSelected:")) { | 
|  | size_t startIndex = strlen("CommandSelected:"); | 
|  | int index = atoi(msg.c_str() + startIndex); | 
|  | fDebugger.setIndex(index); | 
|  | if (!fFlushLoopRunning) { | 
|  | Paint(); | 
|  | } | 
|  | } else if (msg.startsWith("Rewind")) { | 
|  | fCanvas->clear(SK_ColorWHITE); | 
|  | fDebugger.setIndex(0); | 
|  | if (!fFlushLoopRunning) { | 
|  | Paint(); | 
|  | } | 
|  | } else if (msg.startsWith("StepBack")) { | 
|  | fCanvas->clear(SK_ColorWHITE); | 
|  | int currentIndex = fDebugger.index(); | 
|  | if (currentIndex > 1) { | 
|  | fDebugger.setIndex(currentIndex - 1); | 
|  | if (!fFlushLoopRunning) { | 
|  | Paint(); | 
|  | } | 
|  | } | 
|  | } else if (msg.startsWith("Pause")) { | 
|  | // TODO(borenet) | 
|  | } else if (msg.startsWith("StepForward")) { | 
|  | int currentIndex = fDebugger.index(); | 
|  | if (currentIndex < fDebugger.getSize() -1) { | 
|  | fDebugger.setIndex(currentIndex + 1); | 
|  | if (!fFlushLoopRunning) { | 
|  | Paint(); | 
|  | } | 
|  | } | 
|  | } else if (msg.startsWith("Play")) { | 
|  | fDebugger.setIndex(fDebugger.getSize() - 1); | 
|  | if (!fFlushLoopRunning) { | 
|  | Paint(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Paint() { | 
|  | if (!fImage.is_null()) { | 
|  | fDebugger.draw(fCanvas); | 
|  | fDeviceContext.PaintImageData(fImage, pp::Point(0, 0)); | 
|  | if (!fFlushPending) { | 
|  | fFlushPending = true; | 
|  | fDeviceContext.Flush(pp::CompletionCallback(&FlushCallback, this)); | 
|  | } else { | 
|  | SkDebugf("A flush is pending... Skipping flush.\n"); | 
|  | } | 
|  | } else { | 
|  | SkDebugf("No pixels to write to!\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) { | 
|  | if (position.size().width() == fWidth && | 
|  | position.size().height() == fHeight) { | 
|  | return;  // We don't care about the position, only the size. | 
|  | } | 
|  | fWidth = position.size().width(); | 
|  | fHeight = position.size().height(); | 
|  |  | 
|  | fDeviceContext = pp::Graphics2D(this, pp::Size(fWidth, fHeight), false); | 
|  | if (!BindGraphics(fDeviceContext)) { | 
|  | SkDebugf("Couldn't bind the device context\n"); | 
|  | return; | 
|  | } | 
|  | fImage = pp::ImageData(this, | 
|  | PP_IMAGEDATAFORMAT_BGRA_PREMUL, | 
|  | pp::Size(fWidth, fHeight), false); | 
|  | const SkImageInfo info = SkImageInfo::MakeN32Premul(fWidth, fHeight); | 
|  | fBitmap.installPixels(info, fImage.data(), info.minRowBytes()); | 
|  | if (fCanvas) { | 
|  | delete fCanvas; | 
|  | } | 
|  | fCanvas = new SkCanvas(fBitmap); | 
|  | fCanvas->clear(SK_ColorWHITE); | 
|  | if (!fFlushLoopRunning) { | 
|  | Paint(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void OnFlush() { | 
|  | fFlushLoopRunning = true; | 
|  | fFlushPending = false; | 
|  | Paint(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | pp::Graphics2D fDeviceContext; | 
|  | pp::ImageData fImage; | 
|  | int fWidth; | 
|  | int fHeight; | 
|  |  | 
|  | SkBitmap fBitmap; | 
|  | SkCanvas* fCanvas; | 
|  | SkDebugger fDebugger; | 
|  |  | 
|  | bool fFlushLoopRunning; | 
|  | bool fFlushPending; | 
|  | }; | 
|  |  | 
|  | void FlushCallback(void* data, int32_t result) { | 
|  | static_cast<SkiaInstance*>(data)->OnFlush(); | 
|  | } | 
|  |  | 
|  | class SkiaModule : public pp::Module { | 
|  | public: | 
|  | SkiaModule() : pp::Module() {} | 
|  | virtual ~SkiaModule() {} | 
|  |  | 
|  | virtual pp::Instance* CreateInstance(PP_Instance instance) { | 
|  | return new SkiaInstance(instance); | 
|  | } | 
|  | }; | 
|  |  | 
|  | namespace pp { | 
|  | Module* CreateModule() { | 
|  | return new SkiaModule(); | 
|  | } | 
|  | }  // namespace pp |