life.c revision 3551c9c881056c480085172ff9840cab31610854
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 <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10 11#include "ppapi/c/pp_resource.h" 12#include "ppapi/c/ppb_core.h" 13#include "ppapi/c/ppb_fullscreen.h" 14#include "ppapi/c/ppb_graphics_2d.h" 15#include "ppapi/c/ppb_image_data.h" 16#include "ppapi/c/ppb_input_event.h" 17#include "ppapi/c/ppb_instance.h" 18#include "ppapi/c/ppb_view.h" 19 20#include "ppapi_simple/ps_event.h" 21#include "ppapi_simple/ps_main.h" 22 23PPB_Core* g_pCore; 24PPB_Fullscreen* g_pFullscreen; 25PPB_Graphics2D* g_pGraphics2D; 26PPB_ImageData* g_pImageData; 27PPB_Instance* g_pInstance; 28PPB_View* g_pView; 29PPB_InputEvent* g_pInputEvent; 30PPB_KeyboardInputEvent* g_pKeyboardInput; 31PPB_MouseInputEvent* g_pMouseInput; 32PPB_TouchInputEvent* g_pTouchInput; 33 34struct { 35 PP_Resource ctx; 36 struct PP_Size size; 37 int bound; 38 uint8_t* cell_in; 39 uint8_t* cell_out; 40} g_Context; 41 42 43const unsigned int kInitialRandSeed = 0xC0DE533D; 44 45/* BGRA helper macro, for constructing a pixel for a BGRA buffer. */ 46#define MakeBGRA(b, g, r, a) \ 47 (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) 48 49 50/* 51 * Given a count of cells in a 3x3 grid where cells are worth 1 except for 52 * the center which is worth 9, this is a color representation of how 53 * "alive" that cell is making for a more interesting representation than 54 * a binary alive or dead. 55 */ 56const uint32_t kNeighborColors[] = { 57 MakeBGRA(0x00, 0x00, 0x00, 0xff), 58 MakeBGRA(0x00, 0x40, 0x00, 0xff), 59 MakeBGRA(0x00, 0x60, 0x00, 0xff), 60 MakeBGRA(0x00, 0x80, 0x00, 0xff), 61 MakeBGRA(0x00, 0xA0, 0x00, 0xff), 62 MakeBGRA(0x00, 0xC0, 0x00, 0xff), 63 MakeBGRA(0x00, 0xE0, 0x00, 0xff), 64 MakeBGRA(0x00, 0x00, 0x00, 0xff), 65 MakeBGRA(0x00, 0x40, 0x00, 0xff), 66 MakeBGRA(0x00, 0x60, 0x00, 0xff), 67 MakeBGRA(0x00, 0x80, 0x00, 0xff), 68 MakeBGRA(0x00, 0xA0, 0x00, 0xff), 69 MakeBGRA(0x00, 0xC0, 0x00, 0xff), 70 MakeBGRA(0x00, 0xE0, 0x00, 0xff), 71 MakeBGRA(0x00, 0xFF, 0x00, 0xff), 72 MakeBGRA(0x00, 0xFF, 0x00, 0xff), 73 MakeBGRA(0x00, 0xFF, 0x00, 0xff), 74 MakeBGRA(0x00, 0xFF, 0x00, 0xff), 75}; 76 77/* 78 * These represent the new health value of a cell based on its neighboring 79 * values. The health is binary: either alive or dead. 80 */ 81const uint8_t kIsAlive[] = { 82 0, 0, 0, 1, 0, 0, 0, 0, 0, /* Values if the center cell is dead. */ 83 0, 0, 1, 1, 0, 0, 0, 0, 0 /* Values if the center cell is alive. */ 84}; 85 86void UpdateContext(uint32_t width, uint32_t height) { 87 if (width != g_Context.size.width || height != g_Context.size.height) { 88 size_t size = width * height; 89 size_t index; 90 91 free(g_Context.cell_in); 92 free(g_Context.cell_out); 93 94 /* Create a new context */ 95 g_Context.cell_in = (uint8_t*) malloc(size); 96 g_Context.cell_out = (uint8_t*) malloc(size); 97 98 memset(g_Context.cell_out, 0, size); 99 for (index = 0; index < size; index++) { 100 g_Context.cell_in[index] = rand() & 1; 101 } 102 } 103 104 /* Recreate the graphics context on a view change */ 105 g_pCore->ReleaseResource(g_Context.ctx); 106 g_Context.size.width = width; 107 g_Context.size.height = height; 108 g_Context.ctx = 109 g_pGraphics2D->Create(PSGetInstanceId(), &g_Context.size, PP_TRUE); 110 g_Context.bound = 111 g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx); 112} 113 114void DrawCell(int32_t x, int32_t y) { 115 int32_t width = g_Context.size.width; 116 int32_t height = g_Context.size.height; 117 118 if (!g_Context.cell_in) return; 119 120 if (x > 0 && x < width - 1 && y > 0 && y < height - 1) { 121 g_Context.cell_in[x - 1 + y * width] = 1; 122 g_Context.cell_in[x + 1 + y * width] = 1; 123 g_Context.cell_in[x + (y - 1) * width] = 1; 124 g_Context.cell_in[x + (y + 1) * width] = 1; 125 } 126} 127 128 129void ProcessEvent(PSEvent* event) { 130 switch(event->type) { 131 /* If the view updates, build a new Graphics 2D Context */ 132 case PSE_INSTANCE_DIDCHANGEVIEW: { 133 struct PP_Rect rect; 134 135 g_pView->GetRect(event->as_resource, &rect); 136 UpdateContext(rect.size.width, rect.size.height); 137 break; 138 } 139 140 case PSE_INSTANCE_HANDLEINPUT: { 141 PP_InputEvent_Type type = g_pInputEvent->GetType(event->as_resource); 142 PP_InputEvent_Modifier modifiers = 143 g_pInputEvent->GetModifiers(event->as_resource); 144 145 switch(type) { 146 case PP_INPUTEVENT_TYPE_MOUSEDOWN: { 147 struct PP_Point location = 148 g_pMouseInput->GetPosition(event->as_resource); 149 DrawCell(location.x, location.y); 150 break; 151 } 152 153 case PP_INPUTEVENT_TYPE_MOUSEMOVE: { 154 struct PP_Point location = 155 g_pMouseInput->GetPosition(event->as_resource); 156 157 /* If the button is down, draw */ 158 if (modifiers & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { 159 DrawCell(location.x, location.y); 160 } 161 break; 162 } 163 164 case PP_INPUTEVENT_TYPE_KEYDOWN: { 165 PP_Bool fullscreen = g_pFullscreen->IsFullscreen(PSGetInstanceId()); 166 g_pFullscreen->SetFullscreen(PSGetInstanceId(), 167 fullscreen ? PP_FALSE : PP_TRUE); 168 break; 169 } 170 default: 171 break; 172 } 173 /* case PSE_INSTANCE_HANDLEINPUT */ 174 break; 175 } 176 177 default: 178 break; 179 } 180} 181 182 183void Stir(uint32_t width, uint32_t height) { 184 int i; 185 if (g_Context.cell_in == NULL || g_Context.cell_out == NULL) 186 return; 187 188 for (i = 0; i < width; ++i) { 189 g_Context.cell_in[i] = rand() & 1; 190 g_Context.cell_in[i + (height - 1) * width] = rand() & 1; 191 } 192 for (i = 0; i < height; ++i) { 193 g_Context.cell_in[i * width] = rand() & 1; 194 g_Context.cell_in[i * width + (width - 1)] = rand() & 1; 195 } 196} 197 198void Render() { 199 struct PP_Size* psize = &g_Context.size; 200 PP_ImageDataFormat format = g_pImageData->GetNativeImageDataFormat(); 201 202 /* 203 * Create a buffer to draw into. Since we are waiting until the next flush 204 * chrome has an opportunity to cache this buffer see ppb_graphics_2d.h. 205 */ 206 PP_Resource image = 207 g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE); 208 uint8_t* pixels = g_pImageData->Map(image); 209 210 struct PP_ImageDataDesc desc; 211 uint8_t* cell_temp; 212 uint32_t x, y; 213 214 /* If we somehow have not allocated these pointers yet, skip this frame. */ 215 if (!g_Context.cell_in || !g_Context.cell_out) return; 216 217 /* Get the stride. */ 218 g_pImageData->Describe(image, &desc); 219 220 /* Stir up the edges to prevent the simulation from reaching steady state. */ 221 Stir(desc.size.width, desc.size.height); 222 223 /* Do neighbor summation; apply rules, output pixel color. */ 224 for (y = 1; y < desc.size.height - 1; ++y) { 225 uint8_t *src0 = (g_Context.cell_in + (y - 1) * desc.size.width) + 1; 226 uint8_t *src1 = src0 + desc.size.width; 227 uint8_t *src2 = src1 + desc.size.width; 228 int count; 229 uint32_t color; 230 uint8_t *dst = (g_Context.cell_out + y * desc.size.width) + 1; 231 uint32_t *pixel_line = (uint32_t*) (pixels + y * desc.stride); 232 233 for (x = 1; x < (desc.size.width - 1); ++x) { 234 /* Build sum, weight center by 9x. */ 235 count = src0[-1] + src0[0] + src0[1] + 236 src1[-1] + src1[0] * 9 + src1[1] + 237 src2[-1] + src2[0] + src2[1]; 238 color = kNeighborColors[count]; 239 240 *pixel_line++ = color; 241 *dst++ = kIsAlive[count]; 242 ++src0; 243 ++src1; 244 ++src2; 245 } 246 } 247 248 cell_temp = g_Context.cell_in; 249 g_Context.cell_in = g_Context.cell_out; 250 g_Context.cell_out = cell_temp; 251 252 /* Unmap the range, we no longer need it. */ 253 g_pImageData->Unmap(image); 254 255 /* Replace the contexts, and block until it's on the screen. */ 256 g_pGraphics2D->ReplaceContents(g_Context.ctx, image); 257 g_pGraphics2D->Flush(g_Context.ctx, PP_BlockUntilComplete()); 258 259 /* Release the image data, we no longer need it. */ 260 g_pCore->ReleaseResource(image); 261} 262 263/* 264 * Starting point for the module. We do not use main since it would 265 * collide with main in libppapi_cpp. 266 */ 267int example_main(int argc, char *argv[]) { 268 fprintf(stdout,"Started main.\n"); 269 g_pCore = (PPB_Core*)PSGetInterface(PPB_CORE_INTERFACE); 270 g_pFullscreen = (PPB_Fullscreen*)PSGetInterface(PPB_FULLSCREEN_INTERFACE); 271 g_pGraphics2D = (PPB_Graphics2D*)PSGetInterface(PPB_GRAPHICS_2D_INTERFACE); 272 g_pInstance = (PPB_Instance*)PSGetInterface(PPB_INSTANCE_INTERFACE); 273 g_pImageData = (PPB_ImageData*)PSGetInterface(PPB_IMAGEDATA_INTERFACE); 274 g_pView = (PPB_View*)PSGetInterface(PPB_VIEW_INTERFACE); 275 276 g_pInputEvent = 277 (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE); 278 g_pKeyboardInput = (PPB_KeyboardInputEvent*) 279 PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE); 280 g_pMouseInput = 281 (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE); 282 g_pTouchInput = 283 (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE); 284 285 PSEventSetFilter(PSE_ALL); 286 while (1) { 287 /* Process all waiting events without blocking */ 288 PSEvent* event; 289 while ((event = PSEventTryAcquire()) != NULL) { 290 ProcessEvent(event); 291 PSEventRelease(event); 292 } 293 294 /* Render a frame, blocking until complete. */ 295 if (g_Context.bound) { 296 Render(); 297 } 298 } 299 return 0; 300} 301 302/* 303 * Register the function to call once the Instance Object is initialized. 304 * see: pappi_simple/ps_main.h 305 */ 306PPAPI_SIMPLE_REGISTER_MAIN(example_main); 307