C++程序  |  221行  |  6.97 KB


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