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 6 #include <assert.h> 7 #include <math.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 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" 20 21 #include "ppapi_simple/ps_event.h" 22 #include "ppapi_simple/ps_main.h" 23 24 PPB_Core* g_pCore; 25 PPB_Fullscreen* g_pFullscreen; 26 PPB_Graphics2D* g_pGraphics2D; 27 PPB_ImageData* g_pImageData; 28 PPB_Instance* g_pInstance; 29 PPB_View* g_pView; 30 PPB_InputEvent* g_pInputEvent; 31 PPB_KeyboardInputEvent* g_pKeyboardInput; 32 PPB_MouseInputEvent* g_pMouseInput; 33 PPB_TouchInputEvent* g_pTouchInput; 34 35 struct { 36 PP_Resource ctx; 37 struct PP_Size size; 38 int bound; 39 uint8_t* cell_in; 40 uint8_t* cell_out; 41 } g_Context; 42 43 44 const unsigned int kInitialRandSeed = 0xC0DE533D; 45 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)) 49 50 51 /* 52 * Convert a count value into a live (green) or dead color value. 53 */ 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), 73 }; 74 75 /* 76 * These represent the new health value of a cell based on its neighboring 77 * values. The health is binary: either alive or dead. 78 */ 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 82 }; 83 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; 87 size_t index; 88 89 free(g_Context.cell_in); 90 free(g_Context.cell_out); 91 92 /* Create a new context */ 93 g_Context.cell_in = (uint8_t*) malloc(size); 94 g_Context.cell_out = (uint8_t*) malloc(size); 95 96 memset(g_Context.cell_out, 0, size); 97 for (index = 0; index < size; index++) { 98 g_Context.cell_in[index] = rand() & 1; 99 } 100 } 101 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; 106 g_Context.ctx = 107 g_pGraphics2D->Create(PSGetInstanceId(), &g_Context.size, PP_TRUE); 108 g_Context.bound = 109 g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx); 110 } 111 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; 115 116 if (!g_Context.cell_in) return; 117 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; 123 } 124 } 125 126 void ProcessTouchEvent(PSEvent* event) { 127 uint32_t count = g_pTouchInput->GetTouchCount(event->as_resource, 128 PP_TOUCHLIST_TYPE_TOUCHES); 129 uint32_t i, j; 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); 144 } 145 } 146 } 147 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: { 152 struct PP_Rect rect; 153 154 g_pView->GetRect(event->as_resource, &rect); 155 UpdateContext(rect.size.width, rect.size.height); 156 break; 157 } 158 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); 163 164 switch(type) { 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); 172 } 173 break; 174 } 175 176 case PP_INPUTEVENT_TYPE_TOUCHSTART: 177 case PP_INPUTEVENT_TYPE_TOUCHMOVE: 178 ProcessTouchEvent(event); 179 break; 180 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); 185 break; 186 } 187 188 default: 189 break; 190 } 191 /* case PSE_INSTANCE_HANDLEINPUT */ 192 break; 193 } 194 195 default: 196 break; 197 } 198 } 199 200 201 void Stir(uint32_t width, uint32_t height) { 202 int i; 203 if (g_Context.cell_in == NULL || g_Context.cell_out == NULL) 204 return; 205 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; 209 } 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; 213 } 214 } 215 216 void Render() { 217 struct PP_Size* psize = &g_Context.size; 218 PP_ImageDataFormat format = PP_IMAGEDATAFORMAT_BGRA_PREMUL; 219 220 /* 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. 223 */ 224 PP_Resource image = 225 g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE); 226 uint8_t* pixels = g_pImageData->Map(image); 227 228 struct PP_ImageDataDesc desc; 229 uint8_t* cell_temp; 230 uint32_t x, y; 231 232 /* If we somehow have not allocated these pointers yet, skip this frame. */ 233 if (!g_Context.cell_in || !g_Context.cell_out) return; 234 235 /* Get the stride. */ 236 g_pImageData->Describe(image, &desc); 237 238 /* Stir up the edges to prevent the simulation from reaching steady state. */ 239 Stir(desc.size.width, desc.size.height); 240 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; 246 int count; 247 uint32_t color; 248 uint8_t *dst = (g_Context.cell_out + y * desc.size.width) + 1; 249 uint32_t *pixel_line = (uint32_t*) (pixels + y * desc.stride); 250 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]; 262 ++src0; 263 ++src1; 264 ++src2; 265 } 266 } 267 268 cell_temp = g_Context.cell_in; 269 g_Context.cell_in = g_Context.cell_out; 270 g_Context.cell_out = cell_temp; 271 272 /* Unmap the range, we no longer need it. */ 273 g_pImageData->Unmap(image); 274 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()); 278 279 /* Release the image data, we no longer need it. */ 280 g_pCore->ReleaseResource(image); 281 } 282 283 /* 284 * Starting point for the module. We do not use main since it would 285 * collide with main in libppapi_cpp. 286 */ 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); 295 296 g_pInputEvent = 297 (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE); 298 g_pKeyboardInput = (PPB_KeyboardInputEvent*) 299 PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE); 300 g_pMouseInput = 301 (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE); 302 g_pTouchInput = 303 (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE); 304 305 PSEventSetFilter(PSE_ALL); 306 while (1) { 307 /* Process all waiting events without blocking */ 308 PSEvent* event; 309 while ((event = PSEventTryAcquire()) != NULL) { 310 ProcessEvent(event); 311 PSEventRelease(event); 312 } 313 314 /* Render a frame, blocking until complete. */ 315 if (g_Context.bound) { 316 Render(); 317 } 318 } 319 return 0; 320 } 321 322 /* 323 * Register the function to call once the Instance Object is initialized. 324 * see: pappi_simple/ps_main.h 325 */ 326 PPAPI_SIMPLE_REGISTER_MAIN(example_main); 327