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 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 78 main() 79 { 80 NApplication app; 81 return 0; 82 } 83 84 NApplication::NApplication() 85 :BApplication("application/x-vnd.Prok-DemoTemplate") 86 { 87 Run(); // see you in ReadyToRun() 88 } 89 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 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 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 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 275 NWindowScreen::Entry(void* p) 276 { 277 return ((NWindowScreen*)p)->MyCode(); 278 } 279 280 281 int32 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 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 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 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 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 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 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 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