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.
12 #include "ppapi/c/pp_resource.h"
13 #include "ppapi/c/ppb_core.h"
14 #include "ppapi/c/ppb_fullscreen.h"
15 #include "ppapi/c/ppb_graphics_2d.h"
16 #include "ppapi/c/ppb_image_data.h"
17 #include "ppapi/c/ppb_input_event.h"
18 #include "ppapi/c/ppb_instance.h"
19 #include "ppapi/c/ppb_view.h"
21 #include "ppapi_simple/ps_event.h"
22 #include "ppapi_simple/ps_main.h"
25 PPB_Fullscreen
* g_pFullscreen
;
26 PPB_Graphics2D
* g_pGraphics2D
;
27 PPB_ImageData
* g_pImageData
;
28 PPB_Instance
* g_pInstance
;
30 PPB_InputEvent
* g_pInputEvent
;
31 PPB_KeyboardInputEvent
* g_pKeyboardInput
;
32 PPB_MouseInputEvent
* g_pMouseInput
;
33 PPB_TouchInputEvent
* g_pTouchInput
;
44 const unsigned int kInitialRandSeed
= 0xC0DE533D;
46 /* BGRA helper macro, for constructing a pixel for a BGRA buffer. */
47 #define MakeBGRA(b, g, r, a) \
48 (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
52 * Convert a count value into a live (green) or dead color value.
54 const uint32_t kNeighborColors
[] = {
55 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
56 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
57 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
58 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
59 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
60 MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
61 MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
62 MakeBGRA(0x00, 0xFF, 0x00, 0xFF),
63 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
64 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
65 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
66 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
67 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
68 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
69 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
70 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
71 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
72 MakeBGRA(0x00, 0x00, 0x00, 0xFF),
76 * These represent the new health value of a cell based on its neighboring
77 * values. The health is binary: either alive or dead.
79 const uint8_t kIsAlive
[] = {
80 0, 0, 0, 0, 0, 1, 1, 1, 0,
81 0, 0, 0, 0, 0, 0, 0, 0, 0
84 void UpdateContext(uint32_t width
, uint32_t height
) {
85 if (width
!= g_Context
.size
.width
|| height
!= g_Context
.size
.height
) {
86 size_t size
= width
* height
;
89 free(g_Context
.cell_in
);
90 free(g_Context
.cell_out
);
92 /* Create a new context */
93 g_Context
.cell_in
= (uint8_t*) malloc(size
);
94 g_Context
.cell_out
= (uint8_t*) malloc(size
);
96 memset(g_Context
.cell_out
, 0, size
);
97 for (index
= 0; index
< size
; index
++) {
98 g_Context
.cell_in
[index
] = rand() & 1;
102 /* Recreate the graphics context on a view change */
103 g_pCore
->ReleaseResource(g_Context
.ctx
);
104 g_Context
.size
.width
= width
;
105 g_Context
.size
.height
= height
;
107 g_pGraphics2D
->Create(PSGetInstanceId(), &g_Context
.size
, PP_TRUE
);
109 g_pInstance
->BindGraphics(PSGetInstanceId(), g_Context
.ctx
);
112 void DrawCell(int32_t x
, int32_t y
) {
113 int32_t width
= g_Context
.size
.width
;
114 int32_t height
= g_Context
.size
.height
;
116 if (!g_Context
.cell_in
) return;
118 if (x
> 0 && x
< width
- 1 && y
> 0 && y
< height
- 1) {
119 g_Context
.cell_in
[x
- 1 + y
* width
] = 1;
120 g_Context
.cell_in
[x
+ 1 + y
* width
] = 1;
121 g_Context
.cell_in
[x
+ (y
- 1) * width
] = 1;
122 g_Context
.cell_in
[x
+ (y
+ 1) * width
] = 1;
126 void ProcessTouchEvent(PSEvent
* event
) {
127 uint32_t count
= g_pTouchInput
->GetTouchCount(event
->as_resource
,
128 PP_TOUCHLIST_TYPE_TOUCHES
);
130 for (i
= 0; i
< count
; i
++) {
131 struct PP_TouchPoint touch
= g_pTouchInput
->GetTouchByIndex(
132 event
->as_resource
, PP_TOUCHLIST_TYPE_TOUCHES
, i
);
133 int radius
= (int)touch
.radius
.x
;
134 int x
= (int)touch
.position
.x
;
135 int y
= (int)touch
.position
.y
;
136 /* num = 1/100th the area of touch point */
137 int num
= (int)(M_PI
* radius
* radius
/ 100.0f
);
138 for (j
= 0; j
< num
; j
++) {
139 int dx
= rand() % (radius
* 2) - radius
;
140 int dy
= rand() % (radius
* 2) - radius
;
141 /* only plot random cells within the touch area */
142 if (dx
* dx
+ dy
* dy
<= radius
* radius
)
143 DrawCell(x
+ dx
, y
+ dy
);
148 void ProcessEvent(PSEvent
* event
) {
149 switch(event
->type
) {
150 /* If the view updates, build a new Graphics 2D Context */
151 case PSE_INSTANCE_DIDCHANGEVIEW
: {
154 g_pView
->GetRect(event
->as_resource
, &rect
);
155 UpdateContext(rect
.size
.width
, rect
.size
.height
);
159 case PSE_INSTANCE_HANDLEINPUT
: {
160 PP_InputEvent_Type type
= g_pInputEvent
->GetType(event
->as_resource
);
161 PP_InputEvent_Modifier modifiers
=
162 g_pInputEvent
->GetModifiers(event
->as_resource
);
165 case PP_INPUTEVENT_TYPE_MOUSEDOWN
:
166 case PP_INPUTEVENT_TYPE_MOUSEMOVE
: {
167 struct PP_Point location
=
168 g_pMouseInput
->GetPosition(event
->as_resource
);
169 /* If the button is down, draw */
170 if (modifiers
& PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN
) {
171 DrawCell(location
.x
, location
.y
);
176 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
177 case PP_INPUTEVENT_TYPE_TOUCHMOVE
:
178 ProcessTouchEvent(event
);
181 case PP_INPUTEVENT_TYPE_KEYDOWN
: {
182 PP_Bool fullscreen
= g_pFullscreen
->IsFullscreen(PSGetInstanceId());
183 g_pFullscreen
->SetFullscreen(PSGetInstanceId(),
184 fullscreen
? PP_FALSE
: PP_TRUE
);
191 /* case PSE_INSTANCE_HANDLEINPUT */
201 void Stir(uint32_t width
, uint32_t height
) {
203 if (g_Context
.cell_in
== NULL
|| g_Context
.cell_out
== NULL
)
206 for (i
= 0; i
< width
; ++i
) {
207 g_Context
.cell_in
[i
] = rand() & 1;
208 g_Context
.cell_in
[i
+ (height
- 1) * width
] = rand() & 1;
210 for (i
= 0; i
< height
; ++i
) {
211 g_Context
.cell_in
[i
* width
] = rand() & 1;
212 g_Context
.cell_in
[i
* width
+ (width
- 1)] = rand() & 1;
217 struct PP_Size
* psize
= &g_Context
.size
;
218 PP_ImageDataFormat format
= PP_IMAGEDATAFORMAT_BGRA_PREMUL
;
221 * Create a buffer to draw into. Since we are waiting until the next flush
222 * chrome has an opportunity to cache this buffer see ppb_graphics_2d.h.
225 g_pImageData
->Create(PSGetInstanceId(), format
, psize
, PP_FALSE
);
226 uint8_t* pixels
= g_pImageData
->Map(image
);
228 struct PP_ImageDataDesc desc
;
232 /* If we somehow have not allocated these pointers yet, skip this frame. */
233 if (!g_Context
.cell_in
|| !g_Context
.cell_out
) return;
235 /* Get the stride. */
236 g_pImageData
->Describe(image
, &desc
);
238 /* Stir up the edges to prevent the simulation from reaching steady state. */
239 Stir(desc
.size
.width
, desc
.size
.height
);
241 /* Do neighbor summation; apply rules, output pixel color. */
242 for (y
= 1; y
< desc
.size
.height
- 1; ++y
) {
243 uint8_t *src0
= (g_Context
.cell_in
+ (y
- 1) * desc
.size
.width
) + 1;
244 uint8_t *src1
= src0
+ desc
.size
.width
;
245 uint8_t *src2
= src1
+ desc
.size
.width
;
248 uint8_t *dst
= (g_Context
.cell_out
+ y
* desc
.size
.width
) + 1;
249 uint32_t *pixel_line
= (uint32_t*) (pixels
+ y
* desc
.stride
);
251 for (x
= 1; x
< (desc
.size
.width
- 1); ++x
) {
252 /* Jitter and sum neighbors. */
253 count
= src0
[-1] + src0
[0] + src0
[1] +
254 src1
[-1] + + src1
[1] +
255 src2
[-1] + src2
[0] + src2
[1];
256 /* Include center cell. */
257 count
= count
+ count
+ src1
[0];
258 /* Use table lookup indexed by count to determine pixel & alive state. */
259 color
= kNeighborColors
[count
];
260 *pixel_line
++ = color
;
261 *dst
++ = kIsAlive
[count
];
268 cell_temp
= g_Context
.cell_in
;
269 g_Context
.cell_in
= g_Context
.cell_out
;
270 g_Context
.cell_out
= cell_temp
;
272 /* Unmap the range, we no longer need it. */
273 g_pImageData
->Unmap(image
);
275 /* Replace the contexts, and block until it's on the screen. */
276 g_pGraphics2D
->ReplaceContents(g_Context
.ctx
, image
);
277 g_pGraphics2D
->Flush(g_Context
.ctx
, PP_BlockUntilComplete());
279 /* Release the image data, we no longer need it. */
280 g_pCore
->ReleaseResource(image
);
284 * Starting point for the module. We do not use main since it would
285 * collide with main in libppapi_cpp.
287 int example_main(int argc
, char *argv
[]) {
288 fprintf(stdout
,"Started main.\n");
289 g_pCore
= (PPB_Core
*)PSGetInterface(PPB_CORE_INTERFACE
);
290 g_pFullscreen
= (PPB_Fullscreen
*)PSGetInterface(PPB_FULLSCREEN_INTERFACE
);
291 g_pGraphics2D
= (PPB_Graphics2D
*)PSGetInterface(PPB_GRAPHICS_2D_INTERFACE
);
292 g_pInstance
= (PPB_Instance
*)PSGetInterface(PPB_INSTANCE_INTERFACE
);
293 g_pImageData
= (PPB_ImageData
*)PSGetInterface(PPB_IMAGEDATA_INTERFACE
);
294 g_pView
= (PPB_View
*)PSGetInterface(PPB_VIEW_INTERFACE
);
297 (PPB_InputEvent
*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE
);
298 g_pKeyboardInput
= (PPB_KeyboardInputEvent
*)
299 PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE
);
301 (PPB_MouseInputEvent
*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE
);
303 (PPB_TouchInputEvent
*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE
);
305 PSEventSetFilter(PSE_ALL
);
307 /* Process all waiting events without blocking */
309 while ((event
= PSEventTryAcquire()) != NULL
) {
311 PSEventRelease(event
);
314 /* Render a frame, blocking until complete. */
315 if (g_Context
.bound
) {
323 * Register the function to call once the Instance Object is initialized.
324 * see: pappi_simple/ps_main.h
326 PPAPI_SIMPLE_REGISTER_MAIN(example_main
);