1 #include <Application.h>
2 #include <WindowScreen.h>
3 #include <Screen.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <math.h>
8 #include <SupportDefs.h> // min_c() and max_c()
9
10 #ifdef DEBUGGING
11 #define PRINT(x) printf x
12 #else
13 #define PRINT(x)
14 #endif
15
16 // macros
17 #define set_pixel(x,y,color) (frame_buffer[x + (line_length*y)] = color)
18 #define get_pixel(x,y) (frame_buffer[x + (line_length*y)])
19
20 class NApplication : public BApplication {
21 public:
22 NApplication();
23 bool is_quitting; // So that the WindowScreen knows what
24 // to do when disconnected.
25 private:
26 bool QuitRequested();
27 void ReadyToRun();
28 };
29
30 class NWindowScreen : public BWindowScreen {
31 public:
32 NWindowScreen(status_t*);
33 private:
34 void ScreenConnected(bool);
35 int32 MyCode();
36 static int32 Entry(void*);
37 // handy stuff
set_frame_rate(float fps)38 void set_frame_rate(float fps) {frame_pause = (bigtime_t)((1000 * 1000)/fps);}
39 // special for demos
40 enum {
41 // used for star->last_draw
42 INVALID = 0x7fffffff
43 };
44 typedef struct {
45 float init_velocity;
46 float gravity;
47 double cos_z_theta;
48 int32 y;
49 int32 x;
50 int32 timeval;
51 uint32 last_draw;
52 int32 lx,ly;
53 } particle;
54 uint32 particle_count;
55 particle *particle_list;
56 // simple raster functions
57 void draw_line(int x1, int y1, int x2, int y2, int color);
58 void draw_rect(int x, int y, int w, int h, int color);
59 void fill_rect(int x, int y, int w, int h, int color);
60 void draw_ellipse(int cx, int cy, int wide, int deep, int color);
61 void fill_ellipse(int x, int y, int xradius, int yradius, int color);
62 void ellipse_points(int x, int y, int x_offset, int y_offset, int color);
63 void ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color);
64 thread_id tid;
65 sem_id sem;
66 area_id area;
67 uint8* save_buffer;
68 uint8* frame_buffer;
69 ulong line_length;
70 bigtime_t frame_pause; // time between frames
71 int width,height;
72 int COLORS;
73 bool thread_is_locked; // small hack to allow to quit the
74 // app from ScreenConnected()
75 };
76
77 int
main()78 main()
79 {
80 NApplication app;
81 return 0;
82 }
83
NApplication()84 NApplication::NApplication()
85 :BApplication("application/x-vnd.Prok-DemoTemplate")
86 {
87 Run(); // see you in ReadyToRun()
88 }
89
ReadyToRun()90 void NApplication::ReadyToRun()
91 {
92 PRINT(("ReadyToRun()\n"));
93 status_t ret = B_ERROR;
94 is_quitting = false;
95 NWindowScreen *ws = new NWindowScreen(&ret);
96 PRINT(("WindowScreen ctor returned. ret = %s\n", strerror(ret)));
97 // exit if constructing the WindowScreen failed.
98 if((ws == NULL) || (ret < B_OK))
99 {
100 //printf("the window screen was NULL, or there was an error\n");
101 PostMessage(B_QUIT_REQUESTED);
102 }
103 else
104 PRINT(("everything's just peachy. done with ReadyToRun().\n"));
105 }
106
QuitRequested()107 bool NApplication::QuitRequested()
108 {
109 PRINT(("QuitRequested()\n"));
110 status_t ret;
111 is_quitting = true;
112 wait_for_thread(find_thread("rendering thread"), &ret); // wait for the render thread to finish
113 return true;
114 }
115
NWindowScreen(status_t * ret)116 NWindowScreen::NWindowScreen(status_t *ret)
117 : BWindowScreen("Example", B_8_BIT_640x480, ret), width(639), height(479), COLORS(256)
118 {
119 PRINT(("WindowScreen ctor.\n"));
120 thread_is_locked = true;
121 tid = 0;
122 if(*ret == B_OK)
123 {
124 PRINT(("creating blocking sem and save_buffer area.\n"));
125 // this semaphore controls the access to the WindowScreen
126 sem = create_sem(0,"WindowScreen Access");
127 // this area is used to save the whole framebuffer when
128 // switching workspaces. (better than malloc()).
129 area = create_area("save", (void**)&save_buffer, B_ANY_ADDRESS, 640*480, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
130 // exit if an error occured.
131 if((sem < B_OK) || (area < B_OK))
132 {
133 PRINT(("create_area() or create_sem() failed\n"));
134 *ret = B_ERROR;
135 }
136 else
137 {
138 PRINT(("calling Show().\n"));
139 Show(); // let's go. See you in ScreenConnected.
140 }
141 }
142 else
143 {
144 PRINT(("BWindowScreen base class ctor returned failure\n"));
145 be_app->PostMessage(B_QUIT_REQUESTED);
146 }
147 // set the frame rate
148 set_frame_rate(30.);
149 }
150
151
152 void
ScreenConnected(bool connected)153 NWindowScreen::ScreenConnected(bool connected)
154 {
155 PRINT(("ScreenConnected()\n"));
156 fflush(stdout);
157 if(connected)
158 {
159 if(SetSpace(B_8_BIT_640x480) < B_OK)
160 {
161 //SetFrameBuffer(640, 480);
162 PRINT(("SetSpace() failed\n"));
163 // properly set the framebuffer. exit if an error occurs.
164 be_app->PostMessage(B_QUIT_REQUESTED);
165 return;
166 }
167 // get the framebuffer-related info, each time the
168 // WindowScreen is connected (multiple monitor)
169 frame_buffer = (uint8*)(CardInfo()->frame_buffer);
170 line_length = FrameBufferInfo()->bytes_per_row;
171 if(tid == 0)
172 {
173 // clean the framebuffer
174 PRINT(("zeroing the framebuffer\n"));
175 memset(frame_buffer,0,480*line_length);
176 // spawn the rendering thread. exit if an error occurs.
177 PRINT(("spawning the render thread.\n"));
178 tid = spawn_thread(Entry,"rendering thread", B_URGENT_DISPLAY_PRIORITY,this);
179 if(resume_thread(tid) < B_OK)
180 {
181 be_app->PostMessage(B_QUIT_REQUESTED);
182 return;
183 }
184 }
185 else
186 {
187 for(int y=0;y<480;y++)
188 {
189 // restore the framebuffer when switching back from
190 // another workspace.
191 memcpy(frame_buffer+y*line_length,save_buffer+640*y,640);
192 }
193 }
194 // set our color list.
195 rgb_color palette[256];
196 rgb_color c1;
197 for(int i=0,j=0;i<256;i++,j++)
198 {
199 if(i<64)
200 {
201 c1.red = j*4; // greys
202 c1.green = j*4;
203 c1.blue = j*4;
204 c1.alpha = 255;
205 }
206 if((i>=64) && (i<128))
207 {
208 c1.red = j*4; // reds
209 c1.green = 0;
210 c1.blue = 0;
211 c1.alpha = 255;
212 }
213 if((i>=128) && (i<192))
214 {
215 c1.red = 0; // greens
216 c1.green = j*4;
217 c1.blue = 0;
218 c1.alpha = 255;
219 }
220 if((i>=192) && (i<256))
221 {
222 c1.red = 0; // blues
223 c1.green = 0;
224 c1.blue = j*4;
225 c1.alpha = 255;
226 }
227 if(j == 64)
228 j=0;
229 palette[i]=c1;
230 }
231 SetColorList(palette);
232
233 // allow the rendering thread to run.
234 thread_is_locked = false;
235 release_sem(sem);
236 }
237 else /* !connected */
238 {
239 // block the rendering thread.
240 if(!thread_is_locked)
241 {
242 acquire_sem(sem);
243 thread_is_locked = true;
244 }
245 // kill the rendering and clean up when quitting
246 if((((NApplication*)be_app)->is_quitting))
247 {
248 status_t ret;
249 kill_thread(tid);
250 wait_for_thread(tid,&ret);
251 delete_sem(sem);
252 delete_area(area);
253 free(particle_list);
254 }
255 else
256 {
257 // set the color list black so that the screen doesn't seem
258 // to freeze while saving the framebuffer
259 rgb_color c={0,0,0,255};
260 rgb_color palette[256];
261 // build the palette
262 for(int i=0;i<256;i++)
263 palette[i] = c;
264 // set the palette
265 SetColorList(palette);
266 // save the framebuffer
267 for(int y=0;y<480;y++)
268 memcpy(save_buffer+640*y,frame_buffer+y*line_length,640);
269 }
270 }
271 }
272
273
274 int32
Entry(void * p)275 NWindowScreen::Entry(void* p)
276 {
277 return ((NWindowScreen*)p)->MyCode();
278 }
279
280
281 int32
MyCode()282 NWindowScreen::MyCode()
283 {
284 bigtime_t trgt = system_time() + frame_pause;
285 srandom(system_time());
286 // beforehand stuff
287 particle_count = 1024*2;
288 particle_list = (particle *)malloc(sizeof(particle)*particle_count);
289 for (uint32 i=0; i<particle_count; i++) {
290 uint32 rand_max = 0xffffffff;
291 particle_list[i].init_velocity = -((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max)*3.333; // magic number
292 particle_list[i].gravity = -(((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max))*0.599; // more magic
293
294 // make the particle initialy invisible and fixed, but at a random moment in time
295 particle_list[i].lx = 0;
296 particle_list[i].ly = 0;
297 particle_list[i].last_draw = INVALID;
298 particle_list[i].timeval = random() & 64;
299 particle_list[i].x = 0; // this gets figured out at drawtime
300 particle_list[i].y = 0; // same here
301 particle_list[i].cos_z_theta = cos(random() % 360); // grab an angle
302 }
303
304
305 // the loop o' fun
306 while (!(((NApplication*)be_app)->is_quitting)) {
307 // try to sync with the vertical retrace
308 if (BScreen(this).WaitForRetrace() != B_OK) {
309 // snoze for a bit so that other threads can be happy.
310 // We are realtime priority you know
311 if (system_time() < trgt)
312 snooze(trgt - system_time());
313 trgt = system_time() + frame_pause;
314 }
315
316 // gain access to the framebuffer before writing to it.
317 acquire_sem(sem); // block until we're allowed to own the framebuffer
318
319 ///////////////////////////////
320 // do neat stuff here //
321 //////////////////////////////
322 PRINT(("rendering a frame.\n"));
323
324
325 // eye candy VII - particles! - my own cookin
326 int32 x, y, cx,cy;
327 set_frame_rate(60.); // woo. ntsc
328 // calculate the center
329 cx = width/2;
330 cy = height/2;
331
332 // palette test
333 //set_frame_rate(0.1);
334 //for(int i=0;i<256;i++)
335 // draw_line(i,0,i,height, i);
336
337 PRINT(("Starting particle drawing loop\n"));
338 particle *s = particle_list;
339 for (uint32 i=0; i<particle_count; i++) {
340 PRINT(("drawing particle %d\r", i));
341
342 // save the old position
343 s->lx = s->x;
344 s->ly = s->y;
345
346 PRINT(("cx=%d, cy=%d\n", cx,cy));
347
348 // move the particle
349 // find y and x
350 // (s->gravity/2)*(s->timeval*s->timeval) * 1.85 is magic
351 y = s->y = (int32)(cy + (int32)((s->gravity/2)*(s->timeval*s->timeval)*1.94) + ((s->init_velocity - (s->gravity*s->timeval)) * s->timeval));
352 x = s->x = (int32)(cx + (int32)(s->timeval * s->cos_z_theta)); // 3d rotation
353
354 // interate timeval
355 s->timeval++;
356
357 // sanity check
358 if(x <= 0)
359 goto erase_and_reset;
360 if(x > width)
361 goto erase_and_reset;
362 if(y < 0)
363 goto erase; // invisible + erase last position
364 if(y > height)
365 goto erase_and_reset;
366
367 // erase the previous position, if necessary
368 if (s->last_draw != INVALID)
369 set_pixel(s->lx,s->ly,0);
370
371 // if it's visible, then draw it.
372 set_pixel(s->x,s->y, 169);
373 s->last_draw = 1;
374 goto loop;
375
376 erase_and_reset:
377 if((s->lx <= width) && (s->lx >= 0) && (s->ly <= height) && (s->ly >= 0))
378 set_pixel(s->lx, s->ly,0);
379 s->x = 0;
380 s->y = 0;
381 s->lx = 0;
382 s->ly = 0;
383 s->timeval = 0;
384 s->last_draw = INVALID;
385 goto loop;
386
387 erase:
388 // erase it.
389 if(s->last_draw != INVALID)
390 set_pixel(s->lx, s->ly,0);
391 s->lx = s->x;
392 s->ly = s->y;
393 s->last_draw = INVALID;
394 loop:
395 s++;
396 //printf("end draw loop\n");
397 }
398 PRINT(("frame done\n"));
399
400 //////////////////////////////////
401 // stop doing neat stuff //
402 /////////////////////////////////
403
404 // release the semaphore while waiting. gotta release it
405 // at some point or nasty things will happen!
406 release_sem(sem);
407 // loop for another frame!
408 }
409 return B_OK;
410 }
411
412 //////////////////////////////
413 // Misc - a place for demos to put their convenience functions
414 //////////////////////////////
415
416
417 //////////////////////////////
418 // My Silly Raster Lib
419 //////////////////////////////
420
421 /*
422
423 Functions:
424 void draw_line(int x1, int y1, int x2, int y2, int color);
425 void draw_rect(int x, int y, int w, int h, int color);
426 void fill_rect(int x, int y, int w, int h, int color);
427 void draw_ellipse(int x, int y, int xradius, int yradius, int color);
428 void fill_ellipse(int x, int y, int xradius, int yradius, int color);
429
430 */
431
432 void
draw_line(int x1,int y1,int x2,int y2,int color)433 NWindowScreen::draw_line(int x1, int y1, int x2, int y2, int color)
434 {
435 // Simple Bresenham's line drawing algorithm
436 int d,x,y,ax,ay,sx,sy,dx,dy;
437
438 #define ABS(x) (((x)<0) ? -(x) : (x))
439 #define SGN(x) (((x)<0) ? -1 : 1)
440
441 dx=x2-x1; ax=ABS(dx)<<1; sx=SGN(dx);
442 dy=y2-y1; ay=ABS(dy)<<1; sy=SGN(dy);
443
444 x=x1;
445 y=y1;
446 if(ax>ay)
447 {
448 d=ay-(ax>>1);
449 for(;;)
450 {
451 set_pixel(x,y,color);
452 if(x==x2) return;
453 if(d>=0)
454 {
455 y+=sy;
456 d-=ax;
457 }
458 x+=sx;
459 d+=ay;
460 }
461 }
462 else
463 {
464 d=ax-(ay>>1);
465 for(;;)
466 {
467 set_pixel(x,y,color);
468 if(y==y2) return;
469 if(d>=0)
470 {
471 x+=sx;
472 d-=ay;
473 }
474 y+=sy;
475 d+=ax;
476 }
477 }
478 }
479
480
481 void
draw_rect(int x,int y,int w,int h,int color)482 NWindowScreen::draw_rect(int x, int y, int w, int h, int color)
483 {
484 draw_line(x,y,x+w,y,color);
485 draw_line(x,y,x,y+h,color);
486 draw_line(x,y+h,x+w,y+h,color);
487 draw_line(x+w,y,x+w,y+h,color);
488 }
489
490
491 void
fill_rect(int x,int y,int w,int h,int color)492 NWindowScreen::fill_rect(int x, int y, int w, int h, int color)
493 {
494 for (int i = 0; i < w; i++) {
495 for (int j = 0; j < h; j++) {
496 set_pixel(i, j, color);
497 }
498 }
499 }
500
501
502 void
draw_ellipse(int cx,int cy,int wide,int deep,int color)503 NWindowScreen::draw_ellipse(int cx, int cy, int wide, int deep, int color)
504 {
505 // if we're asked to draw a really small ellipse, put a single pixel in the buffer
506 // and bail
507 if((wide < 1) || (deep < 1))
508 {
509 set_pixel(cx,cy,color);
510 return;
511 }
512
513 // MidPoint Ellipse algorithm.
514 // page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book)
515 int16 x, y;
516 int16 wide_squared, deep_squared;
517 double d;
518
519 x = 0;
520 y = deep;
521 wide_squared = wide * wide;
522 deep_squared = deep * deep;
523 d = deep_squared - (wide_squared*deep) + (wide_squared/4);
524
525 ellipse_points(x, y, cx, cy, color);
526 while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1)))
527 {
528 if(d < 0)
529 d += deep_squared*(2*x + 3);
530 else
531 {
532 d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2);
533 y--;
534 }
535 x++;
536 ellipse_points(x, y, cx, cy, color);
537 }
538
539 d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared;
540 while(y > 0)
541 {
542 if(d < 0)
543 {
544 d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3);
545 x++;
546 }
547 else
548 d += wide_squared*(-2*y + 3);
549 y--;
550 ellipse_points(x, y, cx, cy, color);
551 }
552 }
553
554
555 void
fill_ellipse(int cx,int cy,int wide,int deep,int color)556 NWindowScreen::fill_ellipse(int cx, int cy, int wide, int deep, int color)
557 {
558 // if we're asked to draw a really small ellipse, put a single pixel in the buffer
559 // and bail
560 if((wide < 1) || (deep < 1))
561 {
562 set_pixel(cx,cy,color);
563 return;
564 }
565
566 // MidPoint Ellipse algorithm.
567 // page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book)
568 int16 x, y;
569 int16 wide_squared, deep_squared;
570 double d;
571
572 x = 0;
573 y = deep;
574 wide_squared = wide * wide;
575 deep_squared = deep * deep;
576 d = deep_squared - (wide_squared*deep) + (wide_squared/4);
577
578 ellipse_fill_points(x, y, cx, cy, color);
579 while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1)))
580 {
581 if(d < 0)
582 d += deep_squared*(2*x + 3);
583 else
584 {
585 d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2);
586 y--;
587 }
588 x++;
589 ellipse_fill_points(x, y, cx, cy, color);
590 }
591
592 d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared;
593 while(y > 0)
594 {
595 if(d < 0)
596 {
597 d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3);
598 x++;
599 }
600 else
601 d += wide_squared*(-2*y + 3);
602 y--;
603 ellipse_fill_points(x, y, cx, cy, color);
604 }
605 }
606
607
608 void
ellipse_points(int x,int y,int x_offset,int y_offset,int color)609 NWindowScreen::ellipse_points(int x, int y, int x_offset, int y_offset, int color)
610 {
611 // fill four pixels for every iteration in draw_ellipse
612
613 // the x_offset and y_offset values are needed since the midpoint ellipse algorithm
614 // assumes the midpoint to be at the origin
615 // do a sanity check before each set_pixel, that way we clip to the edges
616
617 int xCoord, yCoord;
618
619 xCoord = x_offset + x;
620 yCoord = y_offset + y;
621 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
622 set_pixel(xCoord,yCoord,color);
623 xCoord = x_offset - x;
624 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
625 set_pixel(xCoord,yCoord,color);
626 xCoord = x_offset + x;
627 yCoord = y_offset - y;
628 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
629 set_pixel(xCoord,yCoord,color);
630 xCoord = x_offset - x;
631 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
632 set_pixel(xCoord,yCoord,color);
633 }
634
635
636 void
ellipse_fill_points(int x,int y,int x_offset,int y_offset,int color)637 NWindowScreen::ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color)
638 {
639 // put lines between two pixels twice. once for y positive, the other for y negative (symmetry)
640 // for every iteration in fill_ellipse
641
642 // the x_offset and y_offset values are needed since the midpoint ellipse algorithm
643 // assumes the midpoint to be at the origin
644 // do a sanity check before each set_pixel, that way we clip to the edges
645
646 int xCoord1, yCoord1;
647 int xCoord2, yCoord2;
648
649 xCoord1 = x_offset - x;
650 yCoord1 = y_offset + y;
651 xCoord2 = x_offset + x;
652 yCoord2 = y_offset + y;
653 if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height))
654 if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height))
655 draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color);
656
657 xCoord1 = x_offset - x;
658 yCoord1 = y_offset - y;
659 xCoord2 = x_offset + x;
660 yCoord2 = y_offset - y;
661 if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height))
662 if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height))
663 draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color);
664
665 }
666