Send a crash report when a hung process is detected.
[chromium-blink-merge.git] / native_client_sdk / src / examples / api / graphics_2d / graphics_2d.cc
blob7a56619c6634b0cf1a7e0dd3405069ecda6c6173
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.
5 #include <stdio.h>
6 #include <stdlib.h>
8 #include "ppapi/c/ppb_image_data.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/point.h"
15 #include "ppapi/utility/completion_callback_factory.h"
17 #ifdef WIN32
18 #undef PostMessage
19 // Allow 'this' in initializer list
20 #pragma warning(disable : 4355)
21 #endif
23 namespace {
25 static const int kMouseRadius = 20;
27 uint8_t RandUint8(uint8_t min, uint8_t max) {
28 uint64_t r = rand();
29 uint8_t result = static_cast<uint8_t>(r * (max - min + 1) / RAND_MAX) + min;
30 return result;
33 uint32_t MakeColor(uint8_t r, uint8_t g, uint8_t b) {
34 uint8_t a = 255;
35 PP_ImageDataFormat format = pp::ImageData::GetNativeImageDataFormat();
36 if (format == PP_IMAGEDATAFORMAT_BGRA_PREMUL) {
37 return (a << 24) | (r << 16) | (g << 8) | b;
38 } else {
39 return (a << 24) | (b << 16) | (g << 8) | r;
43 } // namespace
45 class Graphics2DInstance : public pp::Instance {
46 public:
47 explicit Graphics2DInstance(PP_Instance instance)
48 : pp::Instance(instance),
49 callback_factory_(this),
50 mouse_down_(false),
51 buffer_(NULL),
52 device_scale_(1.0f) {}
54 ~Graphics2DInstance() { delete[] buffer_; }
56 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
57 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
59 unsigned int seed = 1;
60 srand(seed);
61 CreatePalette();
62 return true;
65 virtual void DidChangeView(const pp::View& view) {
66 device_scale_ = view.GetDeviceScale();
67 pp::Size new_size = pp::Size(view.GetRect().width() * device_scale_,
68 view.GetRect().height() * device_scale_);
70 if (!CreateContext(new_size))
71 return;
73 // When flush_context_ is null, it means there is no Flush callback in
74 // flight. This may have happened if the context was not created
75 // successfully, or if this is the first call to DidChangeView (when the
76 // module first starts). In either case, start the main loop.
77 if (flush_context_.is_null())
78 MainLoop(0);
81 virtual bool HandleInputEvent(const pp::InputEvent& event) {
82 if (!buffer_)
83 return true;
85 if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN ||
86 event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE) {
87 pp::MouseInputEvent mouse_event(event);
89 if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_NONE)
90 return true;
92 mouse_ = pp::Point(mouse_event.GetPosition().x() * device_scale_,
93 mouse_event.GetPosition().y() * device_scale_);
94 mouse_down_ = true;
97 if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP)
98 mouse_down_ = false;
100 return true;
103 private:
104 void CreatePalette() {
105 for (int i = 0; i < 64; ++i) {
106 // Black -> Red
107 palette_[i] = MakeColor(i * 2, 0, 0);
108 palette_[i + 64] = MakeColor(128 + i * 2, 0, 0);
109 // Red -> Yellow
110 palette_[i + 128] = MakeColor(255, i * 4, 0);
111 // Yellow -> White
112 palette_[i + 192] = MakeColor(255, 255, i * 4);
116 bool CreateContext(const pp::Size& new_size) {
117 const bool kIsAlwaysOpaque = true;
118 context_ = pp::Graphics2D(this, new_size, kIsAlwaysOpaque);
119 // Call SetScale before BindGraphics so the image is scaled correctly on
120 // HiDPI displays.
121 context_.SetScale(1.0f / device_scale_);
122 if (!BindGraphics(context_)) {
123 fprintf(stderr, "Unable to bind 2d context!\n");
124 context_ = pp::Graphics2D();
125 return false;
128 // Allocate a buffer of palette entries of the same size as the new context.
129 buffer_ = new uint8_t[new_size.width() * new_size.height()];
130 size_ = new_size;
132 return true;
135 void Update() {
136 // Old-school fire technique cribbed from
137 // http://ionicsolutions.net/2011/12/30/demo-fire-effect/
138 UpdateCoals();
139 DrawMouse();
140 UpdateFlames();
143 void UpdateCoals() {
144 int width = size_.width();
145 int height = size_.height();
146 size_t span = 0;
148 // Draw two rows of random values at the bottom.
149 for (int y = height - 2; y < height; ++y) {
150 size_t offset = y * width;
151 for (int x = 0; x < width; ++x) {
152 // On a random chance, draw some longer strips of brighter colors.
153 if (span || RandUint8(1, 4) == 1) {
154 if (!span)
155 span = RandUint8(10, 20);
156 buffer_[offset + x] = RandUint8(128, 255);
157 span--;
158 } else {
159 buffer_[offset + x] = RandUint8(32, 96);
165 void UpdateFlames() {
166 int width = size_.width();
167 int height = size_.height();
168 for (int y = 1; y < height - 1; ++y) {
169 size_t offset = y * width;
170 for (int x = 1; x < width - 1; ++x) {
171 int sum = 0;
172 sum += buffer_[offset - width + x - 1];
173 sum += buffer_[offset - width + x + 1];
174 sum += buffer_[offset + x - 1];
175 sum += buffer_[offset + x + 1];
176 sum += buffer_[offset + width + x - 1];
177 sum += buffer_[offset + width + x];
178 sum += buffer_[offset + width + x + 1];
179 buffer_[offset - width + x] = sum / 7;
184 void DrawMouse() {
185 if (!mouse_down_)
186 return;
188 int width = size_.width();
189 int height = size_.height();
191 // Draw a circle at the mouse position.
192 int radius = kMouseRadius * device_scale_;
193 int cx = mouse_.x();
194 int cy = mouse_.y();
195 int minx = cx - radius <= 0 ? 1 : cx - radius;
196 int maxx = cx + radius >= width ? width - 1 : cx + radius;
197 int miny = cy - radius <= 0 ? 1 : cy - radius;
198 int maxy = cy + radius >= height ? height - 1 : cy + radius;
199 for (int y = miny; y < maxy; ++y) {
200 for (int x = minx; x < maxx; ++x) {
201 if ((x - cx) * (x - cx) + (y - cy) * (y - cy) < radius * radius)
202 buffer_[y * width + x] = RandUint8(192, 255);
207 void Paint() {
208 // See the comment above the call to ReplaceContents below.
209 PP_ImageDataFormat format = pp::ImageData::GetNativeImageDataFormat();
210 const bool kDontInitToZero = false;
211 pp::ImageData image_data(this, format, size_, kDontInitToZero);
213 uint32_t* data = static_cast<uint32_t*>(image_data.data());
214 if (!data)
215 return;
217 uint32_t num_pixels = size_.width() * size_.height();
218 size_t offset = 0;
219 for (uint32_t i = 0; i < num_pixels; ++i) {
220 data[offset] = palette_[buffer_[offset]];
221 offset++;
224 // Using Graphics2D::ReplaceContents is the fastest way to update the
225 // entire canvas every frame. According to the documentation:
227 // Normally, calling PaintImageData() requires that the browser copy
228 // the pixels out of the image and into the graphics context's backing
229 // store. This function replaces the graphics context's backing store
230 // with the given image, avoiding the copy.
232 // In the case of an animation, you will want to allocate a new image for
233 // the next frame. It is best if you wait until the flush callback has
234 // executed before allocating this bitmap. This gives the browser the
235 // option of caching the previous backing store and handing it back to
236 // you (assuming the sizes match). In the optimal case, this means no
237 // bitmaps are allocated during the animation, and the backing store and
238 // "front buffer" (which the module is painting into) are just being
239 // swapped back and forth.
241 context_.ReplaceContents(&image_data);
244 void MainLoop(int32_t) {
245 if (context_.is_null()) {
246 // The current Graphics2D context is null, so updating and rendering is
247 // pointless. Set flush_context_ to null as well, so if we get another
248 // DidChangeView call, the main loop is started again.
249 flush_context_ = context_;
250 return;
253 Update();
254 Paint();
255 // Store a reference to the context that is being flushed; this ensures
256 // the callback is called, even if context_ changes before the flush
257 // completes.
258 flush_context_ = context_;
259 context_.Flush(
260 callback_factory_.NewCallback(&Graphics2DInstance::MainLoop));
263 pp::CompletionCallbackFactory<Graphics2DInstance> callback_factory_;
264 pp::Graphics2D context_;
265 pp::Graphics2D flush_context_;
266 pp::Size size_;
267 pp::Point mouse_;
268 bool mouse_down_;
269 uint8_t* buffer_;
270 uint32_t palette_[256];
271 float device_scale_;
274 class Graphics2DModule : public pp::Module {
275 public:
276 Graphics2DModule() : pp::Module() {}
277 virtual ~Graphics2DModule() {}
279 virtual pp::Instance* CreateInstance(PP_Instance instance) {
280 return new Graphics2DInstance(instance);
284 namespace pp {
285 Module* CreateModule() { return new Graphics2DModule(); }
286 } // namespace pp