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 float b_sqrt(float x); 101 bool ProjectStar(star *s, geometry *geo); 102 bool CheckClipping(star *s, buffer *buf, bool reset_clipping); 103 void DrawStar(star *s, buffer *buf); 104 void EraseStar(star *s, buffer *buf); 105 106 /* Good approximation of square root, for x > 0.0 That resolves 107 the problem of having a dependency with the math library, and 108 it's good enough for what we need. */ 109 float b_sqrt(float x) { 110 uint32 val; 111 float y,z,t; 112 float flottant, tampon; 113 114 flottant = x; 115 val = *((uint32*)&flottant); 116 val >>= 1; 117 val += 0x1FC00000L; 118 *((uint32*)&tampon) = val; 119 y = tampon; 120 z = y*y+x; 121 t = y*y-x; 122 y *= (float)4.0; 123 x = z*z; 124 t = t*t; 125 y = z*y; 126 t = (float)2.0*x-t; 127 return t/y; 128 } 129 130 /* This function initialise the 32 sizes of anti-aliased star, each one 131 represented in 4 different half-pixel alignement : 132 x : -0.25, y : -0.25 133 x : +0.25, y : -0.25 134 x : -0.25, y : +0.25 135 x : +0.25, y : +0.25 */ 136 void InitPatterns() 137 { 138 int32 i, j, k, count; 139 float radius, x0, y0, x, y, dist, delta; 140 uint8 color; 141 uint8 *list, *color_offset; 142 143 /* do the 4 half-pixel alignement */ 144 for (j=0; j<4; j++) { 145 if (j&1) x0 = 1.25; 146 else x0 = 0.75; 147 if (j&2) y0 = 1.25; 148 else y0 = 0.75; 149 150 /* do the 32 sizes */ 151 for (i=0; i<LEVEL_COUNT; i++) { 152 radius = (float)(i+1) * (2.8/(float)LEVEL_COUNT); 153 count = 0; 154 list = pattern_list[j*LEVEL_COUNT + i]; 155 color_offset = pattern_color_offset[j*LEVEL_COUNT + i]; 156 157 /* scan the 32 pixels of the matrix */ 158 for (k=0; k<32; k++) { 159 x = ((float)pattern_dh[k] + ROUNDING) - x0; 160 y = ((float)pattern_dv[k] + ROUNDING) - y0; 161 162 dist = b_sqrt(x*x + y*y); 163 /* process non source pixel */ 164 if (dist > 0.5) { 165 delta = radius - dist + 0.5; 166 if (delta >= 1.0) { 167 *color_offset++ = 7; 168 *list++ = k; 169 count++; 170 } 171 else if (delta > 0.5) { 172 *color_offset++ = (uint8)(7.499 - 16.0 * (1.0 - delta) * (1.0 - delta) + ROUNDING); 173 *list++ = k; 174 count++; 175 } 176 else if (delta > 0) { 177 color = (uint8)(16.0 * delta * delta); 178 if (color > 0) { 179 *color_offset++ = color; 180 *list++ = k; 181 count++; 182 } 183 } 184 } 185 /* process source pixel (the one containing the center of the star) */ 186 else { 187 if (radius < 0.25) { 188 color = (uint8)(32.0 * radius * radius + ROUNDING); 189 if (color == 0) 190 color++; 191 } 192 else if (radius < 0.75) { 193 delta = radius + 0.25; 194 color = (uint8)(7.499 - 22.0 * (1.0 - delta) * (1.0 - delta) + ROUNDING); 195 } 196 else 197 color = 7; 198 *color_offset++ = color; 199 *list++ = k; 200 count++; 201 pixel_color_offset[i] = color; 202 } 203 } 204 pattern_list_count[j*LEVEL_COUNT + i] = count; 205 } 206 } 207 } 208 209 /* Project a star (s) in the view space of the camera, as described by (geo). 210 Returns true if the star seems to be visible (in the pyramid of vision, 211 closer than the rear plan, farther than the front plan), or false if it's 212 clear that the star isnot visible. */ 213 bool ProjectStar(star *s, geometry *geo) 214 { 215 int32 h_double, v_double, level; 216 float x0, y0, z0, x, y, z, inv_z; 217 218 /* Calculate the coordinate of the star after doing the cycling operation 219 that convert the cube of the starfield in a torus. This ensure that 220 get the copy of the star that is the only one likely to be visible from 221 the camera. */ 222 x0 = s->x; 223 if (x0 < geo->cutx) 224 x0 += 1.0; 225 y0 = s->y; 226 if (y0 < geo->cuty) 227 y0 += 1.0; 228 z0 = s->z; 229 if (z0 < geo->cutz) 230 z0 += 1.0; 231 /* Translate the star relative to the position of the camera. */ 232 x0 -= geo->x; 233 y0 -= geo->y; 234 z0 -= geo->z; 235 236 /* Calculate the z coordinate (depth) of the star in the camera referential. */ 237 z = geo->m[0][2]*x0 + geo->m[1][2]*y0 + geo->m[2][2]*z0; 238 239 /* Do the rear and front plan clipping */ 240 if ((z < geo->z_min) || (z > geo->z_max)) 241 return false; 242 243 /* Calculate the x coordinate (horizontal) of the star in the camera referential. */ 244 x = geo->m[0][0]*x0 + geo->m[1][0]*y0 + geo->m[2][0]*z0; 245 246 /* Do the left and right clipping based on the pyramid of vision. */ 247 if ((x < geo->xz_min*z-BORDER_CLIPPING) || (x > geo->xz_max*z+BORDER_CLIPPING)) 248 return false; 249 250 /* Calculate the y coordinate (vertical) of the star in the camera referential. */ 251 y = geo->m[0][1]*x0 + geo->m[1][1]*y0 + geo->m[2][1]*z0; 252 253 /* Do the top and bottom clipping based on the pyramid of vision. */ 254 if ((y < geo->yz_min*z-BORDER_CLIPPING) || (y > geo->yz_max*z+BORDER_CLIPPING)) 255 return false; 256 257 /* Calculate the invert of z, used to project both H and V coordinate. Apply 258 the zoom-factor at the same time. The zoom-factor was overscale by a factor 259 of two in advance, for the half-pixel precision processing */ 260 inv_z = geo->zoom_factor/z; 261 262 /* Calculate the double pixel coordinate in the buffer (in half-pixel). */ 263 h_double = (int32)(x * inv_z + geo->offset_h); 264 v_double = (int32)(y * inv_z + geo->offset_v); 265 266 /* Calculate the light level of the star. We use that little weird function 267 to a get faster gradient to black near the rear plan. */ 268 level = (int32)(s->size * (inv_z * geo->z_max_square - z * geo->zoom_factor)) >> 8; 269 /* The light level can go higher that our max (saturation). */ 270 if (level >= LEVEL_COUNT) 271 level = LEVEL_COUNT-1; 272 273 /* Get the real pixel coordinate in the buffer from the double coordinates */ 274 s->h = h_double >> 1; 275 s->v = v_double >> 1; 276 /* Save the light level (used to recognize single pixel star) */ 277 s->level = level; 278 /* switch between the 4 pattern table use for the 4 half-aligned. */ 279 if ((h_double & 1) == 1) level += LEVEL_COUNT; 280 if ((v_double & 1) == 1) level += 2*LEVEL_COUNT; 281 s->pattern_level = level; 282 return true; 283 } 284 285 /* Once a star has been projected (using ProjectStar), we need to determine 286 which pixel of the star matrix are visible (if any). This depend of the 287 clipping of the specific buffer you're using. This function will do that 288 for the star (s), in the buffer (buf). It will return false if the star 289 is fully invisible, true if not. The falg reset_clipping is used to 290 reprocess the clipping from scratsh, or to just cumulate the new clipping 291 to the last drawing clipping (this is needed when updating the clipping 292 of every stars after changing the clipping region of the buffer). */ 293 bool CheckClipping(star *s, buffer *buf, bool reset_clipping) 294 { 295 int32 delta; 296 uint32 i, total_visible, tmp_visible; 297 clipping_rect box; 298 clipping_rect *r; 299 300 /* Simple case : the star is represented by only one pixel. */ 301 if (pattern_list_count[s->pattern_level] == 1) { 302 /* if the pixel is not in the bounding box of the clipping region, 303 the star is guarantee to be invisible. */ 304 if ((s->h < buf->clip_bounds.left) || 305 (s->h > buf->clip_bounds.right) || 306 (s->v < buf->clip_bounds.top) || 307 (s->v > buf->clip_bounds.bottom)) 308 goto invisible; 309 /* if the clipping region contains only one rectangle, then it's 310 equal to its bounding box, so no further test are needed. */ 311 if (buf->clip_list_count == 1) 312 goto visible; 313 /* In the other case, we need to go through the list of rectangle 314 of the clipping region and check if the pixel is in any of those */ 315 r = buf->clip_list; 316 for (i=0; i<buf->clip_list_count; r++, i++) 317 if ((s->h >= r->left) && 318 (s->h <= r->right) && 319 (s->v >= r->top) && 320 (s->v <= r->bottom)) 321 goto visible; 322 /* The pixel is not visible. The star is marked as not drawn. */ 323 invisible: 324 s->last_draw_offset = INVALID; 325 return false; 326 visible: 327 /* The pixel is visible. The offset at which the star should be draw is 328 calculated and store for using by drawing (and erasing later). */ 329 s->last_draw_offset = s->v * buf->bytes_per_row + s->h * buf->bytes_per_pixel; 330 return true; 331 } 332 /* Complex case : the star is represented by more than one pixel. */ 333 else { 334 /* Calculate the box the bounding box of the matrix of 32 pixels used 335 to represent the star, called box. */ 336 box.left = s->h - 2; 337 box.right = s->h + 3; 338 box.top = s->v - 2; 339 box.bottom = s->v + 3; 340 341 /* Check if the box is fully outside of the bounding box of the clipping 342 region. That woudl guarantee that the star is invisible. */ 343 if ((box.right < buf->clip_bounds.left) || 344 (box.left > buf->clip_bounds.right) || 345 (box.bottom < buf->clip_bounds.top) || 346 (box.top > buf->clip_bounds.bottom)) 347 goto invisible_pat; 348 349 /* Now, we have to go through the list of rectangle of the clipping region 350 and cumulate the mask of the star matrix pixels that are visible in any 351 of those rectangle. At start time, the mask is empty. */ 352 total_visible = 0; 353 r = buf->clip_list; 354 for (i=0; i<buf->clip_list_count; r++, i++) { 355 /* When reseting the clipping, all pixel of the matrix are tested. In 356 the other mode, only the pixel previously visible are tested (as we 357 want to know which one of the previously drawn pixel still need to 358 be erased. */ 359 if (reset_clipping) 360 tmp_visible = 0xffffffff; 361 else 362 tmp_visible = s->last_draw_pattern; 363 364 /* Calculate the clipping on the left side of the rectangle. */ 365 delta = r->left-box.left; 366 if (delta > 5) 367 continue; 368 if (delta > 0) 369 tmp_visible &= visible_mask_left[delta]; 370 371 /* Calculate the clipping on the right side of the rectangle. */ 372 delta = box.right-r->right; 373 if (delta > 5) 374 continue; 375 if (delta > 0) 376 tmp_visible &= visible_mask_right[delta]; 377 378 /* Calculate the clipping on the top side of the rectangle. */ 379 delta = r->top-box.top; 380 if (delta > 5) 381 continue; 382 if (delta > 0) 383 tmp_visible &= visible_mask_top[delta]; 384 385 /* Calculate the clipping on the bottom side of the rectangle. */ 386 delta = box.bottom-r->bottom; 387 if (delta > 5) 388 continue; 389 if (delta > 0) 390 tmp_visible &= visible_mask_bottom[delta]; 391 392 /* Pixel of the matrix not clipped out at that point are visible 393 inside this rectangle of the clipping region. We need to add 394 them to the mask of currently known visible pixel. */ 395 total_visible |= tmp_visible; 396 /* If all pixel of the matrix are already visible, no need to continue 397 further. */ 398 if (total_visible == 0xffffffff) 399 goto visible_pat; 400 } 401 /* If no pixel are visible, then we know... */ 402 if (total_visible != 0) 403 goto visible_pat; 404 405 /* The star is not visible. It's marked as not drawn. */ 406 invisible_pat: 407 s->last_draw_offset = INVALID; 408 return false; 409 visible_pat: 410 /* The star is partially visible. The offset at which the star should be 411 draw is calculated and store for using by drawing (and erasing later). 412 The mask of which pixel of the matrix are visible is store for use 413 at drawing and erasing time. */ 414 s->last_draw_offset = s->v * buf->bytes_per_row + s->h * buf->bytes_per_pixel; 415 s->last_draw_pattern = total_visible; 416 return true; 417 } 418 } 419 420 /* After calling ProjectStar and CheckClipping, we're finally ready to 421 draw the star in its destination buffer. So let's do it... */ 422 void DrawStar(star *s, buffer *buf) 423 { 424 int32 i, index, count; 425 uint8 *draw8; 426 uint16 *draw16; 427 uint32 *draw32; 428 uint32 *colors; 429 uint8 *pat_list; 430 uint8 *pat_color_offset; 431 432 /* Simple case : the star is represented by only one pixel. */ 433 count = pattern_list_count[s->pattern_level]; 434 if (count == 1) { 435 /* Depending the depth mode of the drawing buffer... */ 436 switch (buf->depth_mode) { 437 case PIXEL_1_BYTE : 438 /* Get the pointer to the address we want to draw to... */ 439 draw8 = (uint8*)((char*)buf->bits + s->last_draw_offset); 440 /* ... and write the color pattern we want to use depending of 441 the lighting level and the color scheme of the star. */ 442 *draw8 = buf->colors[s->color_type][pixel_color_offset[s->level]]; 443 break; 444 case PIXEL_2_BYTES : 445 /* Same thing for 2 bytes mode */ 446 draw16 = (uint16*)((char*)buf->bits + s->last_draw_offset); 447 *draw16 = buf->colors[s->color_type][pixel_color_offset[s->level]]; 448 break; 449 case PIXEL_4_BYTES : 450 /* Same thing for 4 bytes mode */ 451 draw32 = (uint32*)((char*)buf->bits + s->last_draw_offset); 452 *draw32 = buf->colors[s->color_type][pixel_color_offset[s->level]]; 453 break; 454 } 455 } 456 /* Complex case : the star is represented by a multiple pixels. */ 457 else { 458 /* Pointer to the color table used depending the color scheme of 459 the star. */ 460 colors = buf->colors[s->color_type]; 461 pat_list = pattern_list[s->pattern_level]; 462 pat_color_offset = pattern_color_offset[s->pattern_level]; 463 464 /* Plot all pixel used to represent the star one after one... */ 465 for (i=0; i<count; i++) { 466 /* This is the index of the pixel in the matrix */ 467 index = pat_list[i]; 468 /* Check if this pixel is visible (using the result of the clipping) */ 469 if (s->last_draw_pattern & (1<<index)) { 470 switch (buf->depth_mode) { 471 case PIXEL_1_BYTE : 472 /* Get the pointer to the address we want to draw to... */ 473 draw8 = (uint8*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 474 /* ... and write the color pattern we want to use depending of 475 the lighting level and the color scheme of the star. */ 476 *draw8 = colors[pat_color_offset[i]]; 477 break; 478 case PIXEL_2_BYTES : 479 /* Same thing for 2 bytes mode */ 480 draw16 = (uint16*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 481 *draw16 = colors[pat_color_offset[i]]; 482 break; 483 case PIXEL_4_BYTES : 484 /* Same thing for 4 bytes mode */ 485 draw32 = (uint32*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 486 *draw32 = colors[pat_color_offset[i]]; 487 break; 488 } 489 } 490 } 491 } 492 } 493 494 /* Before redrawing a star at its new position, we need to erase what we draw 495 at the previous frame... */ 496 void EraseStar(star *s, buffer *buf) 497 { 498 int32 i, index, count; 499 uint8 *draw8; 500 uint16 *draw16; 501 uint32 *draw32; 502 uint32 back_color; 503 uint8 *pat_list; 504 505 /* Color pattern we use to erase the buffer. */ 506 back_color = buf->back_color; 507 508 /* Simple case : the star is represented by only one pixel. */ 509 count = pattern_list_count[s->pattern_level]; 510 if (count == 1) { 511 /* Depending the depth mode of the drawing buffer... */ 512 switch (buf->depth_mode) { 513 case PIXEL_1_BYTE : 514 /* Get the pointer to the address we want to erase... */ 515 draw8 = (uint8*)((char*)buf->bits + s->last_draw_offset); 516 /* ... and write the background color pattern. */ 517 *draw8 = back_color; 518 break; 519 case PIXEL_2_BYTES : 520 /* Same thing for 2 bytes mode */ 521 draw16 = (uint16*)((char*)buf->bits + s->last_draw_offset); 522 *draw16 = back_color; 523 break; 524 case PIXEL_4_BYTES : 525 /* Same thing for 4 bytes mode */ 526 draw32 = (uint32*)((char*)buf->bits + s->last_draw_offset); 527 *draw32 = back_color; 528 break; 529 } 530 } 531 /* Complex case : the star is represented by a multiple pixels. */ 532 else { 533 pat_list = pattern_list[s->pattern_level]; 534 535 /* Erase all pixel used to represent the star one after one... */ 536 for (i=0; i<count; i++) { 537 index = pat_list[i]; 538 /* Check if this pixel is visible (using the result of the clipping) */ 539 if (s->last_draw_pattern & (1<<index)) { 540 switch (buf->depth_mode) { 541 case PIXEL_1_BYTE : 542 /* Get the pointer to the address we want to draw to... */ 543 draw8 = (uint8*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 544 /* ... and write the background color pattern. */ 545 *draw8 = back_color; 546 break; 547 case PIXEL_2_BYTES : 548 /* Same thing for 2 bytes mode */ 549 draw16 = (uint16*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 550 *draw16 = back_color; 551 break; 552 case PIXEL_4_BYTES : 553 /* Same thing for 4 bytes mode */ 554 draw32 = (uint32*)((char*)buf->pattern_bits[index] + s->last_draw_offset); 555 *draw32 = back_color; 556 break; 557 } 558 } 559 } 560 } 561 } 562 563 /* This function do the transition from previous state to the new state 564 as described in (geo), in the buffer (buf), for the list of star (sp) */ 565 void RefreshStarPacket(buffer *buf, star_packet *sp, geometry *geo) 566 { 567 int32 i, min_count; 568 star *s; 569 570 /* Calculate the number of stars that were process during the 571 previous frame and still need to be process for that frame. */ 572 min_count = sp->erase_count; 573 if (sp->count < min_count) 574 min_count = sp->count; 575 576 s = sp->list; 577 578 /* For all those star... */ 579 for (i=0; i<min_count; s++, i++) { 580 /* ... erase them if necessary, ... */ 581 if (s->last_draw_offset != INVALID) 582 EraseStar(s, buf); 583 /* ... project them at their new position, ... */ 584 if (ProjectStar(s, geo)) { 585 /* ... check the clipping of the buffer if the star are in 586 the pyramid of vision, ... */ 587 if (CheckClipping(s, buf, true)) 588 /* ... and draw them if they're really visible. */ 589 DrawStar(s, buf); 590 } 591 /* ... or mark them as invisible if they're not in the pyramid 592 of vision. */ 593 else 594 s->last_draw_offset = INVALID; 595 } 596 597 /* For star that were process at the previous frame but that we don't 598 want to process anymore, we just need to erase them. */ 599 for (; i<sp->erase_count; s++, i++) 600 if (s->last_draw_offset != INVALID) 601 EraseStar(s, buf); 602 603 /* For star that were not process before, but are now, we just need to 604 go through the projection, clipping and drawing steps. */ 605 for (; i<sp->count; s++, i++) { 606 if (ProjectStar(s, geo)) { 607 if (CheckClipping(s, buf, true)) 608 DrawStar(s, buf); 609 } 610 else 611 s->last_draw_offset = INVALID; 612 } 613 } 614 615 /* Update the clipping visibility of all star of the list (sp) to 616 respect the new clipping defined for the buffer (buf). */ 617 void RefreshClipping(buffer *buf, star_packet *sp) 618 { 619 star *s; 620 int32 i; 621 622 s = sp->list; 623 for (i=0; i<sp->erase_count; s++, i++) 624 if (s->last_draw_offset != INVALID) 625 CheckClipping(s, buf, false); 626 } 627 628 629