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