1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <sstream> 6 7 #include "ppapi/c/pp_errors.h" 8 #include "ppapi/cpp/completion_callback.h" 9 #include "ppapi/cpp/graphics_2d.h" 10 #include "ppapi/cpp/image_data.h" 11 #include "ppapi/cpp/input_event.h" 12 #include "ppapi/cpp/instance.h" 13 #include "ppapi/cpp/module.h" 14 #include "ppapi/cpp/rect.h" 15 #include "ppapi/cpp/var.h" 16 #include "ppapi/utility/completion_callback_factory.h" 17 18 // When compiling natively on Windows, PostMessage can be #define-d to 19 // something else. 20 #ifdef PostMessage 21 #undef PostMessage 22 #endif 23 24 // Example plugin to demonstrate usage of pp::View and pp::Graphics2D APIs for 25 // rendering 2D graphics at device resolution. See Paint() for more details. 26 class MyInstance : public pp::Instance { 27 public: 28 explicit MyInstance(PP_Instance instance) 29 : pp::Instance(instance), 30 width_(0), 31 height_(0), 32 pixel_width_(0), 33 pixel_height_(0), 34 device_scale_(1.0f), 35 css_scale_(1.0f), 36 using_device_pixels_(true) { 37 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | 38 PP_INPUTEVENT_CLASS_KEYBOARD); 39 } 40 41 virtual void DidChangeView(const pp::View& view) { 42 pp::Rect view_rect = view.GetRect(); 43 if (view_rect.width() == width_ && 44 view_rect.height() == height_ && 45 view.GetDeviceScale() == device_scale_ && 46 view.GetCSSScale() == css_scale_) 47 return; // We don't care about the position, only the size and scale. 48 49 width_ = view_rect.width(); 50 height_ = view_rect.height(); 51 device_scale_ = view.GetDeviceScale(); 52 css_scale_ = view.GetCSSScale(); 53 54 pixel_width_ = width_ * device_scale_; 55 pixel_height_ = height_ * device_scale_; 56 57 SetupGraphics(); 58 } 59 60 virtual bool HandleInputEvent(const pp::InputEvent& event) { 61 switch (event.GetType()) { 62 case PP_INPUTEVENT_TYPE_MOUSEDOWN: 63 HandleMouseDown(event); 64 return true; 65 default: 66 return false; 67 } 68 } 69 70 virtual void HandleMessage(const pp::Var& message_data) { 71 if (message_data.is_string()) { 72 std::string str = message_data.AsString(); 73 if (str == "dip") { 74 if (using_device_pixels_) { 75 using_device_pixels_ = false; 76 SetupGraphics(); 77 } 78 } else if (str == "device") { 79 if (!using_device_pixels_) { 80 using_device_pixels_ = true; 81 SetupGraphics(); 82 } 83 } else if (str == "metrics") { 84 std::stringstream stream; 85 stream << "DIP (" << width_ << ", " << height_ << "), device pixels=(" 86 << pixel_width_ << ", " << pixel_height_ <<"), device_scale=" 87 << device_scale_ <<", css_scale=" << css_scale_; 88 PostMessage(stream.str()); 89 } 90 } 91 } 92 93 private: 94 void HandleMouseDown(const pp::InputEvent& event) { 95 pp::MouseInputEvent mouse_event(event); 96 pp::Point position(mouse_event.GetPosition()); 97 pp::Point position_device(position.x() * device_scale_, 98 position.y() * device_scale_); 99 std::stringstream stream; 100 stream << "Mousedown at DIP (" << position.x() << ", " << position.y() 101 << "), device pixel (" << position_device.x() << ", " 102 << position_device.y() << ")"; 103 if (css_scale_ > 0.0f) { 104 pp::Point position_css(position.x() / css_scale_, 105 position.y() / css_scale_); 106 stream << ", CSS pixel (" << position_css.x() << ", " << position_css.y() 107 <<")"; 108 } else { 109 stream <<", unknown CSS pixel. css_scale_=" << css_scale_; 110 } 111 PostMessage(stream.str()); 112 } 113 114 void SetupGraphics() { 115 if (using_device_pixels_) { 116 // The plugin will treat 1 pixel in the device context as 1 device pixel. 117 // This will set up a properly-sized pp::Graphics2D, and tell Pepper 118 // to apply the correct scale so the resulting concatenated scale leaves 119 // each pixel in the device context as one on the display device. 120 device_context_ = pp::Graphics2D(this, 121 pp::Size(pixel_width_, pixel_height_), 122 true); 123 if (device_scale_ > 0.0f) { 124 // If SetScale is promoted to pp::Graphics2D, the dc_dev constructor 125 // can be removed, and this will become the following line instead. 126 // device_context_.SetScale(1.0f / device_scale_); 127 device_context_.SetScale(1.0f / device_scale_); 128 } 129 } else { 130 // The plugin will treat 1 pixel in the device context as one DIP. 131 device_context_ = pp::Graphics2D(this, pp::Size(width_, height_), true); 132 } 133 BindGraphics(device_context_); 134 Paint(); 135 } 136 137 void Paint() { 138 int width = using_device_pixels_ ? pixel_width_ : width_; 139 int height = using_device_pixels_ ? pixel_height_ : height_; 140 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, 141 pp::Size(width, height), false); 142 if (image.is_null()) 143 return; 144 145 // Painting here will demonstrate a few techniques: 146 // - painting a thin blue box and cross-hatch to show the finest resolution 147 // available. 148 // - painting a 25 DIP (logical pixel) green circle to show how objects of a 149 // fixed size in DIPs should be scaled if using a high-resolution 150 // pp::Graphics2D. 151 // - paiting a 50 CSS pixel red circle to show how objects of a fixed size 152 // in CSS pixels should be scaled if using a high-resolution 153 // pp::Graphics2D, as well as how to use the GetCSSScale value properly. 154 155 // Painting in "DIP resolution" mode (|using_device_pixels_| false) will 156 // demonstrate how unscaled graphics would look, even on a high-DPI device. 157 // Painting in "device resolution" mode (|using_device_pixels_| true) will 158 // show how scaled graphics would look crisper on a high-DPI device, in 159 // comparison to using unscaled graphics. Both modes should look identical 160 // when displayed on a non-high-DPI device (window.devicePixelRatio == 1). 161 // Toggling between "DIP resolution" mode and "device resolution" mode 162 // should not change the sizes of the circles. 163 164 // Changing the browser zoom level should cause the CSS circle to zoom, but 165 // not the DIP-sized circle. 166 167 // All painting here does not use any anti-aliasing. 168 float circle_1_radius = 25; 169 if (using_device_pixels_) 170 circle_1_radius *= device_scale_; 171 172 float circle_2_radius = 50 * css_scale_; 173 if (using_device_pixels_) 174 circle_2_radius *= device_scale_; 175 176 for (int y = 0; y < height; ++y) { 177 char* row = static_cast<char*>(image.data()) + (y * image.stride()); 178 uint32_t* pixel = reinterpret_cast<uint32_t*>(row); 179 for (int x = 0; x < width; ++x) { 180 int dx = (width / 2) - x; 181 int dy = (height / 2) - y; 182 float dist_squared = (dx * dx) + (dy * dy); 183 if (x == 0 || y == 0 || x == width - 1 || y == width - 1 || x == y || 184 width - x - 1 == y) { 185 *pixel++ = 0xFF0000FF; 186 } else if (dist_squared < circle_1_radius * circle_1_radius) { 187 *pixel++ = 0xFF00FF00; 188 } else if (dist_squared < circle_2_radius * circle_2_radius) { 189 *pixel++ = 0xFFFF0000; 190 } else { 191 *pixel++ = 0xFF000000; 192 } 193 } 194 } 195 196 device_context_.ReplaceContents(&image); 197 device_context_.Flush(pp::CompletionCallback(&OnFlush, this)); 198 } 199 200 static void OnFlush(void* user_data, int32_t result) {} 201 202 pp::Graphics2D device_context_; 203 int width_; 204 int height_; 205 int pixel_width_; 206 int pixel_height_; 207 float device_scale_; 208 float css_scale_; 209 bool using_device_pixels_; 210 }; 211 212 // This object is the global object representing this plugin library as long as 213 // it is loaded. 214 class MyModule : public pp::Module { 215 public: 216 MyModule() : pp::Module() {} 217 virtual ~MyModule() {} 218 219 // Override CreateInstance to create your customized Instance object. 220 virtual pp::Instance* CreateInstance(PP_Instance instance) { 221 return new MyInstance(instance); 222 } 223 }; 224 225 namespace pp { 226 227 // Factory function for your specialization of the Module object. 228 Module* CreateModule() { 229 return new MyModule(); 230 } 231 232 } // namespace pp 233