blob: b80dde093845a612b79d9105417bb4ec0635fd80 [file] [log] [blame]
/*
* 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