2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 simple demo for nacl client, computed mandelbrot set
42 #include <sys/types.h>
46 #include <nacl/nacl_srpc.h>
47 #include <nacl/nacl_av.h>
49 #define FPEPSILON 1.0e-10
50 #define FPEQUAL(x, y) (abs(x - y) < FPEPSILON)
52 #define IND(arr, width, i, j) *(arr + (i) * (width) + (j))
55 #define BASEHEIGHT 500
57 pthread_mutex_t work_mu
= PTHREAD_MUTEX_INITIALIZER
;
58 pthread_cond_t work_cv
= PTHREAD_COND_INITIALIZER
;
60 enum work_id
{ WORK_SETUP
, WORK_SHUTDOWN
, WORK_SET_REGION
, WORK_DISPLAY
,
69 double setup_canvas_width
;
71 double new_x_left
, new_y_top
, new_x_right
, new_y_bottom
;
77 struct work_item
*work
= NULL
;
79 __thread
struct work_item tls_item
;
80 __thread pthread_mutex_t tls_mu
= PTHREAD_MUTEX_INITIALIZER
;
81 __thread pthread_cond_t tls_cv
= PTHREAD_COND_INITIALIZER
;
82 __thread
int tls_done
;
84 void work_put(struct work_item
*item
) {
87 item
->done
= &tls_done
;
90 pthread_mutex_lock(&work_mu
);
91 while (NULL
!= work
) {
92 pthread_cond_wait(&work_cv
, &work_mu
);
95 pthread_cond_broadcast(&work_cv
);
96 pthread_mutex_unlock(&work_mu
);
98 pthread_mutex_lock(item
->mu
);
99 while (0 == *item
->done
) {
100 pthread_cond_wait(item
->cv
, item
->mu
);
102 pthread_mutex_unlock(item
->mu
);
105 struct work_item
*work_get(void) {
106 struct work_item
*cur
;
108 pthread_mutex_lock(&work_mu
);
109 while (NULL
== (cur
= work
)) {
110 pthread_cond_wait(&work_cv
, &work_mu
);
112 pthread_mutex_unlock(&work_mu
);
117 void work_mark_done(struct work_item
*item
) {
118 pthread_mutex_lock(item
->mu
);
120 pthread_cond_broadcast(item
->cv
);
121 pthread_mutex_unlock(item
->mu
);
123 pthread_mutex_lock(&work_mu
);
125 pthread_cond_broadcast(&work_cv
);
126 pthread_mutex_unlock(&work_mu
);
132 typedef struct Rgb_
{
138 typedef struct Pixel_
{
146 * Module static variables.
150 * Display memory related variables.
152 static char display_mem
[BASEWIDTH
* BASEHEIGHT
* sizeof(Pixel
)];
153 static char pixmap
[BASEWIDTH
* BASEHEIGHT
];
154 static char mask
[BASEWIDTH
* BASEHEIGHT
];
157 * Mandelbrot set range values.
158 * The first four variables are set to "funny" values to force a complete
159 * computation of the client-requested region on the first invocation.
161 static double x_left
= -MAXFLOAT
;
162 static double y_top
= MAXFLOAT
;
163 static double x_right
= MAXFLOAT
;
164 static double y_bottom
= -MAXFLOAT
;
167 static double new_x_left
;
168 static double new_y_top
;
169 static double new_x_right
;
170 static double new_y_bottom
;
174 * The pixel map is a canvas_width * canvas_width array of palette indices.
176 static double canvas_width
;
177 static unsigned int palette_select
= 0;
178 static unsigned int color_interpolation_step
= 0;
181 * Palette indices select one of 256 different colors from a palette that
182 * may be changing (psychedelic mode).
184 #define kPsychInterpSteps 4
185 static Pixel palette
[256][256];
188 * Interpolate ARGB values from the starting colors.
190 static void InterpolateColors() {
191 static const Rgb base_colors
[] = {
193 * Starting color values are inspired by those used by the Xaos
194 * visualizer (sadly not the same as their palette). We picked
195 * some nearby colors using a color wheel site.
197 { 0, 0, 0}, /* black */
198 { 30, 144, 255}, /* blue */
199 { 32, 13, 33}, /* dim violet */
200 {222, 85, 34}, /* red-orange */
201 { 17, 10, 6}, /* dim violet */
202 {135, 46, 71}, /* dim orange */
203 { 53, 60, 28}, /* dim greenish-yellow */
204 {216, 204, 94}, /* yellow */
205 { 34, 58, 46}, /* dim light green */
206 {220, 146, 139}, /* reddish / salmon */
207 { 24, 11, 53}, /* dark blue */
208 {133, 115, 209}, /* blue-purple */
209 { 64, 50, 37}, /* dim orange */
210 { 26, 161, 33}, /* green */
211 { 28, 21, 61}, /* dim blue */
212 { 29, 114, 53}, /* green */
213 { 39, 5, 42}, /* dim violet */
214 {163, 198, 247}, /* sky blue */
215 { 10, 51, 14}, /* dim green */
216 {211, 132, 10}, /* orange */
217 { 17, 25, 42}, /* dim blue */
218 {166, 138, 68}, /* yellow-orange */
219 { 25, 46, 10}, /* dim mint green */
220 {190, 252, 177}, /* bright mint green */
221 { 12, 6, 62}, /* dim blue */
222 { 46, 67, 32}, /* dim yellow-gren */
223 { 23, 51, 53}, /* dim sky blue */
224 {146, 78, 186}, /* violet */
225 { 11, 6, 15}, /* dim violet */
226 {227, 87, 216}, /* pink-purple */
227 { 47, 49, 28}, /* dim yellow */
228 { 19, 28, 55} /* dim blue */
231 const int colors
= sizeof(base_colors
) / sizeof(Rgb
);
232 const int palette_entries
= 256;
233 const int entries_per_color
= palette_entries
/ colors
;
234 const double scale
= (double) colors
/ (double) palette_entries
;
240 * First, build the colors for normal display.
242 for (i
= 0; i
< colors
; ++i
) {
243 int r
= base_colors
[i
].r
;
244 int g
= base_colors
[i
].g
;
245 int b
= base_colors
[i
].b
;
246 double rs
= (double) (base_colors
[(i
+ 1) % colors
].r
- r
) * scale
;
247 double gs
= (double) (base_colors
[(i
+ 1) % colors
].g
- g
) * scale
;
248 double bs
= (double) (base_colors
[(i
+ 1) % colors
].b
- b
) * scale
;
249 for (j
= 0; j
< entries_per_color
; ++j
) {
250 int index
= i
* entries_per_color
+ j
;
251 palette
[0][index
].r
= base_colors
[i
].r
+ (double) j
* rs
;
252 palette
[0][index
].g
= base_colors
[i
].g
+ (double) j
* gs
;
253 palette
[0][index
].b
= base_colors
[i
].b
+ (double) j
* bs
;
254 palette
[0][index
].a
= 0;
258 * Then build the rotated palettes.
260 for (i
= 1; i
< palette_entries
; ++i
) {
261 for (j
= 0; j
< palette_entries
; ++j
) {
262 palette
[i
][j
] = palette
[0][(j
+ i
) % palette_entries
];
268 * The routine to set up the display arrays to be used by all the calls.
271 int SetupGlobals(NaClSrpcChannel
*channel
,
272 NaClSrpcArg
**in_args
,
273 NaClSrpcArg
**out_args
) {
276 p
.u
.setup_canvas_width
= in_args
[0]->u
.dval
;
278 return NACL_SRPC_RESULT_OK
;
281 void do_SetupGlobals(struct work_item
*p
) {
282 canvas_width
= p
->u
.setup_canvas_width
;
283 /* Initialize the mask so that all pixels recompute every time */
284 memset(mask
, 0, sizeof(mask
));
288 NACL_SRPC_METHOD("setup:d:", SetupGlobals
);
290 int ShutdownSharedMemory(NaClSrpcChannel
*channel
,
291 NaClSrpcArg
**in_args
,
292 NaClSrpcArg
**out_args
) {
294 p
.kind
= WORK_SHUTDOWN
;
296 return NACL_SRPC_RESULT_OK
;
299 void do_ShutdownSharedMemory(struct work_item
*p
) {
300 /* Free the pixmap. */
302 /* Return success. */
305 NACL_SRPC_METHOD("shutdown::", ShutdownSharedMemory
);
308 * Compute ARGB value for one pixel.
310 static inline unsigned int mandel(double cx
, double cy
) {
313 unsigned int count
= 0;
314 const double threshold
= 1.0e8
;
316 while (count
< 255 && re
* re
+ im
* im
< threshold
) {
317 double new_re
= re
* re
- im
* im
+ cx
;
318 double new_im
= 2 * re
* im
+ cy
;
328 * Compute an entire pixmap of palette values.
330 static void Compute(int xlo
, int xhi
, double xstart
,
331 int ylo
, int yhi
, double ystart
,
332 int width
, char* map
) {
338 /* Compute the mandelbrot set, leaving color values in pixmap. */
340 for (i
= ylo
; i
< yhi
; ++i
) {
342 for (j
= xlo
; j
< xhi
; ++j
) {
343 IND(map
, width
, i
, j
) = mandel(x
, y
);
350 static void ComputeMasked(int xlo
, int xhi
, double xstart
,
351 int ylo
, int yhi
, double ystart
,
352 int width
, char* map
) {
358 /* Compute the mandelbrot set, leaving color values in pixmap. */
360 for (i
= ylo
; i
< yhi
; ++i
) {
362 for (j
= xlo
; j
< xhi
; ++j
) {
363 if (!IND(mask
, width
, i
, j
)) {
364 IND(map
, width
, i
, j
) = mandel(x
, y
);
372 static void BuildMask(int width
, char* map
, char* mask
) {
377 if (x_left
== -MAXFLOAT
&&
379 x_right
== MAXFLOAT
&&
380 y_bottom
== -MAXFLOAT
) {
383 /* Compute a nine-point stencil around the current point */
384 for (i
= 1; i
< width
- 1; ++i
) {
385 for (j
= 1; j
< width
- 1; ++j
) {
386 total
= (IND(map
, width
, i
- 1, j
- 1) + IND(map
, width
, i
- 1, j
) +
387 IND(map
, width
, i
- 1, j
+ 1) +
388 IND(map
, width
, i
, j
- 1) + IND(map
, width
, i
, j
+ 1) +
389 IND(map
, width
, i
+ 1, j
- 1) + IND(map
, width
, i
+ 1, j
) +
390 IND(map
, width
, i
+ 1, j
+ 1)) >> 3;
391 if (total
== IND(map
, width
, i
, j
)) {
392 IND(mask
, width
, i
, j
) = 1;
394 IND(mask
, width
, i
, j
) = 0;
401 * Move a selected region up or down, reusing already computed values.
403 static void MoveUD(int offset
) {
404 int width
= (int) canvas_width
;
408 char* q
= pixmap
+ offset
* width
;
409 size_t size
= (width
- offset
) * width
;
411 /* Move the invariant picture down by offset pixels. */
413 /* Recompute the newly revealed top portion */
414 Compute(0, width
, x_left
,
415 0, offset
, new_y_top
,
416 (int) canvas_width
, pixmap
);
418 char* p
= pixmap
- offset
* width
;
420 size_t size
= (width
+ offset
) * width
;
422 /* Move the invariant picture up by offset pixels. */
424 /* Recompute the newly revealed bottom portion */
425 Compute(0, width
, x_left
,
426 width
+ offset
, width
, y_bottom
,
427 (int) canvas_width
, pixmap
);
432 * Move a selected region left or right, reusing already computed values.
434 static void MoveLR(int offset
) {
437 int width
= canvas_width
;
440 /* Move the invariant picture left by offset pixels. */
441 for (i
= 0; i
< width
; ++i
) {
442 for (j
= 0; j
< width
- offset
; ++j
) {
443 pixmap
[i
* width
+ j
] = pixmap
[i
* width
+ j
+ offset
];
446 /* Recompute the newly revealed right portion */
447 Compute(width
- offset
, width
, x_right
,
449 (int) canvas_width
, pixmap
);
451 /* Move the invariant picture right by offset pixels. */
452 for (i
= 0; i
< width
; ++i
) {
453 for (j
= width
- 1; j
>= -offset
; --j
) {
454 pixmap
[i
* width
+ j
] = pixmap
[i
* width
+ j
+ offset
];
457 /* Recompute the newly revealed left portion */
458 Compute(0, -offset
, new_x_left
,
460 (int) canvas_width
, pixmap
);
465 * Select the region of the X-Y plane to be viewed and compute.
467 int SetRegion(NaClSrpcChannel
*channel
,
468 NaClSrpcArg
**in_args
,
469 NaClSrpcArg
**out_args
) {
471 p
.kind
= WORK_SET_REGION
;
472 p
.u
.set_region
.new_x_left
= in_args
[0]->u
.dval
;
473 p
.u
.set_region
.new_y_top
= in_args
[1]->u
.dval
;
474 p
.u
.set_region
.new_x_right
= in_args
[2]->u
.dval
;
475 p
.u
.set_region
.new_y_bottom
= in_args
[3]->u
.dval
;
477 return NACL_SRPC_RESULT_OK
;
480 void do_SetRegion(struct work_item
*p
) {
481 new_x_left
= p
->u
.set_region
.new_x_left
;
482 new_y_top
= p
->u
.set_region
.new_y_top
;
483 new_x_right
= p
->u
.set_region
.new_x_right
;
484 new_y_bottom
= p
->u
.set_region
.new_y_bottom
;
486 double new_xstep
= (new_x_right
- new_x_left
) / canvas_width
;
487 double new_ystep
= (new_y_bottom
- new_y_top
) / canvas_width
;
489 if (new_x_left
== x_left
&& new_x_right
== x_right
) {
490 if (new_y_top
== y_top
&& new_y_bottom
== y_bottom
) {
492 } else if (new_ystep
== ystep
) {
494 /* Note that ystep is negative */
495 MoveUD(-(new_y_top
- y_top
) / ystep
);
497 y_bottom
= new_y_bottom
;
499 /* Vertical zoom (and possibly pan) */
501 y_bottom
= new_y_bottom
;
503 Compute(0, canvas_width
, x_left
,
504 0, canvas_width
, y_top
,
505 (int) canvas_width
, pixmap
);
507 } else if (new_y_top
== y_top
&& new_y_bottom
== y_bottom
) {
508 if (new_x_left
== x_left
&& new_x_right
== x_right
) {
510 } else if (new_xstep
== xstep
) {
512 MoveLR((new_x_right
- x_right
) / xstep
);
514 x_right
= new_x_right
;
516 /* Horizontal zoom (and possibly pan) */
518 x_right
= new_x_right
;
520 Compute(0, canvas_width
, x_left
,
521 0, canvas_width
, y_top
,
522 (int) canvas_width
, pixmap
);
524 } else if (FPEQUAL((x_right
- x_left
) / (y_top
- y_bottom
),
525 (new_x_right
- new_x_left
) / (new_y_top
- new_y_bottom
)) &&
526 ((x_right
- x_left
) / (y_top
- y_bottom
) >=
527 (new_x_right
- new_x_left
) / (new_y_top
- new_y_bottom
))) {
529 BuildMask(canvas_width
, pixmap
, mask
);
532 x_right
= new_x_right
;
533 y_bottom
= new_y_bottom
;
536 Compute(0, canvas_width
, x_left
,
537 0, canvas_width
, y_top
,
538 (int) canvas_width
, pixmap
);
543 x_right
= new_x_right
;
544 y_bottom
= new_y_bottom
;
547 Compute(0, canvas_width
, x_left
,
548 0, canvas_width
, y_top
,
549 (int) canvas_width
, pixmap
);
553 NACL_SRPC_METHOD("set_region:dddd:", SetRegion
);
556 * Display the pixmap, using the color palette with a possible shift.
558 int MandelDisplay(NaClSrpcChannel
*channel
,
559 NaClSrpcArg
** in_args
,
560 NaClSrpcArg
** out_args
) {
562 p
.kind
= WORK_DISPLAY
;
564 return NACL_SRPC_RESULT_OK
;
567 void do_MandelDisplay(struct work_item
*wp
) {
568 Pixel
* p
= (Pixel
*) display_mem
;
573 // Render the color values according to the palette.
574 for (i
= 0; i
< canvas_width
; ++i
) {
575 for (j
= 0; j
< canvas_width
; ++j
) {
576 *p
= palette
[palette_select
][*q
];
581 nacl_video_update(display_mem
);
584 NACL_SRPC_METHOD("display::", MandelDisplay
);
587 * To implement "psychedelic mode" we select one of 256 palettes
588 * (actually rotations of the same palette) by using palette_select.
589 * ShiftColors can set it to the default value (0) by passing zero
590 * or simply bump it by one when anything else is passed.
592 int ShiftColors(NaClSrpcChannel
*channel
,
593 NaClSrpcArg
** in_args
,
594 NaClSrpcArg
** out_args
) {
596 p
.kind
= WORK_SHIFT_COLOR
; p
.u
.shift_color
= in_args
[0]->u
.ival
;
598 return NACL_SRPC_RESULT_OK
;
601 void do_ShiftColors(struct work_item
*p
) {
602 int shift_color
= p
->u
.shift_color
;
604 if (0 == shift_color
) {
605 /* Reset to the default color palette */
608 palette_select
= (palette_select
+ 1) % 256;
612 NACL_SRPC_METHOD("shift_colors:i:", ShiftColors
);
620 extern void srpc_init();
622 int main(int argc
, char* argv
[]) {
624 struct work_item
*item
;
625 /* Initialize the graphics subsystem. */
627 retval
= nacl_multimedia_init(NACL_SUBSYSTEM_VIDEO
| NACL_SUBSYSTEM_EMBED
);
631 retval
= nacl_video_init(NACL_VIDEO_FORMAT_RGBA
, BASEWIDTH
, BASEHEIGHT
);
635 /* Process requests from the UI. */
636 while (NULL
!= (item
= work_get())) {
637 switch (item
->kind
) {
638 #define D(k,m) case k: m(item); break
639 D(WORK_SETUP
, do_SetupGlobals
);
640 D(WORK_SHUTDOWN
, do_ShutdownSharedMemory
);
641 D(WORK_SET_REGION
, do_SetRegion
);
642 D(WORK_DISPLAY
, do_MandelDisplay
);
643 D(WORK_SHIFT_COLOR
, do_ShiftColors
);
645 work_mark_done(item
);
647 /* Shutdown and return. */
648 nacl_video_shutdown();
649 nacl_multimedia_shutdown();