Home | History | Annotate | Download | only in scaling
      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