00001
00025 #include "render.h"
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include <assert.h>
00029
00030
00031
00032
00033 #if !defined(_MSC_VER) && !defined(DOXYGEN)
00034 #define _restrict_ __restrict
00035 #else
00036 #define _restrict_
00037 #endif
00038
00039
00040 struct DisplayInfo_t dInfo;
00041 Layer layers[MAX_LAYERS];
00042
00050 static struct UpdateList_t {
00067 SDL_Rect *rects[BACK_STATES];
00074 Uint32 area[BACK_STATES];
00080 Uint16 numRects[BACK_STATES];
00084 Uint16 length;
00092 Sint8 dirty;
00097 Uint8 currState;
00102 Uint8 nextState;
00107 Uint8 totalStates;
00114 Bool renderAll;
00115 } updateList;
00116
00117 Bool RenderInit(Uint16 numRects, Uint8 numStates) {
00118
00119 assert((numRects > 0) && (numStates > 0) && (numStates <= BACK_STATES));
00120
00121 if ((numRects == 0) || (numStates == 0) || (numStates > BACK_STATES)) {
00122
00123
00124 return FALSE;
00125 }
00126
00127 memset(&updateList, 0, sizeof(updateList));
00128
00129 memset(layers, 0 , sizeof(layers));
00130
00131 updateList.dirty = updateList.totalStates = numStates;
00132 updateList.nextState = 1;
00133
00134
00135 updateList.renderAll = TRUE;
00136
00137 do {
00138
00139 numStates--;
00140
00141 if ((updateList.rects[numStates] = malloc(sizeof(SDL_Rect) * numRects))
00142 == NULL) {
00143
00144 return FALSE;
00145 }
00146 } while (numStates > 0);
00147
00148 return TRUE;
00149 }
00150
00151 void RenderClearStates(Uint8 numStates) {
00152 assert(FALSE);
00153 }
00154
00155 void RenderUninit() {
00156
00157 do {
00158
00159 updateList.totalStates--;
00160
00161 free(updateList.rects[updateList.totalStates]);
00162
00163 updateList.numRects[updateList.totalStates] = 0;
00164 } while (updateList.totalStates > 0);
00165
00166 updateList.length = 0;
00167 }
00168
00169 void ForceRenderAll() {
00170 updateList.renderAll = TRUE;
00171 updateList.dirty = updateList.totalStates;
00172 }
00173
00174 Bool RenderWillRenderAll() {
00175 return updateList.renderAll;
00176 }
00177
00178 void ForceDirtyFrame() {
00179 updateList.dirty = updateList.totalStates;
00180 }
00181
00182 void AddRenderItem(RenderItem *item, int layer) {
00183 assert((layer >= 0) && (layer < MAX_LAYERS));
00184 assert(item->parent == NULL);
00185 assert(item->renderer != NULL);
00186
00187 if (layers[layer].worldListLast == NULL) {
00188
00189 layers[layer].worldListLast = layers[layer].worldListFirst = item;
00190
00191 item->next = item->prev = NULL;
00192 }
00193 else {
00194
00195 item->next = NULL;
00196 item->prev = layers[layer].worldListLast;
00197
00198 layers[layer].worldListLast->next = item;
00199
00200 layers[layer].worldListLast = item;
00201 }
00202
00203 item->parent = &layers[layer];
00204
00205 item->enableDraw = FALSE;
00206
00207 item->src.x = item->src.y = 0;
00208 item->src.w = item->loc.w;
00209 item->src.h = item->loc.h;
00210 }
00211
00212 void RemoveRenderItem(RenderItem *item) {
00213
00214 if (item->prev == NULL) {
00215
00216
00217 if ((item->parent->worldListFirst = item->parent->worldListFirst->next)
00218 == NULL) {
00219
00220 item->parent->worldListLast = NULL;
00221 }
00222 else {
00223
00224 item->parent->worldListFirst->prev = NULL;
00225 }
00226 }
00227
00228 else if (item->next == NULL) {
00229
00230
00231 (item->parent->worldListLast = item->parent->worldListLast->prev)->next
00232 = NULL;
00233 }
00234
00235 else {
00236 item->prev->next = item->next;
00237 item->next->prev = item->prev;
00238 }
00239
00240
00241 item->parent = (Layer*)(item->next = item->prev = (RenderItem*)NULL);
00242
00243 }
00244
00256 static void AddToUpdateLists(RenderItem * _restrict_ item,
00257 SDL_Rect * _restrict_ rect) {
00258 int loop,
00259 state,
00260 stateInd,
00261 delta,
00262 area,
00263 overlapArea,
00264 proposedArea,
00265 proposedIndex = -1;
00266 SDL_Rect * _restrict_ updateRect, overlap, proposed;
00267
00268 #if (BACK_STATES > 2)
00269 #error This loop will fail with more than two display states
00270 #endif
00271 for (stateInd = 0, state = updateList.currState;
00272 stateInd < updateList.totalStates;
00273 stateInd++, state = *(((Uint8*)&(updateList.currState)) + stateInd)) {
00274
00275 updateRect = updateList.rects[state];
00276
00277 for (loop = 0; loop < updateList.numRects[state];
00278 loop++, updateRect++) {
00279
00280
00281
00282 if ((delta = updateRect->y - rect->y) > 0) {
00283
00284 if ((delta > rect->h) || !(overlap.h =
00285 (rect->h - delta) > updateRect->h ? updateRect->h :
00286 rect->h - delta))
00287 continue;
00288
00289 overlap.y = updateRect->y;
00290 }
00291 else {
00292
00293 if ((-delta > updateRect->h) || !(overlap.h =
00294 (updateRect->h + delta) > rect->h ? rect->h :
00295 updateRect->h + delta))
00296 continue;
00297
00298 overlap.y = rect->y;
00299 }
00300
00301 if ((delta = updateRect->x - rect->x) > 0) {
00302
00303 if ((delta > rect->w) || !(overlap.w =
00304 (rect->w - delta) > updateRect->w ? updateRect->w :
00305 rect->w - delta))
00306 continue;
00307
00308 overlap.x = updateRect->x;
00309 }
00310 else {
00311
00312 if ((-delta > updateRect->w) || !(overlap.w =
00313 (updateRect->w + delta) > rect->w ? rect->w :
00314 updateRect->w + delta))
00315 continue;
00316
00317 overlap.x = rect->x;
00318 }
00319
00320
00321 if ((overlap.x == rect->x) && (overlap.y == rect->y) &&
00322 (overlap.w == rect->w) && (overlap.h == rect->h)) {
00323
00324 item->rectIndex[stateInd] = loop;
00325
00326 goto AddToUpdateLists_nextState;
00327 }
00328
00329 overlapArea = overlap.h * overlap.w;
00330
00331
00332 if (rect->x < updateRect->x) {
00333 overlap.x = rect->x;
00334 }
00335 else {
00336 overlap.x = updateRect->x;
00337 }
00338 if ((rect->x + rect->w) >
00339 (updateRect->x + updateRect->w)) {
00340 overlap.w = rect->x + rect->w - overlap.x;
00341 }
00342 else {
00343 overlap.w = updateRect->x + updateRect->w - overlap.x;
00344 }
00345 if (rect->y < updateRect->y) {
00346 overlap.y = rect->y;
00347 }
00348 else {
00349 overlap.y = updateRect->y;
00350 }
00351 if ((rect->y + rect->h) >
00352 (updateRect->y + updateRect->h)) {
00353 overlap.h = rect->y + rect->h - overlap.y;
00354 }
00355 else {
00356 overlap.h = updateRect->y + updateRect->h - overlap.y;
00357 }
00358
00359 if ((overlapArea > (area = overlap.w * overlap.h - overlapArea)) &&
00360
00361 (((proposedIndex > -1) && (proposedArea > area)) ||
00362 (proposedIndex == -1 ))) {
00363
00364 proposedIndex = loop;
00365 proposedArea = area;
00366 proposed = overlap;
00367 }
00368 }
00369
00370 if (proposedIndex == -1) {
00371
00372 *updateRect = *rect;
00373
00374 item->rectIndex[stateInd] = updateList.numRects[state]++;
00375 }
00376 else {
00377
00378 item->rectIndex[stateInd] = proposedIndex;
00379
00380 updateList.rects[state][proposedIndex] = proposed;
00381 }
00382 AddToUpdateLists_nextState: ;
00383
00384
00385
00386
00387 }
00388 }
00389
00390 void PlaceRenderItem(RenderItem * _restrict_ item) {
00391 SDL_Rect * _restrict_ prev;
00392 int loop;
00393
00394 item->placed = item->enableDraw = TRUE;
00395
00396
00397 item->physLocX = item->loc.x + item->parent->offX;
00398 item->physLocY = item->loc.y + item->parent->offY;
00399
00400 if ((item->physLocX <= -item->loc.w) || (item->physLocX >= dInfo.pw)
00401 || (item->physLocY <= -item->loc.h) || (item->physLocY >= dInfo.ph)) {
00402
00403 item->visible = FALSE;
00404
00405 if (!updateList.renderAll) {
00406
00407 #if (BACK_STATES > 2)
00408 #error This will fail with more than two display states
00409 #endif
00410
00411 for (prev = &(item->prevStates[updateList.totalStates - 1]),
00412 loop = updateList.totalStates; loop > 0; prev--, loop--) {
00413
00414 if ((prev->w == 0) || (prev->h == 0))
00415
00416 continue;
00417
00418 AddToUpdateLists(item, prev);
00419
00420 item->parent->dirty = TRUE;
00421
00422 updateList.dirty = updateList.totalStates;
00423 }
00424 }
00425
00426 return;
00427 }
00428
00429 if (updateList.renderAll) {
00430
00431 return;
00432 }
00433
00434 if (item->src.w == 0) {
00435 assert(item->loc.w > item->src.x);
00436
00437 item->src.w = item->loc.w - item->src.x;
00438 }
00439
00440 if (item->src.h == 0) {
00441 assert(item->loc.h > item->src.y);
00442
00443 item->src.h = item->loc.h - item->src.y;
00444 }
00445
00446 item->physDest.x = item->physLocX + item->src.x;
00447 item->physDest.w = item->src.w;
00448 item->physDest.y = item->physLocY + item->src.y;
00449 item->physDest.h = item->src.h;
00450
00451 ClipRectToWH(&(item->physDest), dInfo.pw, dInfo.ph);
00452
00453 item->src.x = item->physDest.x - item->physLocX;
00454 item->src.y = item->physDest.y - item->physLocY;
00455 item->src.w = item->physDest.w;
00456 item->src.h = item->physDest.h;
00457
00458 if ((item->physDest.w == 0) || (item->physDest.h == 0)) {
00459
00460 return;
00461 }
00462
00463 item->dirty = TRUE;
00464
00465 item->visible = TRUE;
00466
00467 item->parent->dirty = TRUE;
00468
00469 updateList.dirty = updateList.totalStates;
00470
00471 #if (BACK_STATES > 2)
00472 #error This will fail with more than two display states
00473 #endif
00474
00475 for (prev = &(item->prevStates[updateList.totalStates - 1]),
00476 loop = updateList.totalStates + 1; loop > 0; prev--, loop--) {
00477
00478 if ((prev->w == 0) || (prev->h == 0))
00479
00480 continue;
00481
00482 AddToUpdateLists(item, prev);
00483 }
00484
00485
00486 }
00487
00488 void ClearRenderItem(RenderItem *item) {
00489 int loop, state;
00490
00491 item->dirty = item->visible = item->enableDraw = FALSE;
00492
00493 if ((item->physDest.x <= -item->loc.w) || (item->physDest.x >= dInfo.pw)
00494 || (item->physDest.y <= -item->loc.h) || (item->physDest.y >= dInfo.ph)) {
00495
00496 return;
00497 }
00498
00499 updateList.dirty = updateList.totalStates;
00500
00501 #if (BACK_STATES > 2)
00502 #error This loop will fail with more than two display states
00503 #endif
00504 for (loop = 0, state = updateList.currState; loop < updateList.totalStates;
00505 loop++, state = *(((Uint8*)&(updateList.currState)) + loop)) {
00506
00507 if (item->prevStates[loop].w > 0) {
00508
00509 updateList.rects[state][updateList.numRects[state]++] =
00510 item->prevStates[updateList.totalStates - loop - 1];
00511
00512
00513
00514 }
00515 }
00516 }
00517
00518 void SetLayerOffset(Layer * _restrict_ layer, Sint16 x, Sint16 y) {
00519 RenderItem * _restrict_ item;
00520
00521 layer->offX = x;
00522 layer->offY = y;
00523
00524
00525 for (item = layer->worldListFirst; item != NULL; item = item->next) {
00526
00527 if (item->enableDraw) {
00528
00529 PlaceRenderItem(item);
00530 }
00531 }
00532 }
00533
00547 static Bool FindNextCollision(RenderItem * _restrict_ item) {
00548 int loop, delta;
00549 SDL_Rect * _restrict_ rect;
00550
00551 for (rect = &(updateList.rects[updateList.currState][(loop = item->rectIndex[0] + 1)]);
00552 loop < updateList.numRects[updateList.currState]; loop++, rect++) {
00553
00554
00555 if ((delta = rect->y - item->physLocY) > 0) {
00556
00557 if ((delta > item->loc.h) || !(item->physDest.h =
00558 (item->loc.h - delta) > rect->h ? rect->h :
00559 item->loc.h - delta))
00560 continue;
00561
00562 item->physDest.y = rect->y;
00563 }
00564 else {
00565
00566 if ((-delta > rect->h) || !(item->physDest.h =
00567 (rect->h + delta) > item->loc.h ? item->loc.h :
00568 rect->h + delta))
00569 continue;
00570
00571 item->physDest.y = item->physLocY;
00572 }
00573
00574 if ((delta = rect->x - item->physLocX) > 0) {
00575
00576 if ((delta > item->loc.w) || !(item->physDest.w =
00577 (item->loc.w - delta) > rect->w ? rect->w :
00578 item->loc.w - delta))
00579 continue;
00580
00581 item->physDest.x = rect->x;
00582 }
00583 else {
00584
00585 if ((-delta > rect->w) || !(item->physDest.w =
00586 (rect->w + delta) > item->loc.w ? item->loc.w :
00587 rect->w + delta))
00588 continue;
00589
00590 item->physDest.x = item->physLocX;
00591 }
00592
00593
00594 item->rectIndex[0] = loop;
00595
00596 item->src.x = item->physDest.x - item->physLocX;
00597 item->src.y = item->physDest.y - item->physLocY;
00598 item->src.w = item->physDest.w;
00599 item->src.h = item->physDest.h;
00600 return TRUE;
00601 }
00602
00603 return FALSE;
00604 }
00605
00606 void Render() {
00607 RenderItem * _restrict_ item;
00608 Layer * _restrict_ layer;
00609 int loop, state;
00610 SDL_Rect * _restrict_ rect;
00611 if (updateList.renderAll) {
00612
00613
00614 for (layer = layers, loop = MAX_LAYERS; loop > 0; layer++, loop--) {
00615
00616 for (item = layer->worldListFirst; item != NULL; item = item->next) {
00617
00618 if (!item->enableDraw) continue;
00619
00620 item->physDest.x = item->physLocX = item->loc.x + layer->offX;
00621 item->physDest.y = item->physLocY = item->loc.y + layer->offY;
00622 item->physDest.w = item->loc.w;
00623 item->physDest.h = item->loc.h;
00624
00625 ClipRectToWH(&(item->physDest), dInfo.pw, dInfo.ph);
00626
00627 if ((item->physDest.w == 0) || (item->physDest.h == 0)) {
00628
00629 for (state = updateList.totalStates - 1; state > 0; state--) {
00630
00631 item->prevStates[state] = item->prevStates[state - 1];
00632 }
00633
00634
00635 item->prevStates[0].w = item->prevStates[0].h = 0;
00636
00637 item->dirty = item->visible = item->placed = FALSE;
00638
00639 continue;
00640 }
00641
00642 item->src.x = item->physDest.x - item->physLocX;
00643 item->src.y = item->physDest.y - item->physLocY;
00644 item->src.w = item->physDest.w;
00645 item->src.h = item->physDest.h;
00646
00647 (*(item->renderer))(dInfo.display, item);
00648 #ifdef RENDER_INDIVIDUAL_UPDATES
00649 SDL_UpdateRect(dInfo.display, item->physDest.x,
00650 item->physDest.y, item->physDest.w, item->physDest.h);
00651 #endif
00652
00653
00654 for (state = updateList.totalStates - 1; state > 0; state--) {
00655
00656 item->prevStates[state] = item->prevStates[state - 1];
00657 }
00658
00659
00660 item->prevStates[0].x = item->physLocX;
00661 item->prevStates[0].y = item->physLocY;
00662 item->prevStates[0].w = item->loc.w;
00663 item->prevStates[0].h = item->loc.h;
00664
00665 ClipRectToWH(&(item->prevStates[0]), dInfo.pw, dInfo.ph);
00666
00667 item->dirty = item->visible = item->placed = FALSE;
00668 }
00669
00670 layer->flags = 0;
00671 }
00672 #ifndef RENDER_INDIVIDUAL_UPDATES
00673
00674 SDL_UpdateRect(dInfo.display, 0, 0, 0, 0);
00675 #endif
00676 }
00677 else if (updateList.dirty) {
00678
00679 for (layer = layers, loop = MAX_LAYERS; loop > 0; layer++, loop--) {
00680
00681
00682 for (item = layer->worldListFirst; item != NULL; item = item->next) {
00683
00684 if (item->dirty) {
00685
00686
00687
00688 rect = &(updateList.rects[updateList.currState][item->rectIndex[0]]);
00689
00690 if ((item->physDest.w < rect->w) ||
00691 (item->physDest.h < rect->h)) {
00692
00693 item->physDest.x = item->physLocX;
00694 item->physDest.y = item->physLocY;
00695 item->physDest.w = item->loc.w;
00696 item->physDest.h = item->loc.h;
00697
00698 ClipRectToRect(&(item->physDest), rect);
00699
00700 item->src.x = item->physDest.x - item->physLocX;
00701 item->src.y = item->physDest.y - item->physLocY;
00702 item->src.w = item->physDest.w;
00703 item->src.h = item->physDest.h;
00704 }
00705
00706 (*(item->renderer))(dInfo.display, item);
00707 #ifdef RENDER_INDIVIDUAL_UPDATES
00708 SDL_UpdateRect(dInfo.display, item->physDest.x,
00709 item->physDest.y, item->physDest.w, item->physDest.h);
00710 #endif
00711
00712 Render_updateStates:
00713
00714 for (state = updateList.totalStates - 1; state > 0; state--) {
00715
00716 item->prevStates[state] = item->prevStates[state - 1];
00717 }
00718
00719
00720 item->prevStates[0].x = item->physLocX;
00721 item->prevStates[0].y = item->physLocY;
00722 item->prevStates[0].w = item->loc.w;
00723 item->prevStates[0].h = item->loc.h;
00724
00725 ClipRectToWH(&(item->prevStates[0]), dInfo.pw, dInfo.ph);
00726 }
00727 else if ((layer->dirty || !item->placed) && item->enableDraw) {
00728
00729
00730 item->physLocX = item->loc.x + layer->offX;
00731 item->physLocY = item->loc.y + layer->offY;
00732
00733 if ((item->physLocX <= -item->loc.w)
00734 || (item->physLocX >= dInfo.pw)
00735 || (item->physLocY <= -item->loc.h)
00736 || (item->physLocY >= dInfo.ph)) {
00737
00738 item->visible = FALSE;
00739
00740 goto Render_updateStatesInvis;
00741 }
00742 else {
00743
00744 item->visible = TRUE;
00745
00746 item->rectIndex[0] = -1;
00747
00748 while (FindNextCollision(item)) {
00749
00750 (*(item->renderer))(dInfo.display, item);
00751 #ifdef RENDER_INDIVIDUAL_UPDATES
00752 SDL_UpdateRect(dInfo.display, item->physDest.x,
00753 item->physDest.y, item->physDest.w,
00754 item->physDest.h);
00755 #endif
00756 }
00757 }
00758
00759 goto Render_updateStates;
00760 }
00761 else {
00762
00763 Render_updateStatesInvis:
00764 for (state = updateList.totalStates - 1; state > 0; state--) {
00765
00766 item->prevStates[state] = item->prevStates[state - 1];
00767 }
00768
00769
00770 item->prevStates[0].w = item->prevStates[0].h = 0;
00771 }
00772
00773
00774
00775 item->dirty = item->visible = item->placed = FALSE;
00776
00777 item->src.x = item->src.y = item->src.w = item->src.h = 0;
00778 }
00779
00780 layer->flags = 0;
00781 }
00782
00783 #ifndef RENDER_INDIVIDUAL_UPDATES
00784 SDL_UpdateRects(dInfo.display,
00785 updateList.numRects[updateList.currState],
00786 updateList.rects[updateList.currState]);
00787 #endif
00788 }
00789
00790 if (--updateList.dirty <= 0) {
00791
00792 updateList.dirty = 0;
00793
00794 updateList.renderAll = FALSE;
00795 }
00796
00797 updateList.numRects[updateList.currState] = 0;
00798
00799 updateList.nextState = ((updateList.currState = (updateList.currState + 1)
00800 % updateList.totalStates) + 1) % updateList.totalStates;
00801
00802 if (dInfo.display->flags & SDL_DOUBLEBUF) {
00803 SDL_Flip(dInfo.display);
00804 }
00805 }
00806
00807 void GenericSurfaceRenderer(SDL_Surface *dest, RenderItem *item) {
00808
00809
00810
00811
00812 SDL_LowerBlit(item->data, &(item->src), dest, &(item->physDest));
00813 }
00814
00815 void SolidFillRenderer(SDL_Surface *dest, RenderItem *item) {
00816
00817 SDL_FillRect(dest, &(item->physDest), *(Uint32*)item->data);
00818 }
00819
00823 Uint32 videoFlags = 0;
00824
00825 #define rounddivup(n, d) ((((n) % (d)) > 0) ? ((int)(n) / (d) + 1) : ((int)(n) / (d)))
00826
00827 Bool ResizeWindow(Uint16 w, Uint16 h) {
00828 if ((dInfo.display = SDL_SetVideoMode(w, h, 0, videoFlags)) == NULL) {
00829
00830 return FALSE;
00831 }
00832 dInfo.pw = w;
00833 dInfo.lw = rounddivup(dInfo.pw, dInfo.luw);
00834 dInfo.ph = h;
00835 dInfo.lh = rounddivup(dInfo.ph, dInfo.luh);
00836 ForceRenderAll();
00837 return TRUE;
00838 }
00839