1 /*
2 SCREENLIB: A framebuffer library based on the SDL library
3 Copyright (C) 1997 Sam Lantinga
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 5635-34 Springhouse Dr.
21 Pleasanton, CA 94588 (USA)
22 slouken@devolution.com
23 */
24
25 #include <stdio.h>
26
27 #include "SDL_FrameBuf.h"
28 #include "pixel.h"
29
30
31 #define LOWER_PREC(X) ((X)/16) /* Lower the precision of a value */
32 #define RAISE_PREC(X) ((X)/16) /* Raise the precision of a value */
33
34 #define MIN(A, B) ((A < B) ? A : B)
35 #define MAX(A, B) ((A > B) ? A : B)
36
37 #define ADJUSTX(X) \
38 { \
39 if ( X < 0 ) X = 0; \
40 else \
41 if ( X > screen->w ) X = screen->w; \
42 }
43 #define ADJUSTY(Y) \
44 { \
45 if ( Y < 0 ) Y = 0; \
46 else \
47 if ( Y > screen->h ) Y = screen->h; \
48 }
49
50 /* Constructors cannot fail. :-/ */
FrameBuf()51 FrameBuf:: FrameBuf()
52 {
53 /* Initialize various variables to null state */
54 screenfg = NULL;
55 screenbg = NULL;
56 blitQ = NULL;
57 dirtymap = NULL;
58 updatelist = NULL;
59 errstr = NULL;
60 faded = 0;
61 locked = 0;
62 images.next = NULL;
63 itail = &images;
64 }
65
PrintSurface(char * title,SDL_Surface * surface)66 static void PrintSurface(char *title, SDL_Surface *surface)
67 {
68 #ifdef FRAMEBUF_DEBUG
69 fprintf(stderr, "%s:\n", title);
70 fprintf(stderr, "\t%dx%d at %d bpp\n", surface->w, surface->h,
71 surface->format->BitsPerPixel);
72 fprintf(stderr, "\tLocated in %s memory\n",
73 (surface->flags & SDL_HWSURFACE) ? "video" : "system" );
74 if ( surface->flags & SDL_HWACCEL ) {
75 fprintf(stderr, "\t(hardware acceleration possible)\n");
76 }
77 if ( SDL_LockSurface(surface) == 0 ) {
78 fprintf(stderr, "\tPixels at 0x%x\n", surface->pixels);
79 SDL_UnlockSurface(surface);
80 }
81 fprintf(stderr, "\n");
82 #endif
83 return;
84 }
85
86 int
Init(int width,int height,Uint32 video_flags,SDL_Color * colors,SDL_Surface * icon)87 FrameBuf:: Init(int width, int height, Uint32 video_flags,
88 SDL_Color *colors, SDL_Surface *icon)
89 {
90 /* Set the icon, if any */
91 if ( icon ) {
92 int masklen;
93 Uint8 *mask;
94
95 masklen = ((icon->w+7)/8)*icon->h;
96 mask = new Uint8[masklen];
97 memset(mask, 0xFF, masklen);
98 SDL_WM_SetIcon(icon, mask);
99 delete[] mask;
100 }
101
102 /* Try for the 8-bit video mode that was requested, accept any depth */
103 //video_flags |= SDL_ANYFORMAT;
104 screenfg = SDL_SetVideoMode(width, height, 8, video_flags);
105 if ( screenfg == NULL ) {
106 SetError("Couldn't set %dx%d video mode: %s",
107 width, height, SDL_GetError());
108 return(-1);
109 }
110 FocusFG();
111 PrintSurface("Created foreground", screenfg);
112
113 /* Create the background */
114 screenbg = SDL_CreateRGBSurface(screen->flags, screen->w, screen->h,
115 screen->format->BitsPerPixel,
116 screen->format->Rmask,
117 screen->format->Gmask,
118 screen->format->Bmask, 0);
119 if ( screenbg == NULL ) {
120 SetError("Couldn't create background: %s", SDL_GetError());
121 return(-1);
122 }
123 PrintSurface("Created background", screenbg);
124
125 /* Create a dirty rectangle map of the screen */
126 dirtypitch = LOWER_PREC(width);
127 dirtymaplen = LOWER_PREC(height)*dirtypitch;
128 dirtymap = new SDL_Rect *[dirtymaplen];
129
130 /* Create the update list */
131 updatelist = new SDL_Rect[UPDATE_CHUNK];
132 ClearDirtyList();
133 updatemax = UPDATE_CHUNK;
134
135 /* Create the blit list */
136 blitQ = new BlitQ[QUEUE_CHUNK];
137 blitQlen = 0;
138 blitQmax = QUEUE_CHUNK;
139
140 /* Set the blit clipping rectangle */
141 clip.x = 0;
142 clip.y = 0;
143 clip.w = screen->w;
144 clip.h = screen->h;
145
146 /* Copy the image colormap and set a black background */
147 SetBackground(0, 0, 0);
148 if ( colors ) {
149 SetPalette(colors);
150 }
151
152 /* Figure out what putpixel routine to use */
153 switch (screen->format->BytesPerPixel) {
154 case 1:
155 PutPixel = PutPixel1;
156 break;
157 case 2:
158 PutPixel = PutPixel2;
159 break;
160 case 3:
161 PutPixel = PutPixel3;
162 break;
163 case 4:
164 PutPixel = PutPixel4;
165 break;
166 }
167 return(0);
168 }
169
~FrameBuf()170 FrameBuf:: ~FrameBuf()
171 {
172 image_list *ielem, *iold;
173
174 UNLOCK_IF_NEEDED();
175 for ( ielem = images.next; ielem; ) {
176 iold = ielem;
177 ielem = ielem->next;
178 SDL_FreeSurface(iold->image);
179 delete iold;
180 }
181 if ( screenbg )
182 SDL_FreeSurface(screenbg);
183 if ( blitQ )
184 delete[] blitQ;
185 if ( dirtymap )
186 delete[] dirtymap;
187 if ( updatelist )
188 delete[] updatelist;
189 }
190
191 /* Setup routines */
192 void
SetPalette(SDL_Color * colors)193 FrameBuf:: SetPalette(SDL_Color *colors)
194 {
195 int i;
196
197 if ( screenfg->format->palette ) {
198 SDL_SetColors(screenfg, colors, 0, 256);
199 SDL_SetColors(screenbg, screenfg->format->palette->colors,
200 0, screenfg->format->palette->ncolors);
201 }
202 for ( i=0; i<256; ++i ) {
203 image_map[i] = SDL_MapRGB(screenfg->format,
204 colors[i].r, colors[i].g, colors[i].b);
205 }
206 SetBackground(BGrgb[0], BGrgb[1], BGrgb[2]);
207 }
208 void
SetBackground(Uint8 R,Uint8 G,Uint8 B)209 FrameBuf:: SetBackground(Uint8 R, Uint8 G, Uint8 B)
210 {
211 BGrgb[0] = R;
212 BGrgb[1] = G;
213 BGrgb[2] = B;
214 BGcolor = SDL_MapRGB(screenfg->format, R, G, B);
215 FocusBG();
216 Clear();
217 FocusFG();
218 }
219 Uint32
MapRGB(Uint8 R,Uint8 G,Uint8 B)220 FrameBuf:: MapRGB(Uint8 R, Uint8 G, Uint8 B)
221 {
222 return(SDL_MapRGB(screenfg->format, R, G, B));
223 }
224 void
ClipBlit(SDL_Rect * cliprect)225 FrameBuf:: ClipBlit(SDL_Rect *cliprect)
226 {
227 clip = *cliprect;
228 }
229
230 /* Locking routines */
231 void
Lock(void)232 FrameBuf:: Lock(void)
233 {
234 /* Keep trying to lock the screen until we succeed */
235 if ( ! locked ) {
236 ++locked;
237 while ( SDL_LockSurface(screen) < 0 ) {
238 SDL_Delay(10);
239 }
240 screen_mem = (Uint8 *)screen->pixels;
241 }
242 }
243 void
Unlock(void)244 FrameBuf:: Unlock(void)
245 {
246 if ( locked ) {
247 SDL_UnlockSurface(screen);
248 --locked;
249 }
250 }
251 void
PerformBlits(void)252 FrameBuf:: PerformBlits(void)
253 {
254 if ( blitQlen > 0 ) {
255 /* Perform lazy unlocking */
256 UNLOCK_IF_NEEDED();
257
258 /* Blast and free the queued blits */
259 for ( int i=0; i<blitQlen; ++i ) {
260 SDL_LowerBlit(blitQ[i].src, &blitQ[i].srcrect,
261 screen, &blitQ[i].dstrect);
262 SDL_FreeSurface(blitQ[i].src);
263 }
264 blitQlen = 0;
265 }
266 }
267 void
Update(int auto_update)268 FrameBuf:: Update(int auto_update)
269 {
270 int i;
271
272 /* Blit and update the changed rectangles */
273 PerformBlits();
274 if ( (screen == screenbg) && auto_update ) {
275 for ( i=0; i<updatelen; ++i ) {
276 SDL_LowerBlit(screenbg, &updatelist[i],
277 screenfg, &updatelist[i]);
278 }
279 SDL_UpdateRects(screenfg, updatelen, updatelist);
280 } else {
281 SDL_UpdateRects(screen, updatelen, updatelist);
282 }
283 ClearDirtyList();
284 }
285
286 /* Drawing routines */
287 void
Clear(Sint16 x,Sint16 y,Uint16 w,Uint16 h,clipval do_clip)288 FrameBuf:: Clear(Sint16 x, Sint16 y, Uint16 w, Uint16 h, clipval do_clip)
289 {
290 /* If we're focused on the foreground, copy from background */
291 if ( screen == screenfg ) {
292 QueueBlit(x, y, screenbg, x, y, w, h, do_clip);
293 } else {
294 Uint8 screen_bpp;
295 Uint8 *screen_loc;
296
297 LOCK_IF_NEEDED();
298 screen_bpp = screen->format->BytesPerPixel;
299 screen_loc = screen_mem + y*screen->pitch + x*screen_bpp;
300 w *= screen_bpp;
301 while ( h-- ) {
302 /* Note that BGcolor is a 32 bit quantity while memset()
303 fills with an 8-bit quantity. This only matters if
304 the background is a different color than black on a
305 HiColor or TrueColor display.
306 */
307 memset(screen_loc, BGcolor, w);
308 screen_loc += screen->pitch;
309 }
310 }
311 }
312 void
DrawPoint(Sint16 x,Sint16 y,Uint32 color)313 FrameBuf:: DrawPoint(Sint16 x, Sint16 y, Uint32 color)
314 {
315 SDL_Rect dirty;
316
317 /* Adjust the bounds */
318 if ( x < 0 ) return;
319 if ( x > screen->w ) return;
320 if ( y < 0 ) return;
321 if ( y > screen->h ) return;
322
323 PerformBlits();
324 LOCK_IF_NEEDED();
325 PutPixel(screen_mem+y*screen->pitch+x*screen->format->BytesPerPixel,
326 screen, color);
327 dirty.x = x;
328 dirty.y = y;
329 dirty.w = 1;
330 dirty.h = 1;
331 AddDirtyRect(&dirty);
332 }
333 /* Simple, slow, line drawing algorithm. Improvement, anyone? :-) */
334 void
DrawLine(Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint32 color)335 FrameBuf:: DrawLine(Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color)
336 {
337 SDL_Rect dirty;
338 Sint16 x , y;
339 Sint16 lo, hi;
340 double slope, b;
341 Uint8 screen_bpp;
342 Uint8 *screen_loc;
343
344 /* Adjust the bounds */
345 ADJUSTX(x1); ADJUSTY(y1);
346 ADJUSTX(x2); ADJUSTY(y2);
347
348 PerformBlits();
349 LOCK_IF_NEEDED();
350 screen_bpp = screen->format->BytesPerPixel;
351 if ( y1 == y2 ) { /* Horizontal line */
352 if ( x1 < x2 ) {
353 lo = x1;
354 hi = x2;
355 } else {
356 lo = x2;
357 hi = x1;
358 }
359 screen_loc = screen_mem + y1*screen->pitch + lo*screen_bpp;
360 for ( x=lo; x<=hi; ++x ) {
361 PutPixel(screen_loc, screen, color);
362 screen_loc += screen_bpp;
363 }
364 dirty.x = lo;
365 dirty.y = y1;
366 dirty.w = (Uint16)(hi-lo+1);
367 dirty.h = 1;
368 AddDirtyRect(&dirty);
369 } else if ( x1 == x2 ) { /* Vertical line */
370 if ( y1 < y2 ) {
371 lo = y1;
372 hi = y2;
373 } else {
374 lo = y2;
375 hi = y1;
376 }
377 screen_loc = screen_mem + lo*screen->pitch + x1*screen_bpp;
378 for ( y=lo; y<=hi; ++y ) {
379 PutPixel(screen_loc, screen, color);
380 screen_loc += screen->pitch;
381 }
382 dirty.x = x1;
383 dirty.y = lo;
384 dirty.w = 1;
385 dirty.h = (Uint16)(hi-lo+1);
386 AddDirtyRect(&dirty);
387 } else {
388 /* Equation: y = mx + b */
389 slope = ((double)((int)(y2 - y1)) /
390 (double)((int)(x2 - x1)));
391 b = (double)(y1 - slope*(double)x1);
392 if ( ((slope < 0) ? slope > -1 : slope < 1) ) {
393 if ( x1 < x2 ) {
394 lo = x1;
395 hi = x2;
396 } else {
397 lo = x2;
398 hi = x1;
399 }
400 for ( x=lo; x<=hi; ++x ) {
401 y = (int)((slope*(double)x) + b);
402 screen_loc = screen_mem +
403 y*screen->pitch + x*screen_bpp;
404 PutPixel(screen_loc, screen, color);
405 }
406 } else {
407 if ( y1 < y2 ) {
408 lo = y1;
409 hi = y2;
410 } else {
411 lo = y2;
412 hi = y1;
413 }
414 for ( y=lo; y<=hi; ++y ) {
415 x = (int)(((double)y - b)/slope);
416 screen_loc = screen_mem +
417 y*screen->pitch + x*screen_bpp;
418 PutPixel(screen_loc, screen, color);
419 }
420 }
421 dirty.x = MIN(x1, x2);
422 dirty.y = MIN(y1, y2);
423 dirty.w = (Uint16)(MAX(x1, x2)-dirty.x+1);
424 dirty.h = (Uint16)(MAX(y1, y2)-dirty.y+1);
425 AddDirtyRect(&dirty);
426 }
427 }
428 void
DrawRect(Sint16 x,Sint16 y,Uint16 w,Uint16 h,Uint32 color)429 FrameBuf:: DrawRect(Sint16 x, Sint16 y, Uint16 w, Uint16 h, Uint32 color)
430 {
431 SDL_Rect dirty;
432 int i;
433 Uint8 screen_bpp;
434 Uint8 *screen_loc;
435
436 /* Adjust the bounds */
437 ADJUSTX(x); ADJUSTY(y);
438 if ( (x+w) > screen->w ) w = (Uint16)(screen->w-x);
439 if ( (y+h) > screen->h ) h = (Uint16)(screen->h-y);
440
441 PerformBlits();
442 LOCK_IF_NEEDED();
443 screen_bpp = screen->format->BytesPerPixel;
444
445 /* Horizontal lines */
446 screen_loc = screen_mem + y*screen->pitch + x*screen_bpp;
447 for ( i=0; i<w; ++i ) {
448 PutPixel(screen_loc, screen, color);
449 screen_loc += screen_bpp;
450 }
451 screen_loc = screen_mem + (y+h-1)*screen->pitch + x*screen_bpp;
452 for ( i=0; i<w; ++i ) {
453 PutPixel(screen_loc, screen, color);
454 screen_loc += screen_bpp;
455 }
456
457 /* Vertical lines */
458 screen_loc = screen_mem + y*screen->pitch + x*screen_bpp;
459 for ( i=0; i<h; ++i ) {
460 PutPixel(screen_loc, screen, color);
461 screen_loc += screen->pitch;
462 }
463 screen_loc = screen_mem + y*screen->pitch + (x+w-1)*screen_bpp;
464 for ( i=0; i<h; ++i ) {
465 PutPixel(screen_loc, screen, color);
466 screen_loc += screen->pitch;
467 }
468
469 /* Update rectangle */
470 dirty.x = x;
471 dirty.y = y;
472 dirty.w = w;
473 dirty.h = h;
474 AddDirtyRect(&dirty);
475 }
476 void
FillRect(Sint16 x,Sint16 y,Uint16 w,Uint16 h,Uint32 color)477 FrameBuf:: FillRect(Sint16 x, Sint16 y, Uint16 w, Uint16 h, Uint32 color)
478 {
479 SDL_Rect dirty;
480 Uint16 i, skip;
481 Uint8 screen_bpp;
482 Uint8 *screen_loc;
483
484 /* Adjust the bounds */
485 ADJUSTX(x); ADJUSTY(y);
486 if ( (x+w) > screen->w ) w = (screen->w-x);
487 if ( (y+h) > screen->h ) h = (screen->h-y);
488
489 /* Set the dirty rectangle */
490 dirty.x = x;
491 dirty.y = y;
492 dirty.w = w;
493 dirty.h = h;
494
495 /* Semi-efficient, for now. :) */
496 LOCK_IF_NEEDED();
497 screen_bpp = screen->format->BytesPerPixel;
498 screen_loc = screen_mem + y*screen->pitch + x*screen_bpp;
499 skip = screen->pitch - (w*screen_bpp);
500 while ( h-- ) {
501 for ( i=w; i!=0; --i ) {
502 PutPixel(screen_loc, screen, color);
503 screen_loc += screen_bpp;
504 }
505 screen_loc += skip;
506 }
507 AddDirtyRect(&dirty);
508 }
509
memswap(Uint8 * dst,Uint8 * src,Uint8 len)510 static inline void memswap(Uint8 *dst, Uint8 *src, Uint8 len)
511 {
512 #ifdef SWAP_XOR
513 /* Swap two buffers using ye ol' xor trick :-) */
514 while ( len-- ) {
515 *dst ^= *src;
516 *src ^= *dst;
517 *dst ^= *src;
518 ++src; ++dst;
519 }
520 #else
521 /* Swap two buffers using a temporary variable */
522 register Uint8 tmp;
523
524 while ( len-- ) {
525 tmp = *dst;
526 *dst = *src;
527 *src = tmp;
528 ++src; ++dst;
529 }
530 #endif
531 }
532
533 void
Fade(void)534 FrameBuf:: Fade(void)
535 {
536 const int max = 32;
537 Uint16 ramp[256];
538
539 for ( int j = 1; j <= max; j++ ) {
540 int v = faded ? j : max - j + 1;
541 for ( int i = 0; i < 256; i++ ) {
542 ramp[i] = (i * v / max) << 8;
543 }
544 SDL_SetGammaRamp(ramp, ramp, ramp);
545 SDL_Delay(10);
546 }
547 faded = !faded;
548
549 if ( faded ) {
550 for ( int i = 0; i < 256; i++ ) {
551 ramp[i] = 0;
552 }
553 SDL_SetGammaRamp(ramp, ramp, ramp);
554 }
555 }
556
557 SDL_Surface *
GrabArea(Uint16 x,Uint16 y,Uint16 w,Uint16 h)558 FrameBuf:: GrabArea(Uint16 x, Uint16 y, Uint16 w, Uint16 h)
559 {
560 SDL_Surface *area;
561
562 /* Clip the area we are grabbing */
563 if ( x >= screen->w ) {
564 return(NULL);
565 }
566 if ( (x+w) > screen->w ) {
567 w -= (screen->w-(x+w));
568 }
569 if ( y >= screen->h ) {
570 return(NULL);
571 }
572 if ( (y+h) > screen->h ) {
573 h -= (screen->h-(y+h));
574 }
575
576 /* Allocate an area of the same pixel format */
577 area = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
578 screen->format->BitsPerPixel,
579 screen->format->Rmask,
580 screen->format->Gmask,
581 screen->format->Bmask, 0);
582 if ( area ) {
583 Uint8 *area_mem;
584 Uint8 *scrn_mem;
585 int cursor_shown;
586
587 if ( area->format->palette ) {
588 memcpy(area->format->palette->colors,
589 screen->format->palette->colors,
590 screen->format->palette->ncolors
591 *sizeof(SDL_Color));
592 }
593 cursor_shown = SDL_ShowCursor(0);
594 Lock();
595 area_mem = (Uint8 *)area->pixels;
596 scrn_mem = screen_mem + y*screen->pitch +
597 x*screen->format->BytesPerPixel;
598 w *= screen->format->BytesPerPixel;
599 while ( h-- ) {
600 memcpy(area_mem, scrn_mem, w);
601 area_mem += area->pitch;
602 scrn_mem += screen->pitch;
603 }
604 Unlock();
605 SDL_ShowCursor(cursor_shown);
606 }
607
608 /* Add the area to the list of images */
609 itail->next = new image_list;
610 itail = itail->next;
611 itail->image = area;
612 itail->next = NULL;
613 return(area);
614 }
615
616 int
ScreenDump(char * prefix,Uint16 x,Uint16 y,Uint16 w,Uint16 h)617 FrameBuf:: ScreenDump(char *prefix, Uint16 x, Uint16 y, Uint16 w, Uint16 h)
618 {
619 SDL_Surface *dump;
620 int retval;
621
622 /* Get a suitable new filename */
623 dump = GrabArea(x, y, w, h);
624 if ( dump ) {
625 int which, found;
626 char file[1024];
627 FILE *fp;
628
629 found = 0;
630 for ( which=0; !found; ++which ) {
631 snprintf(file, sizeof(file), "%s%d.bmp", prefix, which);
632 if ( ((fp=fopen(file, "r")) == NULL) &&
633 ((fp=fopen(file, "w")) != NULL) ) {
634 found = 1;
635 }
636 if ( fp != NULL ) {
637 fclose(fp);
638 }
639 }
640 retval = SDL_SaveBMP(dump, file);
641 if ( retval < 0 ) {
642 SetError("%s", SDL_GetError());
643 }
644 FreeImage(dump);
645 } else {
646 retval = -1;
647 }
648 return(retval);
649 }
650
651 SDL_Surface *
LoadImage(Uint16 w,Uint16 h,Uint8 * pixels,Uint8 * mask)652 FrameBuf:: LoadImage(Uint16 w, Uint16 h, Uint8 *pixels, Uint8 *mask)
653 {
654 SDL_Surface *artwork;
655 int i, j, pad;
656 Uint8 *art_mem, *pix_mem;
657
658 /* Assume 8-bit artwork using the current palette */
659 artwork = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
660 screenfg->format->BitsPerPixel,
661 screenfg->format->Rmask,
662 screenfg->format->Gmask,
663 screenfg->format->Bmask, 0);
664 if ( artwork == NULL ) {
665 SetError("Couldn't create artwork: %s", SDL_GetError());
666 return(NULL);
667 }
668
669 /* Set the palette and copy pixels, checking for colorkey */
670 if ( artwork->format->palette != NULL ) {
671 memcpy(artwork->format->palette->colors,
672 screenfg->format->palette->colors,
673 screenfg->format->palette->ncolors*sizeof(SDL_Color));
674 }
675 pad = ((w%4) ? (4-(w%4)) : 0);
676 if ( mask ) {
677 int used[256];
678 Uint8 colorkey, m;
679
680 /* Look for an unused palette entry */
681 memset(used, 0, 256);
682 pix_mem = pixels;
683 for ( i=(w*h); i!=0; --i ) {
684 ++used[*pix_mem];
685 ++pix_mem;
686 }
687 for ( i=0; used[i] && i<256; ++i ) {
688 /* Keep looking */;
689 }
690 colorkey = image_map[(Uint8)i];
691
692 /* Copy over the pixels */
693 pix_mem = pixels;
694 for ( i=0; i<h; ++i ) {
695 art_mem = (Uint8 *)artwork->pixels + i*artwork->pitch;
696 for ( j=0; j<w; ++j ) {
697 if ( (j%8) == 0 ) {
698 m = *mask++;
699 }
700 if ( m & 0x80 ) {
701 PutPixel(art_mem, screenfg,
702 image_map[*pix_mem]);
703 } else {
704 PutPixel(art_mem, screenfg, colorkey);
705 }
706 m <<= 1;
707 art_mem += artwork->format->BytesPerPixel;
708 pix_mem += 1;
709 }
710 pix_mem += pad;
711 }
712 SDL_SetColorKey(artwork,SDL_SRCCOLORKEY|SDL_RLEACCEL,colorkey);
713 } else {
714 /* Copy over the pixels */
715 pix_mem = pixels;
716 for ( i=0; i<h; ++i ) {
717 art_mem = (Uint8 *)artwork->pixels + i*artwork->pitch;
718 for ( j=0; j<w; ++j ) {
719 PutPixel(art_mem, screenfg,
720 image_map[*pix_mem]);
721 art_mem += artwork->format->BytesPerPixel;
722 pix_mem += 1;
723 }
724 pix_mem += pad;
725 }
726 }
727 /* Add the image to the list of images */
728 itail->next = new image_list;
729 itail = itail->next;
730 itail->image = artwork;
731 itail->next = NULL;
732 return(artwork);
733 }
734 void
FreeImage(SDL_Surface * image)735 FrameBuf:: FreeImage(SDL_Surface *image)
736 {
737 image_list *ielem, *iold;
738
739 /* Remove the image from the list of images */
740 for ( ielem=&images; ielem->next; ) {
741 iold = ielem->next;
742 if ( iold->image == image ) {
743 if ( iold == itail ) {
744 itail = ielem;
745 }
746 ielem->next = iold->next;
747 SDL_FreeSurface(iold->image);
748 delete iold;
749 return;
750 } else {
751 ielem = ielem->next;
752 }
753 }
754 fprintf(stderr, "Warning: image to be freed not in list\n");
755 }
756
757 void
QueueBlit(int dstx,int dsty,SDL_Surface * src,int srcx,int srcy,int w,int h,clipval do_clip)758 FrameBuf:: QueueBlit(int dstx, int dsty, SDL_Surface *src,
759 int srcx, int srcy, int w, int h, clipval do_clip)
760 {
761 int diff;
762
763 /* Perform clipping */
764 if ( do_clip == DOCLIP ) {
765 diff = (int)clip.x - dstx;
766 if ( diff > 0 ) {
767 w -= diff;
768 if ( w <= 0 )
769 return;
770 srcx += diff;
771 dstx = clip.x;
772 }
773 diff = (int)clip.y - dsty;
774 if ( diff > 0 ) {
775 h -= diff;
776 if ( h <= 0 )
777 return;
778 srcy += diff;
779 dsty = clip.y;
780 }
781 diff = (int)(dstx+w) - (clip.x+clip.w);
782 if ( diff > 0 ) {
783 w -= diff;
784 if ( w <= 0 )
785 return;
786 }
787 diff = (int)(dsty+h) - (clip.y+clip.h);
788 if ( diff > 0 ) {
789 h -= diff;
790 if ( h <= 0 )
791 return;
792 }
793 }
794
795 /* Lengthen the queue if necessary */
796 if ( blitQlen == blitQmax ) {
797 BlitQ *newq;
798
799 blitQmax += QUEUE_CHUNK;
800 newq = new BlitQ[blitQmax];
801 memcpy(newq, blitQ, blitQlen*sizeof(BlitQ));
802 delete[] blitQ;
803 blitQ = newq;
804 }
805
806 /* Add the blit to the queue */
807 ++src->refcount;
808 blitQ[blitQlen].src = src;
809 blitQ[blitQlen].srcrect.x = srcx;
810 blitQ[blitQlen].srcrect.y = srcy;
811 blitQ[blitQlen].srcrect.w = w;
812 blitQ[blitQlen].srcrect.h = h;
813 blitQ[blitQlen].dstrect.x = dstx;
814 blitQ[blitQlen].dstrect.y = dsty;
815 blitQ[blitQlen].dstrect.w = w;
816 blitQ[blitQlen].dstrect.h = h;
817 AddDirtyRect(&blitQ[blitQlen].dstrect);
818 ++blitQlen;
819 }
820
821 /* Maintenance routines */
822 /* Add a rectangle to the update list
823 This is a little bit smart -- if the center nearly overlaps the center
824 of another rectangle update, expand the existing rectangle to include
825 the new area, instead adding another update rectangle.
826 */
827 void
AddDirtyRect(SDL_Rect * rect)828 FrameBuf:: AddDirtyRect(SDL_Rect *rect)
829 {
830 Uint16 mapoffset;
831 SDL_Rect *newrect;
832
833 /* The dirty map offset is the center of the rectangle */
834 mapoffset = LOWER_PREC(rect->y+(rect->h/2)) * dirtypitch +
835 LOWER_PREC(rect->x+(rect->w/2));
836
837 if ( dirtymap[mapoffset] == NULL ) {
838 /* New dirty rectangle */
839 if ( updatelen == updatemax ) {
840 /* Expand the updatelist */
841 SDL_Rect *newlist;
842 int i;
843
844 updatemax += UPDATE_CHUNK;
845 newlist = new SDL_Rect[updatemax+UPDATE_CHUNK];
846 memcpy(newlist,updatelist,updatelen*sizeof(SDL_Rect));
847 /* Update the dirty rectangle map with the new list */
848 for ( i=0; i<dirtymaplen; ++i ) {
849 if ( dirtymap[i] != NULL ) {
850 dirtymap[i] = newlist
851 + (dirtymap[i]-updatelist);
852 }
853 }
854 delete[] updatelist;
855 updatelist = newlist;
856 }
857 newrect = &updatelist[updatelen];
858 ++updatelen;
859 memcpy(newrect, rect, sizeof(*newrect));
860 dirtymap[mapoffset] = newrect;
861 } else {
862 Sint16 x1, y1, x2, y2;
863
864 /* Overlapping dirty rectangle -- expand it */
865 newrect = dirtymap[mapoffset];
866 x1 = MIN(rect->x, newrect->x);
867 y1 = MIN(rect->y, newrect->y);
868 x2 = MAX(rect->x+rect->w, newrect->x+newrect->w);
869 y2 = MAX(rect->y+rect->h, newrect->y+newrect->h);
870 newrect->x = x1;
871 newrect->y = y1;
872 newrect->w = (Uint16)(x2 - x1);
873 newrect->h = (Uint16)(y2 - y1);
874 }
875 }
876