Chromium Code Reviews| Index: native_client_sdk/src/examples/demo/life/life.c |
| diff --git a/native_client_sdk/src/examples/demo/life/life.c b/native_client_sdk/src/examples/demo/life/life.c |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d16270b0975a8b172a2d16044edaea564f8afd3d |
| --- /dev/null |
| +++ b/native_client_sdk/src/examples/demo/life/life.c |
| @@ -0,0 +1,285 @@ |
| +// Copyright 2011 The Native Client SDK Authors. All rights reserved. |
|
binji
2013/05/23 18:06:49
2013
noelallen1
2013/05/30 18:07:18
Done.
|
| +// Use of this source code is governed by a BSD-style license that can |
| +// be found in the LICENSE file. |
| + |
| +#include <assert.h> |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| + |
| +#include "ppapi/c/pp_resource.h" |
| +#include "ppapi/c/ppb_core.h" |
| +#include "ppapi/c/ppb_graphics_2d.h" |
| +#include "ppapi/c/ppb_image_data.h" |
| +#include "ppapi/c/ppb_input_event.h" |
| +#include "ppapi/c/ppb_instance.h" |
| +#include "ppapi/c/ppb_view.h" |
| + |
| +#include "ppapi_simple/ppapi_event.h" |
| +#include "ppapi_simple/ppapi_simple_main.h" |
| + |
| +PPB_Core* g_pCore; |
| +PPB_Graphics2D* g_pGraphics2D; |
| +PPB_ImageData* g_pImageData; |
| +PPB_Instance* g_pInstance; |
| +PPB_View* g_pView; |
| +PPB_InputEvent* g_pInputEvent; |
| +PPB_KeyboardInputEvent* g_pKeyboardInput; |
| +PPB_MouseInputEvent* g_pMouseInput; |
| +PPB_TouchInputEvent* g_pTouchInput; |
| + |
| +struct { |
|
binji
2013/05/23 18:06:49
why store this is a struct instead of individual g
noelallen1
2013/05/23 22:01:24
Just for organization reasons.
|
| + PP_Resource ctx_; |
|
binji
2013/05/23 18:06:49
public members for structs don't have trailing _
noelallen1
2013/05/23 22:01:24
Done.
|
| + struct PP_Size size_; |
| + int bound_; |
| + uint8_t* cell_in_; |
| + uint8_t* cell_out_; |
| +} g_Context; |
| + |
| + |
| +const unsigned int kInitialRandSeed = 0xC0DE533D; |
| + |
| +#define MakeRGBA(r, g, b, a) \ |
| + (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) |
| + |
| +// Map of neighboring colors. |
|
binji
2013/05/23 18:06:49
these numbers seem pretty random. Maybe explain a
noelallen1
2013/05/23 22:01:24
Done.
|
| +const uint32_t kNeighborColors[] = { |
| + MakeRGBA(0x00, 0x00, 0x00, 0xff), |
| + MakeRGBA(0x00, 0x40, 0x00, 0xff), |
| + MakeRGBA(0x00, 0x60, 0x00, 0xff), |
| + MakeRGBA(0x00, 0x80, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xA0, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xC0, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xE0, 0x00, 0xff), |
| + MakeRGBA(0x00, 0x00, 0x00, 0xff), |
| + MakeRGBA(0x00, 0x40, 0x00, 0xff), |
| + MakeRGBA(0x00, 0x60, 0x00, 0xff), |
| + MakeRGBA(0x00, 0x80, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xA0, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xC0, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xE0, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| + MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| +}; |
| + |
| +// These represent the new health value of a cell based on its neighboring |
| +// values. The health is binary: either alive or dead. |
| +const uint8_t kIsAlive[] = { |
| + 0, 0, 0, 1, 0, 0, 0, 0, 0, // Values if the center cell is dead. |
| + 0, 0, 1, 1, 0, 0, 0, 0, 0 // Values if the center cell is alive. |
| +}; |
| + |
| + |
| +void UpdateContext(uint32_t width, uint32_t height) { |
| + size_t size = width * height; |
| + size_t index; |
| + |
| + free(g_Context.cell_in_); |
| + free(g_Context.cell_out_); |
| + |
| + // |
| + // Create a new context |
| + // |
| + g_Context.cell_in_ = (uint8_t*) malloc(size); |
| + g_Context.cell_out_ = (uint8_t*) malloc(size); |
| + |
| + memset(g_Context.cell_out_, 0, size); |
| + for (index = 0; index < size; index++) { |
| + g_Context.cell_in_[index] = rand() & 1; |
| + } |
| +} |
| + |
| +void ProcessEvent(PSEvent* event) { |
| + static int mouse_down = 0; |
|
binji
2013/05/23 18:06:49
unused
noelallen1
2013/05/23 22:01:24
Done.
|
| + |
| + switch(event->type_) { |
| + /* If the view updates, build a new Graphics 2D Context */ |
| + case PS_VIEW_EVENT: { |
| + struct PP_Rect rect; |
| + int32_t width; |
| + int32_t height; |
| + if (g_Context.ctx_) { |
| + g_pCore->ReleaseResource(g_Context.ctx_); |
| + g_Context.ctx_ = 0; |
| + } |
| + |
| + g_pView->GetRect(event->resource_, &rect); |
| + width = rect.size.width; |
| + height = rect.size.height; |
| + |
| + if (width != g_Context.size_.width || height != g_Context.size_.height) { |
| + UpdateContext(rect.size.width, rect.size.height); |
| + } |
| + if (width != g_Context.size_.width || height != g_Context.size_.height || |
| + !g_Context.bound_) { |
| + g_Context.ctx_ = |
| + g_pGraphics2D->Create(PSGetInstanceId(), &rect.size, PP_TRUE); |
| + g_Context.bound_ = |
| + g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx_); |
| + g_Context.size_ = rect.size; |
| + } |
| + break; |
| + } |
| + |
| + case PS_INPUT_EVENT: { |
|
nfullagar1
2013/05/23 21:58:36
I'm not super keen on how PS is defining a PS_INPU
noelallen1
2013/05/30 18:07:18
Done.
|
| + PP_InputEvent_Type type = g_pInputEvent->GetType(event->resource_); |
| + PP_InputEvent_Modifier modifiers = |
| + g_pInputEvent->GetModifiers(event->resource_); |
| + int32_t width = g_Context.size_.width; |
| + int32_t height = g_Context.size_.height; |
| + |
| + switch(type) { |
| + case PP_INPUTEVENT_TYPE_MOUSEMOVE: |
| + { |
| + struct PP_Point location = |
| + g_pMouseInput->GetPosition(event->resource_); |
| + |
| + if (!g_Context.cell_in_) break; |
| + |
| + // If the button is down, draw |
| + if (modifiers & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { |
| + int x = location.x; |
| + int y = location.y; |
| + |
| + if (x > 0 && x < width && y > 0 && y < height) { |
|
binji
2013/05/23 18:06:49
x < width - 1
y < height - 1
noelallen1
2013/05/23 22:01:24
Done.
binji
2013/05/30 05:07:50
Missed this one.
noelallen1
2013/05/30 18:07:18
Done.
|
| + g_Context.cell_in_[x-1 + y * width] = 1; |
| + g_Context.cell_in_[x+1 + y * width] = 1; |
| + g_Context.cell_in_[x + (y - 1) * width] = 1; |
| + g_Context.cell_in_[x + (y + 1) * width] = 1; |
| + } |
| + } |
| + } |
| + break; |
| + |
| + case PP_INPUTEVENT_TYPE_KEYUP: { |
| + if (g_pKeyboardInput->GetKeyCode(event->resource_) == 13) { |
|
binji
2013/05/23 18:06:49
enter key
noelallen1
2013/05/30 18:07:18
Done.
|
| + // PSToggleFullscreen(); |
|
binji
2013/05/23 18:06:49
does this not work?
noelallen1
2013/05/23 22:01:24
Done.
|
| + } |
| + break; |
| + } |
| + |
| + default: |
| + break; |
| + } |
| + break; |
| + } |
| + |
| + default: |
| + break; |
| + } |
| +} |
| + |
| + |
| +void Stir(uint32_t width, uint32_t height) { |
| + int i; |
| + if (g_Context.cell_in_ == NULL || g_Context.cell_out_ == NULL) |
| + return; |
| + |
| + for (i = 0; i < width; ++i) { |
| + g_Context.cell_in_[i] = rand() & 1; |
|
binji
2013/05/23 18:06:49
probably not a big deal, but I always heard that t
|
| + g_Context.cell_in_[i + (height - 1) * width] = rand() & 1; |
| + } |
| + for (i = 0; i < height; ++i) { |
| + g_Context.cell_in_[i * width] = rand() & 1; |
| + g_Context.cell_in_[i * width + (width - 1)] = rand() & 1; |
| + } |
| +} |
| + |
| +void Render() { |
| + struct PP_Size* psize = &g_Context.size_; |
| + PP_ImageDataFormat format = g_pImageData->GetNativeImageDataFormat(); |
| + PP_Resource image = |
| + g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE); |
|
binji
2013/05/23 18:06:49
seems strange to create a new image every frame. M
noelallen1
2013/05/23 22:01:24
Done.
|
| + uint8_t* pixels = g_pImageData->Map(image); |
| + |
| + struct PP_ImageDataDesc desc; |
| + uint8_t* cell_temp; |
| + uint32_t x, y; |
| + |
| + // If we somehow have not allocated these pointers yet, skip this frame. |
| + if (!g_Context.cell_in_ || !g_Context.cell_out_) return; |
| + |
| + // Get the stride |
| + g_pImageData->Describe(image, &desc); |
|
nfullagar1
2013/05/23 21:58:36
For consistency, use the desc.size.width & height
noelallen1
2013/05/24 18:08:31
Done.
|
| + |
| + // Stir up the edges to prevent the simulation from reaching steady state. |
| + Stir(psize->width, psize->height); |
| + |
| + // Do neighbor sumation; apply rules, output pixel color. |
|
binji
2013/05/23 18:06:49
sp: summation
noelallen1
2013/05/23 22:01:24
Done.
|
| + for (y = 1; y < (psize->height - 1); ++y) { |
|
binji
2013/05/23 18:06:49
why ()?
noelallen1
2013/05/23 22:01:24
Done.
|
| + uint8_t *src0 = (g_Context.cell_in_ + (y - 1) * psize->width) + 1; |
| + uint8_t *src1 = src0 + psize->width; |
| + uint8_t *src2 = src1 + psize->width; |
| + int count; |
| + uint32_t color; |
| + uint8_t *dst = (g_Context.cell_out_ + (y) * psize->width) + 1; |
|
binji
2013/05/23 18:06:49
why (y)?
noelallen1
2013/05/23 22:01:24
Done.
|
| + uint32_t *pixel_line = (uint32_t*) (pixels + y * desc.stride); |
| + |
| + for (x = 1; x < (psize->width - 1); ++x) { |
| + // Build sum, weight center by 9x. |
| + count = src0[-1] + src0[0] + src0[1] + |
| + src1[-1] + src1[0] * 9 + src1[1] + |
| + src2[-1] + src2[0] + src2[1]; |
| + color = kNeighborColors[count]; |
| + |
| + *pixel_line++ = color; |
| + *dst++ = kIsAlive[count]; |
| + ++src0; |
| + ++src1; |
| + ++src2; |
| + } |
| + } |
| + |
| + cell_temp = g_Context.cell_in_; |
| + g_Context.cell_in_ = g_Context.cell_out_; |
| + g_Context.cell_out_ = cell_temp; |
| + |
| + /* Unmap the range, we no longer need it. */ |
| + g_pImageData->Unmap(image); |
| + |
| + /* Replace the contexts, and block until it's on the screen. */ |
| + g_pGraphics2D->ReplaceContents(g_Context.ctx_, image); |
| + g_pGraphics2D->Flush(g_Context.ctx_, PP_BlockUntilComplete()); |
| + |
| + /* Release the image data, we no longer need it. */ |
| + g_pCore->ReleaseResource(image); |
| +} |
| + |
| + |
| +int my_main(int argc, const char *argv[]) { |
|
binji
2013/05/23 18:06:49
might want to mention this can be called anything
noelallen1
2013/05/23 22:01:24
Done.
|
| + fprintf(stdout,"Started main.\n"); |
| + g_pCore = (PPB_Core*)PSGetInterface(PPB_CORE_INTERFACE); |
| + g_pGraphics2D = (PPB_Graphics2D*)PSGetInterface(PPB_GRAPHICS_2D_INTERFACE); |
| + g_pInstance = (PPB_Instance*)PSGetInterface(PPB_INSTANCE_INTERFACE); |
| + g_pImageData = (PPB_ImageData*)PSGetInterface(PPB_IMAGEDATA_INTERFACE); |
| + g_pView = (PPB_View*)PSGetInterface(PPB_VIEW_INTERFACE); |
| + |
| + g_pInputEvent = |
| + (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE); |
| + g_pKeyboardInput = (PPB_KeyboardInputEvent*) |
| + PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE); |
| + g_pMouseInput = |
| + (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE); |
| + g_pTouchInput = |
|
binji
2013/05/23 18:06:49
unused
noelallen1
2013/05/30 18:07:18
Done.
|
| + (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE); |
| + |
| + PSSetEventFilter(PS_ALL_EVENTS); |
| + while (1) { |
| + /* Process all waiting events without blocking */ |
| + PSEvent* event; |
| + while ((event = PSAcquireEvent(0)) != NULL) { |
|
binji
2013/05/23 18:06:49
what is 0?
noelallen1
2013/05/23 22:01:24
Means don't block, see comment above.
|
| + ProcessEvent(event); |
| + PSReleaseEvent(event); |
| + } |
| + |
| + /* Render a frame, blocking until complete. */ |
| + if (g_Context.bound_) { |
| + Render(); |
| + } |
| + } |
| + return 0; |
| +} |
| + |
|
nfullagar1
2013/05/23 21:58:36
How would this sample differ if PPAPI_SIMPLE_MAIN2
noelallen1
2013/05/24 18:08:31
The idea is that PPAPI_SIMPLE_MAIN2D would automat
|
| +PPAPI_SIMPLE_MAIN(my_main); |
|
binji
2013/05/23 18:06:49
what is this doing? Maybe a little comment.
noelallen1
2013/05/23 22:01:24
Done.
|