1 /* 2 3 ChartRender.c 4 5 by Pierre Raynaud-Richard. 6 7 Copyright 1998 Be Incorporated, All Rights Reserved. 8 9 */ 10 11 /* This file has been designed to be easy to compile as a stand-alone 12 piece of code, allowing you to use advanced intel compiler, even 13 if they are compatible with the whole Be environment. To accomplish 14 that purpose, all declarations were concentrated in ChartRender.h 15 (see that header file for more infos). */ 16 #include "ChartRender.h" 17 18 /* This table provide the horizontal and vertical offset of the matrix 19 of pixel used for drawing stars. This matrix is designed as follow: 20 21 -- [00] [01] [02] [03] -- 22 [04] [05] [06] [07] [08] [09] 23 [10] [11] [12] [13] [14] [15] 24 [16] [17] [18] [19] [20] [21] 25 [22] [23] [24] [25] [26] [27] 26 -- [28] [29] [30] [31] -- 27 28 The reference pixel is [12]. */ 29 int8 pattern_dh[32] = { 30 -1, 0, 1, 2, 31 -2, -1, 0, 1, 2, 3, 32 -2, -1, 0, 1, 2, 3, 33 -2, -1, 0, 1, 2, 3, 34 -2, -1, 0, 1, 2, 3, 35 -1, 0, 1, 2 36 }; 37 38 int8 pattern_dv[32] = { 39 -2, -2, -2, -2, 40 -1, -1, -1, -1, -1, -1, 41 0, 0, 0, 0, 0, 0, 42 1, 1, 1, 1, 1, 1, 43 2, 2, 2, 2, 2, 2, 44 3, 3, 3, 3 45 }; 46 47 /* Those table contains a preprocessed version of the 32 size of star, 48 represented in the 32 pixels matrix, by alpha-blending density [0 to 7]. 49 Those matrix are stored in packed format, as a the list of all pixel 50 whose alpha-blending density is > 0. There is 4 cases because every 51 star can be aligned at half a pixel in both direction (we implement 52 sub-pixel precision and anti-aliasing to reduce the jittering). */ 53 static uint8 pattern_list[4*LEVEL_COUNT][32]; 54 static uint8 pattern_list_count[4*LEVEL_COUNT]; 55 static uint8 pattern_color_offset[4*LEVEL_COUNT][32]; 56 /* this table store the alpha-blending level of the center pixel. This 57 is used for size so small that only the center pixel is lighted. */ 58 static uint8 pixel_color_offset[LEVEL_COUNT]; 59 60 /* Those mask are use for fast clipping, to determine which of the 32 61 pixels of the standard star matrix are visible when coming closer 62 from a left, right, top or bottom clipping border. */ 63 static uint32 visible_mask_left[6] = { 64 0xffffffff, 65 0xffbefbef, 66 0xef3cf3ce, 67 0xce38e38c, 68 0x8c30c308, 69 0x08208200 70 }; 71 72 static uint32 visible_mask_right[6] = { 73 0xffffffff, 74 0xf7df7dff, 75 0x73cf3cf7, 76 0x31c71c73, 77 0x10c30c31, 78 0x00410410, 79 }; 80 81 static uint32 visible_mask_top[6] = { 82 0xffffffff, 83 0xfffffff0, 84 0xfffffc00, 85 0xffff0000, 86 0xffc00000, 87 0xf0000000 88 }; 89 90 static uint32 visible_mask_bottom[6] = { 91 0xffffffff, 92 0x0fffffff, 93 0x003fffff, 94 0x0000ffff, 95 0x000003ff, 96 0x0000000f 97 }; 98 99 /* Private functions used only internally. */ 100 bool ProjectStar(star *s, geometry *geo); 101 bool CheckClipping(star *s, buffer *buf, bool reset_clipping); 102 void DrawStar(star *s, buffer *buf); 103 void EraseStar(star *s, buffer *buf); 104 105 106 /* This function initialise the 32 sizes of anti-aliased star, each one 107 represented in 4 different half-pixel alignement : 108 x : -0.25, y : -0.25 109 x : +0.25, y : -0.25 110 x : -0.25, y : +0.25 111 x : +0.25, y : +0.25 */ 112 void InitPatterns() 113 { 114 int32 i, j, k, count; 115 float radius, x0, y0, x, y, dist, delta; 116 uint8 color; 117 uint8 *list, *color_offset; 118 119 /* do the 4 half-pixel alignement */ 120 for (j=0; j<4; j++) { 121 if (j&1) x0 = 1.25; 122 else x0 = 0.75; 123 if (j&2) y0 = 1.25; 124 else y0 = 0.75; 125 126 /* do the 32 sizes */ 127 for (i=0; i<LEVEL_COUNT; i++) { 128 radius = (float)(i+1) * (2.8/(float)LEVEL_COUNT); 129 count = 0; 130 list = pattern_list[j*LEVEL_COUNT + i]; 131 color_offset = pattern_color_offset[j*LEVEL_COUNT + i]; 132 133 /* scan the 32 pixels of the matrix */ 134 for (k=0; k<32; k++) { 135 x = ((float)pattern_dh[k] + ROUNDING) - x0; 136 y = ((float)pattern_dv[k] + ROUNDING) - y0; 137 138 dist = sqrt(x*x + y*y); 139 /* process non source pixel */ 140 if (dist > 0.5) { 141 delta = radius - dist + 0.5; 142 if (delta >= 1.0) { 143 *color_offset++ = 7; 144 *list++ = k; 145 count++; 146 } 147 else if (delta > 0.5) { 148 *color_offset++ = (uint8)(7.499 - 16.0 * (1.0 - delta) * (1.0 - delta) + ROUNDING); 149 *list++ = k; 150 count++; 151 } 152 else if (delta > 0) { 153 color = (uint8)(16.0 * delta * delta); 154 if (color > 0) { 155 *color_offset++ = color; 156 *list++ = k; 157 count++; 158 } 159 } 160 } 161 /* process source pixel (the one containing the center of the star) */ 162 else { 163 if (radius < 0.25) { 164 color = (uint8)(32.0 * radius * radius + ROUNDING); 165 if (color == 0) 166 color++; 167 } 168 else if (radius < 0.75) { 169 delta = radius + 0.25; 170 color = (uint8)(7.499 - 22.0 * (1.0 - delta) * (1.0 - delta) + ROUNDING); 171 } 172 else 173 color = 7; 174 *color_offset++ = color; 175 *list++ = k; 176 count++; 177 pixel_color_offset[i] = color; 178 } 179 } 180 pattern_list_count[j*LEVEL_COUNT + i] = count; 181 } 182 } 183 } 184 185 /* Project a star (s) in the view space of the camera, as described by (geo). 186 Returns true if the star seems to be visible (in the pyramid of vision, 187 closer than the rear plan, farther than the front plan), or false if it's 188 clear that the star isnot visible. */ 189 bool ProjectStar(star *s, geometry *geo) 190 { 191 int32 h_double, v_double, level; 192 float x0, y0, z0, x, y, z, inv_z; 193 194 /* Calculate the coordinate of the star after doing the cycling operation 195 that convert the cube of the starfield in a torus. This ensure that 196 get the copy of the star that is the only one likely to be visible from 197 the camera. */ 198 x0 = s->x; 199 if (x0 < geo->cutx) 200 x0 += 1.0; 201 y0 = s->y; 202 if (y0 < geo->cuty) 203 y0 += 1.0; 204 z0 = s->z; 205 if (z0 < geo->cutz) 206 z0 += 1.0; 207 /* Translate the star relative to the position of the camera. */ 208 x0 -= geo->x; 209 y0 -= geo->y; 210 z0 -= geo->z; 211 212 /* Calculate the z coordinate (depth) of the star in the camera referential. */ 213 z = geo->m[0][2]*x0 + geo->m[1][2]*y0 + geo->m[2][2]*z0; 214 215 /* Do the rear and front plan clipping */ 216 if ((z < geo->z_min) || (z > geo->z_max)) 217 return false; 218 219 /* Calculate the x coordinate (horizontal) of the star in the camera referential. */ 220 x = geo->m[0][0]*x0 + geo->m[1][0]*y0 + geo->m[2][0]*z0; 221 222 /* Do the left and right clipping based on the pyramid of vision. */ 223 if ((x < geo->xz_min*z-BORDER_CLIPPING) || (x > geo->xz_max*z+BORDER_CLIPPING)) 224 return false; 225 226 /* Calculate the y coordinate (vertical) of the star in the camera referential. */ 227 y = geo->m[0][1]*x0 + geo->m[1][1]*y0 + geo->m[2][1]*z0; 228 229 /* Do the top and bottom clipping based on the pyramid of vision. */ 230 if ((y < geo->yz_min*z-BORDER_CLIPPING) || (y > geo->yz_max*z+BORDER_CLIPPING)) 231 return false; 232 233 /* Calculate the invert of z, used to project both H and V coordinate. Apply 234 the zoom-factor at the same time. The zoom-factor was overscale by a factor 235 of two in advance, for the half-pixel precision processing */ 236 inv_z = geo->zoom_factor/z; 237 238 /* Calculate the double pixel coordinate in the buffer (in half-pixel). */ 239 h_double = (int32)(x * inv_z + geo->offset_h); 240 v_double = (int32)(y * inv_z + geo->offset_v); 241 242 /* Calculate the light level of the star. We use that little weird function 243 to a get faster gradient to black near the rear plan. */ 244 level = (int32)(s->size * (inv_z * geo->z_max_square - z * geo->zoom_factor)) >> 8; 245 /* The light level can go higher that our max (saturation). */ 246 if (level >= LEVEL_COUNT) 247 level = LEVEL_COUNT-1; 248 249 /* Get the real pixel coordinate in the buffer from the double coordinates */ 250 s->h = h_double >> 1; 251 s->v = v_double >> 1; 252 /* Save the light level (used to recognize single pixel star) */ 253 s->level = level; 254 /* switch between the 4 pattern table use for the 4 half-aligned. */ 255 if ((h_double & 1) == 1) level += LEVEL_COUNT; 256 if ((v_double & 1) == 1) level += 2*LEVEL_COUNT; 257 s->pattern_level = level; 258 return true; 259 } 260 261 /* Once a star has been projected (using ProjectStar), we need to determine 262 which pixel of the star matrix are visible (if any). This depend of the 263 clipping of the specific buffer you're using. This function will do that 264 for the star (s), in the buffer (buf). It will return false if the star 265 is fully invisible, true if not. The flag reset_clipping is used to 266 reprocess the clipping from scratch, or to just cumulate the new clipping 267 to the last drawing clipping (this is needed when updating the clipping 268 of every stars after changing the clipping region of the buffer). */ 269 bool CheckClipping(star *s, buffer *buf, bool reset_clipping) 270 { 271 int32 delta; 272 uint32 i, total_visible, tmp_visible; 273 clipping_rect box; 274 clipping_rect *r; 275 276 /* Simple case : the star is represented by only one pixel. */ 277 if (pattern_list_count[s->pattern_level] == 1) { 278 /* if the pixel is not in the bounding box of the clipping region, 279 the star is guarantee to be invisible. */ 280 if ((s->h < buf->clip_bounds.left) || 281 (s->h > buf->clip_bounds.right) || 282 (s->v < buf->clip_bounds.top) || 283 (s->v > buf->clip_bounds.bottom)) 284 goto invisible; 285 /* if the clipping region contains only one rectangle, then it's 286 equal to its bounding box, so no further test are needed. */ 287 if (buf->clip_list_count == 1) 288 goto visible; 289 /* In the other case, we need to go through the list of rectangle 290 of the clipping region and check if the pixel is in any of those */ 291 r = buf->clip_list; 292 for (i=0; i<buf->clip_list_count; r++, i++) 293 if ((s->h >= r->left) && 294 (s->h <= r->right) && 295 (s->v >= r->top) && 296 (s->v <= r->bottom)) 297 goto visible; 298 /* The pixel is not visible. The star is marked as not drawn. */ 299 invisible: 300 s->last_draw_offset = INVALID; 301 return false; 302 visible: 303 /* The pixel is visible. The offset at which the star should be draw is 304 calculated and store for using by drawing (and erasing later). */ 305 s->last_draw_offset = s->v * buf->bytes_per_row + s->h * buf->bytes_per_pixel; 306 return true; 307 } 308 /* Complex case : the star is represented by more than one pixel. */ 309 else { 310 /* Calculate the box the bounding box of the matrix of 32 pixels used 311 to represent the star, called box. */ 312 box.left = s->h - 2; 313 box.right = s->h + 3; 314 box.top = s->v - 2; 315 box.bottom = s->v + 3; 316 317 /* Check if the box is fully outside of the bounding box of the clipping 318 region. That woudl guarantee that the star is invisible. */ 319 if ((box.right < buf->clip_bounds.left) || 320 (box.left > buf->clip_bounds.right) || 321 (box.bottom < buf->clip_bounds.top) || 322 (box.top > buf->clip_bounds.bottom)) 323 goto invisible_pat; 324 325 /* Now, we have to go through the list of rectangle of the clipping region 326 and cumulate the mask of the star matrix pixels that are visible in any 327 of those rectangle. At start time, the mask is empty. */ 328 total_visible = 0; 329 r = buf->clip_list; 330 for (i=0; i<buf->clip_list_count; r++, i++) { 331 /* When reseting the clipping, all pixel of the matrix are tested. In 332 the other mode, only the pixel previously visible are tested (as we 333 want to know which one of the previously drawn pixel still need to 334 be erased. */ 335 if (reset_clipping) 336 tmp_visible = 0xffffffff; 337 else 338 tmp_visible = s->last_draw_pattern; 339 340 /* Calculate the clipping on the left side of the rectangle. */ 341 delta = r->left-box.left; 342 if (delta > 5) 343 continue; 344 if (delta > 0) 345 tmp_visible &= visible_mask_left[delta]; 346 347 /* Calculate the clipping on the right side of the rectangle. */ 348 delta = box.right-r->right; 349 if (delta > 5) 350 continue; 351 if (delta > 0) 352 tmp_visible &= visible_mask_right[delta]; 353 354 /* Calculate the clipping on the top side of the rectangle. */ 355 delta = r->top-box.top; 356 if (delta > 5) 357 continue; 358 if (delta > 0) 359 tmp_visible &= visible_mask_top[delta]; 360 361 /* Calculate the clipping on the bottom side of the rectangle. */ 362 delta = box.bottom-r->bottom; 363 if (delta > 5) 364 continue; 365 if (delta > 0) 366 tmp_visible &= visible_mask_bottom[delta]; 367 368 /* Pixel of the matrix not clipped out at that point are visible 369 inside this rectangle of the clipping region. We need to add 370 them to the mask of currently known visible pixel. */ 371 total_visible |= tmp_visible; 372 /* If all pixel of the matrix are already visible, no need to continue 373 further. */ 374 if (total_visible == 0xffffffff) 375 goto visible_pat; 376 } 377 /* If no pixel are visible, then we know... */ 378 if (total_visible != 0) 379 goto visible_pat; 380 381 /* The star is not visible. It's marked as not drawn. */ 382 invisible_pat: 383 s->last_draw_offset = INVALID; 384 return false; 385 visible_pat: 386 /* The star is partially visible. The offset at which the star should be 387 draw is calculated and store for using by drawing (and erasing later). 388 The mask of which pixel of the matrix are visible is store for use 389 at drawing and erasing time. */ 390 s->last_draw_offset = s->v * buf->bytes_per_row + s->h * buf->bytes_per_pixel; 391 s->last_draw_pattern = total_visible; 392 return true; 393 } 394 } 395 396 /* After calling ProjectStar and CheckClipping, we're finally ready to 397 draw the star in its destination buffer. So let's do it... */ 398 void DrawStar(star *s, buffer *buf) 399 { 400 int32 i, index, count; 401 uint8 *draw8; 402 uint16 *draw16; 403 uint32 *draw32; 404 uint32 *colors; 405 uint8 *pat_list; 406 uint8 *pat_color_offset; 407 408 /* Simple case : the star is represented by only one pixel. */ 409 count = pattern_list_count[s->pattern_level]; 410 if (count == 1) { 411 /* Depending the depth mode of the drawing buffer... */ 412 switch (buf->depth_mode) { 413 case PIXEL_1_BYTE : 414 /* Get the pointer to the address we want to draw to... */ 415 draw8 = (uint8*)((char*)buf->bits + s->last_draw_offset); 416 /* ... and write the color pattern we want to use depending of 417 the lighting level and the color scheme of the star. */ 418 *draw8 = buf->colors[s->color_type][pixel_color_offset[s->level]]; 419 break; 420 case PIXEL_2_BYTES : 421 /* Same thing for 2 bytes mode */ 422 draw16 = (uint16*)((char*)buf->bits + s->last_draw_offset); 423 *draw16 = buf->colors[s->color_type][pixel_color_offset[s->level]]; 424 break; 425 case PIXEL_4_BYTES : 426 /* Same thing for 4 bytes mode */ 427 draw32 = (uint32*)((char*)buf->bits + s->last_draw_offset); 428 *draw32 = buf->colors[s->color_type][pixel_color_offset[s->level]]; 429 break; 430 } 431 } 432 /* Complex case : the star is represented by a multiple pixels. */ 433 else { 434 /* Pointer to the color table used depending the color scheme of 435 the star. */ 436 colors = buf->colors[s->color_type]; 437 pat_list = pattern_list[s->pattern_level]; 438 pat_color_offset = pattern_color_offset[s->pattern_level]; 439 440 /* Plot all pixel used to represent the star one after one... */ 441 for (i=0; i<count; i++) { 442 /* This is the index of the pixel in the matrix */ 443 index = pat_list[i]; 444 /* Check if this pixel is visible (using the result of the clipping) */ 445 if (s->last_draw_pattern & (1<<index)) { 446 switch (buf->depth_mode) { 447 case PIXEL_1_BYTE : 448 /* Get the pointer to the address we want to draw to... */ 449 draw8 = (uint8*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 450 /* ... and write the color pattern we want to use depending of 451 the lighting level and the color scheme of the star. */ 452 *draw8 = colors[pat_color_offset[i]]; 453 break; 454 case PIXEL_2_BYTES : 455 /* Same thing for 2 bytes mode */ 456 draw16 = (uint16*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 457 *draw16 = colors[pat_color_offset[i]]; 458 break; 459 case PIXEL_4_BYTES : 460 /* Same thing for 4 bytes mode */ 461 draw32 = (uint32*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 462 *draw32 = colors[pat_color_offset[i]]; 463 break; 464 } 465 } 466 } 467 } 468 } 469 470 /* Before redrawing a star at its new position, we need to erase what we draw 471 at the previous frame... */ 472 void EraseStar(star *s, buffer *buf) 473 { 474 int32 i, index, count; 475 uint8 *draw8; 476 uint16 *draw16; 477 uint32 *draw32; 478 uint32 back_color; 479 uint8 *pat_list; 480 481 /* Color pattern we use to erase the buffer. */ 482 back_color = buf->back_color; 483 484 /* Simple case : the star is represented by only one pixel. */ 485 count = pattern_list_count[s->pattern_level]; 486 if (count == 1) { 487 /* Depending the depth mode of the drawing buffer... */ 488 switch (buf->depth_mode) { 489 case PIXEL_1_BYTE : 490 /* Get the pointer to the address we want to erase... */ 491 draw8 = (uint8*)((char*)buf->bits + s->last_draw_offset); 492 /* ... and write the background color pattern. */ 493 *draw8 = back_color; 494 break; 495 case PIXEL_2_BYTES : 496 /* Same thing for 2 bytes mode */ 497 draw16 = (uint16*)((char*)buf->bits + s->last_draw_offset); 498 *draw16 = back_color; 499 break; 500 case PIXEL_4_BYTES : 501 /* Same thing for 4 bytes mode */ 502 draw32 = (uint32*)((char*)buf->bits + s->last_draw_offset); 503 *draw32 = back_color; 504 break; 505 } 506 } 507 /* Complex case : the star is represented by a multiple pixels. */ 508 else { 509 pat_list = pattern_list[s->pattern_level]; 510 511 /* Erase all pixel used to represent the star one after one... */ 512 for (i=0; i<count; i++) { 513 index = pat_list[i]; 514 /* Check if this pixel is visible (using the result of the clipping) */ 515 if (s->last_draw_pattern & (1<<index)) { 516 switch (buf->depth_mode) { 517 case PIXEL_1_BYTE : 518 /* Get the pointer to the address we want to draw to... */ 519 draw8 = (uint8*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 520 /* ... and write the background color pattern. */ 521 *draw8 = back_color; 522 break; 523 case PIXEL_2_BYTES : 524 /* Same thing for 2 bytes mode */ 525 draw16 = (uint16*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 526 *draw16 = back_color; 527 break; 528 case PIXEL_4_BYTES : 529 /* Same thing for 4 bytes mode */ 530 draw32 = (uint32*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 531 *draw32 = back_color; 532 break; 533 } 534 } 535 } 536 } 537 } 538 539 /* This function do the transition from previous state to the new state 540 as described in (geo), in the buffer (buf), for the list of star (sp) */ 541 void RefreshStarPacket(buffer *buf, star_packet *sp, geometry *geo) 542 { 543 int32 i, min_count; 544 star *s; 545 546 // TODO: For some reason, when selecting the "2 threads" option under vmware, 547 // some weird timing calculations finish with setting the star packet count to 548 // a negative number. This screws all the next calculations, and the animation 549 // then comes to a stop. 550 sp->count = max_c(sp->count, 0); 551 552 /* Calculate the number of stars that were process during the 553 previous frame and still need to be process for that frame. */ 554 min_count = sp->erase_count; 555 if (sp->count < min_count) 556 min_count = sp->count; 557 558 s = sp->list; 559 560 /* For all those star... */ 561 for (i=0; i<min_count; s++, i++) { 562 /* ... erase them if necessary, ... */ 563 if (s->last_draw_offset != INVALID) 564 EraseStar(s, buf); 565 /* ... project them at their new position, ... */ 566 if (ProjectStar(s, geo)) { 567 /* ... check the clipping of the buffer if the star are in 568 the pyramid of vision, ... */ 569 if (CheckClipping(s, buf, true)) 570 /* ... and draw them if they're really visible. */ 571 DrawStar(s, buf); 572 } 573 /* ... or mark them as invisible if they're not in the pyramid 574 of vision. */ 575 else 576 s->last_draw_offset = INVALID; 577 } 578 579 /* For star that were process at the previous frame but that we don't 580 want to process anymore, we just need to erase them. */ 581 for (; i<sp->erase_count; s++, i++) { 582 if (s->last_draw_offset != INVALID) 583 EraseStar(s, buf); 584 } 585 586 /* For star that were not process before, but are now, we just need to 587 go through the projection, clipping and drawing steps. */ 588 for (; i<sp->count; s++, i++) { 589 if (ProjectStar(s, geo)) { 590 if (CheckClipping(s, buf, true)) 591 DrawStar(s, buf); 592 } 593 else 594 s->last_draw_offset = INVALID; 595 } 596 } 597 598 /* Update the clipping visibility of all star of the list (sp) to 599 respect the new clipping defined for the buffer (buf). */ 600 void RefreshClipping(buffer *buf, star_packet *sp) 601 { 602 star *s; 603 int32 i; 604 605 s = sp->list; 606 for (i=0; i<sp->erase_count; s++, i++) { 607 if (s->last_draw_offset != INVALID) 608 CheckClipping(s, buf, false); 609 } 610 } 611 612 613