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