1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
|
// 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 <sstream>
#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<char*>(image.data()) + (y * image.stride());
uint32_t* pixel = reinterpret_cast<uint32_t*>(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
|