/* * 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* 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(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