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