// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "ppapi/c/pp_errors.h" #include "ppapi/cpp/completion_callback.h" #include "ppapi/cpp/graphics_2d.h" #include "ppapi/cpp/image_data.h" #include "ppapi/cpp/input_event.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/module.h" #include "ppapi/cpp/rect.h" #include "ppapi/cpp/var.h" #include "ppapi/utility/completion_callback_factory.h" // When compiling natively on Windows, PostMessage can be #define-d to // something else. #ifdef PostMessage #undef PostMessage #endif // Example plugin to demonstrate usage of pp::View and pp::Graphics2D APIs for // rendering 2D graphics at device resolution. See Paint() for more details. class MyInstance : public pp::Instance { public: explicit MyInstance(PP_Instance instance) : pp::Instance(instance), width_(0), height_(0), pixel_width_(0), pixel_height_(0), device_scale_(1.0f), css_scale_(1.0f), using_device_pixels_(true) { RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD); } virtual void DidChangeView(const pp::View& view) { pp::Rect view_rect = view.GetRect(); if (view_rect.width() == width_ && view_rect.height() == height_ && view.GetDeviceScale() == device_scale_ && view.GetCSSScale() == css_scale_) return; // We don't care about the position, only the size and scale. width_ = view_rect.width(); height_ = view_rect.height(); device_scale_ = view.GetDeviceScale(); css_scale_ = view.GetCSSScale(); pixel_width_ = width_ * device_scale_; pixel_height_ = height_ * device_scale_; SetupGraphics(); } virtual bool HandleInputEvent(const pp::InputEvent& event) { switch (event.GetType()) { case PP_INPUTEVENT_TYPE_MOUSEDOWN: HandleMouseDown(event); return true; default: return false; } } virtual void HandleMessage(const pp::Var& message_data) { if (message_data.is_string()) { std::string str = message_data.AsString(); if (str == "dip") { if (using_device_pixels_) { using_device_pixels_ = false; SetupGraphics(); } } else if (str == "device") { if (!using_device_pixels_) { using_device_pixels_ = true; SetupGraphics(); } } else if (str == "metrics") { std::stringstream stream; stream << "DIP (" << width_ << ", " << height_ << "), device pixels=(" << pixel_width_ << ", " << pixel_height_ <<"), device_scale=" << device_scale_ <<", css_scale=" << css_scale_; PostMessage(stream.str()); } } } private: void HandleMouseDown(const pp::InputEvent& event) { pp::MouseInputEvent mouse_event(event); pp::Point position(mouse_event.GetPosition()); pp::Point position_device(position.x() * device_scale_, position.y() * device_scale_); std::stringstream stream; stream << "Mousedown at DIP (" << position.x() << ", " << position.y() << "), device pixel (" << position_device.x() << ", " << position_device.y() << ")"; if (css_scale_ > 0.0f) { pp::Point position_css(position.x() / css_scale_, position.y() / css_scale_); stream << ", CSS pixel (" << position_css.x() << ", " << position_css.y() <<")"; } else { stream <<", unknown CSS pixel. css_scale_=" << css_scale_; } PostMessage(stream.str()); } void SetupGraphics() { if (using_device_pixels_) { // The plugin will treat 1 pixel in the device context as 1 device pixel. // This will set up a properly-sized pp::Graphics2D, and tell Pepper // to apply the correct scale so the resulting concatenated scale leaves // each pixel in the device context as one on the display device. device_context_ = pp::Graphics2D(this, pp::Size(pixel_width_, pixel_height_), true); if (device_scale_ > 0.0f) { // If SetScale is promoted to pp::Graphics2D, the dc_dev constructor // can be removed, and this will become the following line instead. // device_context_.SetScale(1.0f / device_scale_); device_context_.SetScale(1.0f / device_scale_); } } else { // The plugin will treat 1 pixel in the device context as one DIP. device_context_ = pp::Graphics2D(this, pp::Size(width_, height_), true); } BindGraphics(device_context_); Paint(); } void Paint() { int width = using_device_pixels_ ? pixel_width_ : width_; int height = using_device_pixels_ ? pixel_height_ : height_; pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, pp::Size(width, height), false); if (image.is_null()) return; // Painting here will demonstrate a few techniques: // - painting a thin blue box and cross-hatch to show the finest resolution // available. // - painting a 25 DIP (logical pixel) green circle to show how objects of a // fixed size in DIPs should be scaled if using a high-resolution // pp::Graphics2D. // - paiting a 50 CSS pixel red circle to show how objects of a fixed size // in CSS pixels should be scaled if using a high-resolution // pp::Graphics2D, as well as how to use the GetCSSScale value properly. // Painting in "DIP resolution" mode (|using_device_pixels_| false) will // demonstrate how unscaled graphics would look, even on a high-DPI device. // Painting in "device resolution" mode (|using_device_pixels_| true) will // show how scaled graphics would look crisper on a high-DPI device, in // comparison to using unscaled graphics. Both modes should look identical // when displayed on a non-high-DPI device (window.devicePixelRatio == 1). // Toggling between "DIP resolution" mode and "device resolution" mode // should not change the sizes of the circles. // Changing the browser zoom level should cause the CSS circle to zoom, but // not the DIP-sized circle. // All painting here does not use any anti-aliasing. float circle_1_radius = 25; if (using_device_pixels_) circle_1_radius *= device_scale_; float circle_2_radius = 50 * css_scale_; if (using_device_pixels_) circle_2_radius *= device_scale_; for (int y = 0; y < height; ++y) { char* row = static_cast(image.data()) + (y * image.stride()); uint32_t* pixel = reinterpret_cast(row); for (int x = 0; x < width; ++x) { int dx = (width / 2) - x; int dy = (height / 2) - y; float dist_squared = (dx * dx) + (dy * dy); if (x == 0 || y == 0 || x == width - 1 || y == width - 1 || x == y || width - x - 1 == y) { *pixel++ = 0xFF0000FF; } else if (dist_squared < circle_1_radius * circle_1_radius) { *pixel++ = 0xFF00FF00; } else if (dist_squared < circle_2_radius * circle_2_radius) { *pixel++ = 0xFFFF0000; } else { *pixel++ = 0xFF000000; } } } device_context_.ReplaceContents(&image); device_context_.Flush(pp::CompletionCallback(&OnFlush, this)); } static void OnFlush(void* user_data, int32_t result) {} pp::Graphics2D device_context_; int width_; int height_; int pixel_width_; int pixel_height_; float device_scale_; float css_scale_; bool using_device_pixels_; }; // This object is the global object representing this plugin library as long as // it is loaded. class MyModule : public pp::Module { public: MyModule() : pp::Module() {} virtual ~MyModule() {} // Override CreateInstance to create your customized Instance object. virtual pp::Instance* CreateInstance(PP_Instance instance) { return new MyInstance(instance); } }; namespace pp { // Factory function for your specialization of the Module object. Module* CreateModule() { return new MyModule(); } } // namespace pp