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