Home | History | Annotate | Download | only in life
      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