/* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "webkit/tools/pepper_test_plugin/plugin_object.h" #include #include #include #include #if defined(INDEPENDENT_PLUGIN) #define CHECK(x) #else #include "base/logging.h" #include "gpu/command_buffer/client/gles2_lib.h" #include "gpu/command_buffer/client/gles2_demo_cc.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/effects/SkGradientShader.h" #endif #include "webkit/tools/pepper_test_plugin/event_handler.h" #include "webkit/tools/pepper_test_plugin/test_object.h" NPNetscapeFuncs* browser; namespace { // Properties ------------------------------------------------------------------ enum { ID_PROPERTY_PROPERTY = 0, ID_PROPERTY_TEST_OBJECT, NUM_PROPERTY_IDENTIFIERS }; static NPIdentifier plugin_property_identifiers[NUM_PROPERTY_IDENTIFIERS]; static const NPUTF8* plugin_property_identifier_names[NUM_PROPERTY_IDENTIFIERS] = { "property", "testObject", }; // Methods --------------------------------------------------------------------- enum { ID_TEST_GET_PROPERTY = 0, ID_SET_TEXT_BOX, NUM_METHOD_IDENTIFIERS }; static NPIdentifier plugin_method_identifiers[NUM_METHOD_IDENTIFIERS]; static const NPUTF8* plugin_method_identifier_names[NUM_METHOD_IDENTIFIERS] = { "testGetProperty", "setTextBox", }; void EnsureIdentifiersInitialized() { static bool identifiers_initialized = false; if (identifiers_initialized) return; browser->getstringidentifiers(plugin_property_identifier_names, NUM_PROPERTY_IDENTIFIERS, plugin_property_identifiers); browser->getstringidentifiers(plugin_method_identifier_names, NUM_METHOD_IDENTIFIERS, plugin_method_identifiers); identifiers_initialized = true; } // Helper functions ------------------------------------------------------------ std::string CreateStringFromNPVariant(const NPVariant& variant) { return std::string(NPVARIANT_TO_STRING(variant).UTF8Characters, NPVARIANT_TO_STRING(variant).UTF8Length); } bool TestGetProperty(PluginObject* obj, const NPVariant* args, uint32 arg_count, NPVariant* result) { if (arg_count == 0) return false; NPObject *object; browser->getvalue(obj->npp(), NPNVWindowNPObject, &object); for (uint32 i = 0; i < arg_count; i++) { CHECK(NPVARIANT_IS_STRING(args[i])); std::string property_string = CreateStringFromNPVariant(args[i]); NPIdentifier property_identifier = browser->getstringidentifier(property_string.c_str()); NPVariant variant; bool retval = browser->getproperty(obj->npp(), object, property_identifier, &variant); browser->releaseobject(object); if (!retval) break; if (i + 1 < arg_count) { CHECK(NPVARIANT_IS_OBJECT(variant)); object = NPVARIANT_TO_OBJECT(variant); } else { *result = variant; return true; } } VOID_TO_NPVARIANT(*result); return false; } // Plugin class functions ------------------------------------------------------ NPObject* PluginAllocate(NPP npp, NPClass* the_class) { EnsureIdentifiersInitialized(); PluginObject* newInstance = new PluginObject(npp); return reinterpret_cast(newInstance); } void PluginDeallocate(NPObject* header) { PluginObject* plugin = reinterpret_cast(header); delete plugin; } void PluginInvalidate(NPObject* obj) { } bool PluginHasMethod(NPObject* obj, NPIdentifier name) { for (int i = 0; i < NUM_METHOD_IDENTIFIERS; i++) { if (name == plugin_method_identifiers[i]) return true; } return false; } bool PluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* args, uint32 arg_count, NPVariant* result) { PluginObject* plugin = reinterpret_cast(header); if (name == plugin_method_identifiers[ID_TEST_GET_PROPERTY]) { return TestGetProperty(plugin, args, arg_count, result); } else if (name == plugin_method_identifiers[ID_SET_TEXT_BOX]) { if (1 == arg_count && NPVARIANT_IS_OBJECT(args[0])) { return event_handler->set_text_box(NPVARIANT_TO_OBJECT(args[0])); } } return false; } bool PluginInvokeDefault(NPObject* obj, const NPVariant* args, uint32 arg_count, NPVariant* result) { INT32_TO_NPVARIANT(1, *result); return true; } bool PluginHasProperty(NPObject* obj, NPIdentifier name) { for (int i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++) { if (name == plugin_property_identifiers[i]) return true; } return false; } bool PluginGetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) { PluginObject* plugin = reinterpret_cast(obj); return false; } bool PluginSetProperty(NPObject* obj, NPIdentifier name, const NPVariant* variant) { PluginObject* plugin = reinterpret_cast(obj); return false; } NPClass plugin_class = { NP_CLASS_STRUCT_VERSION, PluginAllocate, PluginDeallocate, PluginInvalidate, PluginHasMethod, PluginInvoke, PluginInvokeDefault, PluginHasProperty, PluginGetProperty, PluginSetProperty, }; // Bitmap painting ------------------------------------------------------------- #if defined(INDEPENDENT_PLUGIN) void DrawSampleBitmap(void *region, int width, int height) { int32 *bitmap = reinterpret_cast(region); for (int h = 0; h < height; ++h) { uint8 opacity = (h * 0xff) / height; for (int w = 0; w < width; ++w) { // kudos to apatrick for noticing we're using premultiplied alpha uint8 greenness = (w * opacity) / width; *bitmap++ = (opacity << 24) | (greenness << 8); } } } #else void DrawSampleBitmap(void *region, int width, int height) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); bitmap.setPixels(region); SkCanvas canvas(bitmap); SkRect rect; rect.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(width), SkIntToScalar(height)); SkPath path; path.addOval(rect); // Create a gradient shader going from opaque green to transparent. SkPaint paint; paint.setAntiAlias(true); SkColor grad_colors[2]; grad_colors[0] = SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00); grad_colors[1] = SkColorSetARGB(0x00, 0x00, 0xFF, 0x00); SkPoint grad_points[2]; grad_points[0].set(SkIntToScalar(0), SkIntToScalar(0)); grad_points[1].set(SkIntToScalar(0), SkIntToScalar(height)); paint.setShader(SkGradientShader::CreateLinear( grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode)) ->safeUnref(); canvas.drawPath(path, paint); } #endif void FlushCallback(NPP instance, NPDeviceContext* context, NPError err, void* user_data) { } NPExtensions* extensions = NULL; // Sine wave similar to one from simple_sources.cc // F is the desired frequency (e.g. 200), T is sample type (e.g. int16) template void SineWaveCallback( NPDeviceContextAudio *context) { const size_t n_samples = context->config.sampleFrameCount; const size_t n_channels = context->config.outputChannelMap; const double th = 2 * 3.141592653589 * F / context->config.sampleRate; // store time value to avoid clicks on buffer boundries intptr_t t = (intptr_t)context->config.userData; T* buf = reinterpret_cast(context->outBuffer); for (size_t sample = 0; sample < n_samples; ++sample) { T value = static_cast(sin(th * t++) * std::numeric_limits::max()); for (size_t channel = 0; channel < n_channels; ++channel) { *buf++ = value; } } context->config.userData = reinterpret_cast(t); } } // namespace // PluginObject ---------------------------------------------------------------- PluginObject::PluginObject(NPP npp) : npp_(npp), test_object_(browser->createobject(npp, GetTestClass())), device2d_(NULL) { memset(&context_audio_, 0, sizeof(context_audio_)); } PluginObject::~PluginObject() { deviceaudio_->destroyContext(npp_, &context_audio_); // FIXME(brettw) destroy the context. browser->releaseobject(test_object_); } // static NPClass* PluginObject::GetPluginClass() { return &plugin_class; } namespace { void Draw3DCallback(void* data) { static_cast(data)->Draw3D(); } } void PluginObject::New(NPMIMEType pluginType, int16 argc, char* argn[], char* argv[]) { // Default to 2D rendering. dimensions_ = 2; for (int i = 0; i < argc; ++i) { if (strcmp(argn[i], "dimensions") == 0) dimensions_ = atoi(argv[i]); } if (!extensions) { browser->getvalue(npp_, NPNVPepperExtensions, reinterpret_cast(&extensions)); CHECK(extensions); } device2d_ = extensions->acquireDevice(npp_, NPPepper2DDevice); CHECK(device2d_); deviceaudio_ = extensions->acquireDevice(npp_, NPPepperAudioDevice); CHECK(deviceaudio_); } void PluginObject::SetWindow(const NPWindow& window) { if (dimensions_ == 2) { width_ = window.width; height_ = window.height; NPDeviceContext2DConfig config; NPDeviceContext2D context; device2d_->initializeContext(npp_, &config, &context); DrawSampleBitmap(context.region, width_, height_); // TODO(brettw) figure out why this cast is necessary, the functions seem to // match. Could be a calling convention mismatch? NPDeviceFlushContextCallbackPtr callback = reinterpret_cast(&FlushCallback); device2d_->flushContext(npp_, &context, callback, NULL); } else { #if !defined(INDEPENDENT_PLUGIN) if (!command_buffer_.get()) { if (!InitializeCommandBuffer()) return; } gles2_implementation_->Viewport(0, 0, window.width, window.height); // Schedule the first call to Draw. browser->pluginthreadasynccall(npp_, Draw3DCallback, this); #endif } // testing any field would do if (!context_audio_.config.callback) { NPDeviceContextAudioConfig cfg; cfg.sampleRate = 44100; cfg.sampleType = NPAudioSampleTypeInt16; cfg.outputChannelMap = NPAudioChannelStereo; cfg.inputChannelMap = NPAudioChannelNone; cfg.sampleFrameCount = 2048; cfg.flags = 0; cfg.callback = &SineWaveCallback<200, int16>; deviceaudio_->initializeContext(npp_, &cfg, &context_audio_); } } void PluginObject::Draw3D() { #if !defined(INDEPENDENT_PLUGIN) // Render some stuff. gles2::g_gl_impl = gles2_implementation_.get(); GLFromCPPTestFunction(); gles2::GetGLContext()->SwapBuffers(); helper_->Flush(); gles2::g_gl_impl = NULL; // Schedule another call to Draw. browser->pluginthreadasynccall(npp_, Draw3DCallback, this); #endif } bool PluginObject::InitializeCommandBuffer() { #if !defined(INDEPENDENT_PLUGIN) static const int32 kCommandBufferSize = 512 * 1024; command_buffer_.reset(new CommandBufferPepper(npp_, browser)); if (command_buffer_->Initialize(kCommandBufferSize)) { helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer_.get())); if (helper_->Initialize()) { const int32 kTransferBufferSize = 512 * 1024; int32 transfer_buffer_id = command_buffer_->CreateTransferBuffer(kTransferBufferSize); gpu::Buffer transfer_buffer = command_buffer_->GetTransferBuffer(transfer_buffer_id); if (transfer_buffer.ptr) { gles2_implementation_.reset(new gpu::gles2::GLES2Implementation( helper_.get(), transfer_buffer.size, transfer_buffer.ptr, transfer_buffer_id)); return true; } } helper_.reset(); } command_buffer_.reset(); #endif return false; }