1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16 
17 #include "port.h"
18 #include <string.h>
19 		// for memcpy()
20 
21 #include SDL_INCLUDE(SDL.h)
22 #include "sdl_common.h"
23 #include "libs/graphics/gfx_common.h"
24 #include "libs/graphics/tfb_draw.h"
25 #include "libs/graphics/cmap.h"
26 #include "libs/log.h"
27 #include "libs/memlib.h"
28 #include "primitives.h"
29 #include "palette.h"
30 #include "sdluio.h"
31 #include "rotozoom.h"
32 #include "options.h"
33 #include "types.h"
34 
35 typedef SDL_Surface *NativeCanvas;
36 
37 // BYTE x BYTE weight (mult >> 8) table
38 static Uint8 btable[256][256];
39 
40 void
TFB_DrawCanvas_Initialize(void)41 TFB_DrawCanvas_Initialize (void)
42 {
43 	int i, j;
44 	for (i = 0; i < 256; ++i)
45 		for (j = 0; j < 256; ++j)
46 			btable[j][i] = (j * i + 0x80) >> 8;
47 				// need error correction here
48 }
49 
50 const char *
TFB_DrawCanvas_GetError(void)51 TFB_DrawCanvas_GetError (void)
52 {
53 	const char *err = SDL_GetError ();
54 	// TODO: Should we call SDL_ClearError() here so that it is not
55 	//   returned again later?
56 	return err;
57 }
58 
59 static void
checkPrimitiveMode(SDL_Surface * surf,Color * color,DrawMode * mode)60 checkPrimitiveMode (SDL_Surface *surf, Color *color, DrawMode *mode)
61 {
62 	const SDL_PixelFormat *fmt = surf->format;
63 	// Special case: We support DRAW_ALPHA mode to non-alpha surfaces
64 	// for primitives via Color.a
65 	if (mode->kind == DRAW_REPLACE && fmt->Amask == 0 && color->a != 0xff)
66 	{
67 		mode->kind = DRAW_ALPHA;
68 		mode->factor = color->a;
69 		color->a = 0xff;
70 	}
71 }
72 
73 void
TFB_DrawCanvas_Line(int x1,int y1,int x2,int y2,Color color,DrawMode mode,TFB_Canvas target)74 TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, Color color,
75 		DrawMode mode, TFB_Canvas target)
76 {
77 	SDL_Surface *dst = target;
78 	SDL_PixelFormat *fmt = dst->format;
79 	Uint32 sdlColor;
80 	RenderPixelFn plotFn;
81 
82 	checkPrimitiveMode (dst, &color, &mode);
83 	sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a);
84 
85 	plotFn = renderpixel_for (target, mode.kind);
86 	if (!plotFn)
87 	{
88 		log_add (log_Warning, "ERROR: TFB_DrawCanvas_Line "
89 				"unsupported draw mode (%d)", (int)mode.kind);
90 		return;
91 	}
92 
93 	SDL_LockSurface (dst);
94 	line_prim (x1, y1, x2, y2, sdlColor, plotFn, mode.factor, dst);
95 	SDL_UnlockSurface (dst);
96 }
97 
98 void
TFB_DrawCanvas_Rect(RECT * rect,Color color,DrawMode mode,TFB_Canvas target)99 TFB_DrawCanvas_Rect (RECT *rect, Color color, DrawMode mode, TFB_Canvas target)
100 {
101 	SDL_Surface *dst = target;
102 	SDL_PixelFormat *fmt = dst->format;
103 	Uint32 sdlColor;
104 	SDL_Rect sr;
105 	sr.x = rect->corner.x;
106 	sr.y = rect->corner.y;
107 	sr.w = rect->extent.width;
108 	sr.h = rect->extent.height;
109 
110 	checkPrimitiveMode (dst, &color, &mode);
111 	sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a);
112 
113 	if (mode.kind == DRAW_REPLACE)
114 	{	// Standard SDL fillrect rendering
115 		Uint32 colorkey;
116 		if (fmt->Amask && (TFB_GetColorKey (dst, &colorkey) == 0))
117 		{	// special case -- alpha surface with colorkey
118 			// colorkey rects are transparent
119 			if ((sdlColor & ~fmt->Amask) == (colorkey & ~fmt->Amask))
120 				sdlColor &= ~fmt->Amask; // make transparent
121 		}
122 		SDL_FillRect (dst, &sr, sdlColor);
123 	}
124 	else
125 	{	// Custom fillrect rendering
126 		RenderPixelFn plotFn = renderpixel_for (target, mode.kind);
127 		if (!plotFn)
128 		{
129 			log_add (log_Warning, "ERROR: TFB_DrawCanvas_Rect "
130 					"unsupported draw mode (%d)", (int)mode.kind);
131 			return;
132 		}
133 
134 		SDL_LockSurface (dst);
135 		fillrect_prim (sr, sdlColor, plotFn, mode.factor, dst);
136 		SDL_UnlockSurface (dst);
137 	}
138 }
139 
140 static void
TFB_DrawCanvas_Blit(SDL_Surface * src,SDL_Rect * src_r,SDL_Surface * dst,SDL_Rect * dst_r,DrawMode mode)141 TFB_DrawCanvas_Blit (SDL_Surface *src, SDL_Rect *src_r,
142 		SDL_Surface *dst, SDL_Rect *dst_r, DrawMode mode)
143 {
144 	SDL_PixelFormat *srcfmt = src->format;
145 
146 	if (mode.kind == DRAW_REPLACE)
147 	{	// Standard SDL simple blit
148 		SDL_BlitSurface (src, src_r, dst, dst_r);
149 	}
150 	else if (mode.kind == DRAW_ALPHA && srcfmt->Amask == 0)
151 	{	// Standard SDL surface-alpha blit
152 		// Note that surface alpha and per-pixel alpha cannot work
153 		// at the same time, which is why the Amask test
154 		int hasAlpha = TFB_HasSurfaceAlphaMod (src);
155 		assert (!hasAlpha);
156 		// Set surface alpha temporarily
157 		TFB_SetSurfaceAlphaMod (src, mode.factor);
158 		SDL_BlitSurface (src, src_r, dst, dst_r);
159 		TFB_DisableSurfaceAlphaMod (src);
160 	}
161 	else
162 	{	// Custom blit
163 		SDL_Rect loc_src_r, loc_dst_r;
164 		RenderPixelFn plotFn = renderpixel_for (dst, mode.kind);
165 		if (!plotFn)
166 		{
167 			log_add (log_Warning, "ERROR: TFB_DrawCanvas_Blit "
168 					"unsupported draw mode (%d)", (int)mode.kind);
169 			return;
170 		}
171 
172 		if (!src_r)
173 		{	// blit whole image; generate rect
174 			loc_src_r.x = 0;
175 			loc_src_r.y = 0;
176 			loc_src_r.w = src->w;
177 			loc_src_r.h = src->h;
178 			src_r = &loc_src_r;
179 		}
180 
181 		if (!dst_r)
182 		{	// blit to 0,0; generate rect
183 			loc_dst_r.x = 0;
184 			loc_dst_r.y = 0;
185 			loc_dst_r.w = dst->w;
186 			loc_dst_r.h = dst->h;
187 			dst_r = &loc_dst_r;
188 		}
189 
190 		SDL_LockSurface (dst);
191 		blt_prim (src, *src_r, plotFn, mode.factor, dst, *dst_r);
192 		SDL_UnlockSurface (dst);
193 	}
194 }
195 
196 // XXX: If a colormap is passed in, it has to have been acquired via
197 // TFB_GetColorMap(). We release the colormap at the end.
198 void
TFB_DrawCanvas_Image(TFB_Image * img,int x,int y,int scale,int scaleMode,TFB_ColorMap * cmap,DrawMode mode,TFB_Canvas target)199 TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale,
200 		int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Canvas target)
201 {
202 	SDL_Rect srcRect, targetRect, *pSrcRect;
203 	SDL_Surface *surf;
204 	SDL_Palette *NormalPal;
205 
206 	if (img == 0)
207 	{
208 		log_add (log_Warning,
209 				"ERROR: TFB_DrawCanvas_Image passed null image ptr");
210 		return;
211 	}
212 
213 	LockMutex (img->mutex);
214 
215 	NormalPal = ((SDL_Surface *)img->NormalImg)->format->palette;
216 	// only set the new palette if it changed
217 	if (NormalPal && cmap && img->colormap_version != cmap->version)
218 		TFB_SetColors (img->NormalImg, cmap->palette->colors, 0, 256);
219 
220 	if (scale != 0 && scale != GSCALE_IDENTITY)
221 	{
222 		if (scaleMode == TFB_SCALE_TRILINEAR && img->MipmapImg)
223 		{
224 			// only set the new palette if it changed
225 			if (TFB_DrawCanvas_IsPaletted (img->MipmapImg)
226 					&& cmap && img->colormap_version != cmap->version)
227 				TFB_SetColors (img->MipmapImg, cmap->palette->colors, 0, 256);
228 		}
229 		else if (scaleMode == TFB_SCALE_TRILINEAR && !img->MipmapImg)
230 		{	// Do bilinear scaling instead when mipmap is unavailable
231 			scaleMode = TFB_SCALE_BILINEAR;
232 		}
233 
234 		TFB_DrawImage_FixScaling (img, scale, scaleMode);
235 		surf = img->ScaledImg;
236 		if (TFB_DrawCanvas_IsPaletted (surf))
237 		{
238 			// We may only get a paletted scaled image if the source is
239 			// paletted. Currently, all scaling targets are truecolor.
240 			assert (NormalPal && NormalPal->colors);
241 			TFB_SetColors (surf, NormalPal->colors, 0, NormalPal->ncolors);
242 		}
243 
244 		srcRect.x = 0;
245 		srcRect.y = 0;
246 		srcRect.w = img->extent.width;
247 		srcRect.h = img->extent.height;
248 		pSrcRect = &srcRect;
249 
250 		targetRect.x = x - img->last_scale_hs.x;
251 		targetRect.y = y - img->last_scale_hs.y;
252 	}
253 	else
254 	{
255 		surf = img->NormalImg;
256 		pSrcRect = NULL;
257 
258 		targetRect.x = x - img->NormalHs.x;
259 		targetRect.y = y - img->NormalHs.y;
260 	}
261 
262 	if (cmap)
263 	{
264 		img->colormap_version = cmap->version;
265 		// TODO: Technically, this is not a proper place to release a
266 		//   colormap. As it stands now, the colormap must have been
267 		//   addrefed when passed to us.
268 		TFB_ReturnColorMap (cmap);
269 	}
270 
271 	TFB_DrawCanvas_Blit (surf, pSrcRect, target, &targetRect, mode);
272 	UnlockMutex (img->mutex);
273 }
274 
275 // Assumes the source and destination surfaces are in the same format
276 static void
TFB_DrawCanvas_Fill(SDL_Surface * src,Uint32 fillcolor,SDL_Surface * dst)277 TFB_DrawCanvas_Fill (SDL_Surface *src, Uint32 fillcolor, SDL_Surface *dst)
278 {
279 	const SDL_PixelFormat *srcfmt = src->format;
280 	SDL_PixelFormat *dstfmt = dst->format;
281 	const int width = src->w;
282 	const int height = src->h;
283 	const int bpp = dstfmt->BytesPerPixel;
284 	const int sp = src->pitch, dp = dst->pitch;
285 	const int slen = sp / bpp, dlen = dp / bpp;
286 	const int dsrc = slen - width, ddst = dlen - width;
287 	Uint32 *src_p;
288 	Uint32 *dst_p;
289 	int x, y;
290 	Uint32 srckey = 0, dstkey = 0; // 0 means alpha=0 too
291 	Uint32 amask = srcfmt->Amask;
292 	int alpha = (fillcolor & amask) >> srcfmt->Ashift;
293 
294 	if (srcfmt->BytesPerPixel != 4 || dstfmt->BytesPerPixel != 4)
295 	{
296 		log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported surface "
297 				"formats: %d bytes/pixel source, %d bytes/pixel destination",
298 				(int)srcfmt->BytesPerPixel, (int)dstfmt->BytesPerPixel);
299 		return;
300 	}
301 
302 	// Strip the alpha channel from fillcolor because we process the
303 	// alpha separately
304 	fillcolor &= ~amask;
305 
306 	SDL_LockSurface(src);
307 	SDL_LockSurface(dst);
308 
309 	src_p = (Uint32 *)src->pixels;
310 	dst_p = (Uint32 *)dst->pixels;
311 
312 	if (dstkey == fillcolor)
313 	{	// color collision, must switch colorkey
314 		// new colorkey is grey (1/2,1/2,1/2)
315 		dstkey = SDL_MapRGBA (dstfmt, 127, 127, 127, 0);
316 	}
317 
318 	if (srcfmt->Amask)
319 	{	// alpha-based fill
320 		for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc)
321 		{
322 			for (x = 0; x < width; ++x, ++src_p, ++dst_p)
323 			{
324 				Uint32 p = *src_p & amask;
325 
326 				if (p == 0)
327 				{	// fully transparent pixel
328 					*dst_p = dstkey;
329 				}
330 				else if (alpha == 0xff)
331 				{	// not for DRAW_ALPHA; use alpha chan directly
332 					*dst_p = p | fillcolor;
333 				}
334 				else
335 				{	// for DRAW_ALPHA; modulate the alpha channel
336 					p >>= srcfmt->Ashift;
337 					p = (p * alpha) >> 8;
338 					p <<= srcfmt->Ashift;
339 					*dst_p = p | fillcolor;
340 				}
341 			}
342 		}
343 	}
344 	else if (TFB_GetColorKey (src, &srckey) == 0)
345 	{	// colorkey-based fill
346 
347 		for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc)
348 		{
349 			for (x = 0; x < width; ++x, ++src_p, ++dst_p)
350 			{
351 				Uint32 p = *src_p;
352 
353 				*dst_p = (p == srckey) ? dstkey : fillcolor;
354 			}
355 		}
356 	}
357 	else
358 	{
359 		log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported source"
360 				"surface format\n");
361 	}
362 
363 	SDL_UnlockSurface(dst);
364 	SDL_UnlockSurface(src);
365 
366 	// save the colorkey (dynamic image -- not using RLE coding here)
367 	TFB_SetColorKey (dst, dstkey, 0);
368 			// if the filled surface is RGBA, colorkey will only be used
369 			// when SDL_SRCALPHA flag is cleared. this allows us to blit
370 			// the surface in different ways to diff targets
371 }
372 
373 void
TFB_DrawCanvas_FilledImage(TFB_Image * img,int x,int y,int scale,int scaleMode,Color color,DrawMode mode,TFB_Canvas target)374 TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale,
375 		int scaleMode, Color color, DrawMode mode, TFB_Canvas target)
376 {
377 	SDL_Surface *dst = target;
378 	SDL_Rect srcRect, targetRect, *pSrcRect;
379 	SDL_Surface *surf;
380 	SDL_Palette *palette;
381 	int i;
382 	bool force_fill = false;
383 
384 	if (img == 0)
385 	{
386 		log_add (log_Warning,
387 				"ERROR: TFB_DrawCanvas_FilledImage passed null image ptr");
388 		return;
389 	}
390 
391 	checkPrimitiveMode (dst, &color, &mode);
392 
393 	LockMutex (img->mutex);
394 
395 	if (scale != 0 && scale != GSCALE_IDENTITY)
396 	{
397 		if (scaleMode == TFB_SCALE_TRILINEAR)
398 			scaleMode = TFB_SCALE_BILINEAR;
399 					// no point in trilinear for filled images
400 
401 		if (scale != img->last_scale || scaleMode != img->last_scale_type)
402 			force_fill = true;
403 
404 		TFB_DrawImage_FixScaling (img, scale, scaleMode);
405 		surf = img->ScaledImg;
406 		srcRect.x = 0;
407 		srcRect.y = 0;
408 		srcRect.w = img->extent.width;
409 		srcRect.h = img->extent.height;
410 		pSrcRect = &srcRect;
411 
412 		targetRect.x = x - img->last_scale_hs.x;
413 		targetRect.y = y - img->last_scale_hs.y;
414 	}
415 	else
416 	{
417 		if (img->last_scale != 0)
418 		{
419 			// Make sure we remember that the last fill was from
420 			// an unscaled image
421 			force_fill = true;
422 			img->last_scale = 0;
423 		}
424 
425 		surf = img->NormalImg;
426 		pSrcRect = NULL;
427 
428 		targetRect.x = x - img->NormalHs.x;
429 		targetRect.y = y - img->NormalHs.y;
430 	}
431 
432 	palette = surf->format->palette;
433 	if (palette)
434 	{	// set palette for fill-stamp
435 		// Calling TFB_SetColors() results in an expensive src -> dst
436 		// color-mapping operation for an SDL blit, following the call.
437 		// We want to avoid that as much as possible.
438 
439 		// TODO: generate a 32bpp filled image?
440 
441 		SDL_Color colors[256];
442 
443 		colors[0] = ColorToNative (color);
444 		for (i = 1; i < palette->ncolors; i++)
445 			colors[i] = colors[0];
446 
447 		TFB_SetColors (surf, colors, 0, palette->ncolors);
448 		// reflect the change in *actual* image palette
449 		img->colormap_version--;
450 	}
451 	else
452 	{	// fill the non-transparent parts of the image with fillcolor
453 		SDL_Surface *newfill = img->FilledImg;
454 		SDL_PixelFormat *fillfmt;
455 
456 		if (newfill && (newfill->w < surf->w || newfill->h < surf->h))
457 		{
458 			TFB_DrawCanvas_Delete (newfill);
459 			newfill = NULL;
460 		}
461 
462 		// prepare the filled image
463 		if (!newfill)
464 		{
465 			newfill = SDL_CreateRGBSurface (SDL_SWSURFACE,
466 						surf->w, surf->h,
467 						surf->format->BitsPerPixel,
468 						surf->format->Rmask,
469 						surf->format->Gmask,
470 						surf->format->Bmask,
471 						surf->format->Amask);
472 			force_fill = true;
473 		}
474 		fillfmt = newfill->format;
475 
476 		if (force_fill || !sameColor (img->last_fill, color))
477 		{	// image or fillcolor changed - regenerate
478 			Uint32 fillColor;
479 
480 			if (mode.kind == DRAW_ALPHA && fillfmt->Amask)
481 			{	// Per-pixel alpha and surface alpha will not work together
482 				// We have to handle DRAW_ALPHA differently by modulating
483 				// the surface alpha channel ourselves.
484 				color.a = mode.factor;
485 				mode.kind = DRAW_REPLACE;
486 			}
487 			else
488 			{	// Make sure we do not modulate the alpha channel
489 				color.a = 0xff;
490 			}
491 			fillColor = SDL_MapRGBA (newfill->format, color.r, color.g,
492 					color.b, color.a);
493 			TFB_DrawCanvas_Fill (surf, fillColor, newfill);
494 			// cache filled image if possible
495 			img->last_fill = color;
496 		}
497 
498 		img->FilledImg = newfill;
499 		surf = newfill;
500 	}
501 
502 	TFB_DrawCanvas_Blit (surf, pSrcRect, dst, &targetRect, mode);
503 	UnlockMutex (img->mutex);
504 }
505 
506 void
TFB_DrawCanvas_FontChar(TFB_Char * fontChar,TFB_Image * backing,int x,int y,DrawMode mode,TFB_Canvas target)507 TFB_DrawCanvas_FontChar (TFB_Char *fontChar, TFB_Image *backing,
508 		int x, int y, DrawMode mode, TFB_Canvas target)
509 {
510 	SDL_Surface *dst = target;
511 	SDL_Rect srcRect, targetRect;
512 	SDL_Surface *surf;
513 	int w, h;
514 
515 	if (fontChar == 0)
516 	{
517 		log_add (log_Warning, "ERROR: "
518 				"TFB_DrawCanvas_FontChar passed null char ptr");
519 		return;
520 	}
521 	if (backing == 0)
522 	{
523 		log_add (log_Warning, "ERROR: "
524 				"TFB_DrawCanvas_FontChar passed null backing ptr");
525 		return;
526 	}
527 
528 	w = fontChar->extent.width;
529 	h = fontChar->extent.height;
530 
531 	LockMutex (backing->mutex);
532 
533 	surf = backing->NormalImg;
534 	if (surf->format->BytesPerPixel != 4
535 			|| surf->w < w || surf->h < h)
536 	{
537 		log_add (log_Warning, "ERROR: "
538 				"TFB_DrawCanvas_FontChar bad backing surface: %dx%dx%d; "
539 				"char: %dx%d",
540 				surf->w, surf->h, (int)surf->format->BytesPerPixel, w, h);
541 		UnlockMutex (backing->mutex);
542 		return;
543 	}
544 
545 	srcRect.x = 0;
546 	srcRect.y = 0;
547 	srcRect.w = fontChar->extent.width;
548 	srcRect.h = fontChar->extent.height;
549 
550 	targetRect.x = x - fontChar->HotSpot.x;
551 	targetRect.y = y - fontChar->HotSpot.y;
552 
553 	// transfer the alpha channel to the backing surface
554 	SDL_LockSurface (surf);
555 	{
556 		int x, y;
557 		const int sskip = fontChar->pitch - w;
558 		const int dskip = (surf->pitch / 4) - w;
559 		const Uint32 dmask = ~surf->format->Amask;
560 		const int ashift = surf->format->Ashift;
561 		Uint8 *src_p = fontChar->data;
562 		Uint32 *dst_p = (Uint32 *)surf->pixels;
563 
564 		if (mode.kind == DRAW_ALPHA)
565 		{	// Per-pixel alpha and surface alpha will not work together
566 			// We have to handle DRAW_ALPHA differently by modulating
567 			// the backing surface alpha channel ourselves.
568 			// The existing backing surface alpha channel is ignored.
569 			int alpha = mode.factor;
570 			mode.kind = DRAW_REPLACE;
571 
572 			for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip)
573 			{
574 				for (x = 0; x < w; ++x, ++src_p, ++dst_p)
575 				{
576 					Uint32 p = *dst_p & dmask;
577 					Uint32 a = *src_p;
578 
579 					// we use >> 8 instead of / 255, and it does not handle
580 					// alpha == 255 correctly
581 					if (alpha != 0xff)
582 					{	// modulate the alpha channel
583 						a = (a * alpha) >> 8;
584 					}
585 					*dst_p = p | (a << ashift);
586 				}
587 			}
588 		}
589 		else /* if (mode.kind != DRAW_ALPHA) */
590 		{	// Transfer the alpha channel to the backing surface
591 			// DRAW_REPLACE + Color.a is NOT supported right now
592 			for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip)
593 			{
594 				for (x = 0; x < w; ++x, ++src_p, ++dst_p)
595 				{
596 					*dst_p = (*dst_p & dmask) | ((Uint32)*src_p << ashift);
597 				}
598 			}
599 		}
600 	}
601 	SDL_UnlockSurface (surf);
602 
603 	TFB_DrawCanvas_Blit (surf, &srcRect, dst, &targetRect, mode);
604 	UnlockMutex (backing->mutex);
605 }
606 
607 TFB_Canvas
TFB_DrawCanvas_New_TrueColor(int w,int h,BOOLEAN hasalpha)608 TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha)
609 {
610 	SDL_Surface *new_surf;
611 	SDL_PixelFormat* fmt = format_conv_surf->format;
612 
613 	new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h,
614 			fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask,
615 			hasalpha ? fmt->Amask : 0);
616 	if (!new_surf)
617 	{
618 		log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s",
619 				SDL_GetError());
620 		exit (EXIT_FAILURE);
621 	}
622 	return new_surf;
623 }
624 
625 TFB_Canvas
TFB_DrawCanvas_New_ForScreen(int w,int h,BOOLEAN withalpha)626 TFB_DrawCanvas_New_ForScreen (int w, int h, BOOLEAN withalpha)
627 {
628 	SDL_Surface *new_surf;
629 	SDL_PixelFormat* fmt = SDL_Screen->format;
630 
631 	if (fmt->palette)
632 	{
633 		log_add (log_Warning, "TFB_DrawCanvas_New_ForScreen() WARNING:"
634 				"Paletted display format will be slow");
635 
636 		new_surf = TFB_DrawCanvas_New_TrueColor (w, h, withalpha);
637 	}
638 	else
639 	{
640 		if (withalpha && fmt->Amask == 0)
641 			fmt = format_conv_surf->format; // Screen has no alpha and we need it
642 
643 		new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h,
644 				fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask,
645 				withalpha ? fmt->Amask : 0);
646 	}
647 
648 	if (!new_surf)
649 	{
650 		log_add (log_Fatal, "TFB_DrawCanvas_New_ForScreen() INTERNAL PANIC:"
651 				"Failed to create TFB_Canvas: %s", SDL_GetError());
652 		exit (EXIT_FAILURE);
653 	}
654 	return new_surf;
655 }
656 
657 TFB_Canvas
TFB_DrawCanvas_New_Paletted(int w,int h,Color palette[256],int transparent_index)658 TFB_DrawCanvas_New_Paletted (int w, int h, Color palette[256],
659 		int transparent_index)
660 {
661 	SDL_Surface *new_surf;
662 	new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);
663 	if (!new_surf)
664 	{
665 		log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s",
666 				SDL_GetError());
667 		exit (EXIT_FAILURE);
668 	}
669 	if (palette != NULL)
670 	{
671 		TFB_DrawCanvas_SetPalette (new_surf, palette);
672 	}
673 	if (transparent_index >= 0)
674 	{
675 		TFB_SetColorKey (new_surf, transparent_index, 0);
676 	}
677 	else
678 	{
679 		TFB_DisableColorKey (new_surf);
680 	}
681 	return new_surf;
682 }
683 
684 TFB_Canvas
TFB_DrawCanvas_New_ScaleTarget(TFB_Canvas canvas,TFB_Canvas oldcanvas,int type,int last_type)685 TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanvas, int type, int last_type)
686 {
687 	SDL_Surface *src = canvas;
688 	SDL_Surface *old = oldcanvas;
689 	SDL_Surface *newsurf = NULL;
690 
691 	// For the purposes of this function, bilinear == trilinear
692 	if (type == TFB_SCALE_TRILINEAR)
693 		type = TFB_SCALE_BILINEAR;
694 	if (last_type == TFB_SCALE_TRILINEAR)
695 		last_type = TFB_SCALE_BILINEAR;
696 
697 	if (old && type != last_type)
698 	{
699 		TFB_DrawCanvas_Delete (old);
700 		old = NULL;
701 	}
702 	if (old)
703 		return old; /* can just reuse the old one */
704 
705 	if (type == TFB_SCALE_NEAREST)
706 	{
707 		newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, src->w,
708 					src->h,
709 					src->format->BitsPerPixel,
710 					src->format->Rmask,
711 					src->format->Gmask,
712 					src->format->Bmask,
713 					src->format->Amask);
714 		TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
715 	}
716 	else
717 	{
718 		// The scaled image may in fact be larger by 1 pixel than the source
719 		// because of hotspot alignment and fractional edge pixels
720 		if (SDL_Screen->format->BitsPerPixel == 32)
721 			newsurf = TFB_DrawCanvas_New_ForScreen (src->w + 1, src->h + 1, TRUE);
722 		else
723 			newsurf = TFB_DrawCanvas_New_TrueColor (src->w + 1, src->h + 1, TRUE);
724 	}
725 
726 	return newsurf;
727 }
728 
729 TFB_Canvas
TFB_DrawCanvas_New_RotationTarget(TFB_Canvas src_canvas,int angle)730 TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src_canvas, int angle)
731 {
732 	SDL_Surface *src = src_canvas;
733 	SDL_Surface *newsurf;
734 	EXTENT size;
735 
736 	TFB_DrawCanvas_GetRotatedExtent (src_canvas, angle, &size);
737 
738 	newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE,
739 				size.width, size.height,
740 				src->format->BitsPerPixel,
741 				src->format->Rmask,
742 				src->format->Gmask,
743 				src->format->Bmask,
744 				src->format->Amask);
745 	if (!newsurf)
746 	{
747 		log_add (log_Fatal, "TFB_DrawCanvas_New_RotationTarget()"
748 				" INTERNAL PANIC: Failed to create TFB_Canvas: %s",
749 				SDL_GetError());
750 		exit (EXIT_FAILURE);
751 	}
752 	TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
753 
754 	return newsurf;
755 }
756 
757 TFB_Canvas
TFB_DrawCanvas_LoadFromFile(void * dir,const char * fileName)758 TFB_DrawCanvas_LoadFromFile (void *dir, const char *fileName)
759 {
760 	SDL_Surface *surf = sdluio_loadImage (dir, fileName);
761 	if (!surf)
762 		return NULL;
763 
764 	if (surf->format->BitsPerPixel < 8)
765 	{
766 		SDL_SetError ("unsupported image format (min 8bpp)");
767 		SDL_FreeSurface (surf);
768 		surf = NULL;
769 	}
770 
771 	return surf;
772 }
773 
774 void
TFB_DrawCanvas_Delete(TFB_Canvas canvas)775 TFB_DrawCanvas_Delete (TFB_Canvas canvas)
776 {
777 	if (!canvas)
778 	{
779 		log_add (log_Warning, "INTERNAL PANIC: Attempted"
780 				" to delete a NULL canvas!");
781 		/* Should we actually die here? */
782 	}
783 	else
784 	{
785 		SDL_FreeSurface (canvas);
786 	}
787 }
788 
789 BOOLEAN
TFB_DrawCanvas_GetFontCharData(TFB_Canvas canvas,BYTE * outData,unsigned dataPitch)790 TFB_DrawCanvas_GetFontCharData (TFB_Canvas canvas, BYTE *outData,
791 		unsigned dataPitch)
792 {
793 	SDL_Surface *surf = canvas;
794 	int x, y;
795 	Uint8 r, g, b, a;
796 	Uint32 p;
797 	SDL_PixelFormat *fmt = surf->format;
798 	GetPixelFn getpix;
799 
800 	if (!surf || !outData)
801 		return FALSE;
802 
803 	SDL_LockSurface (surf);
804 
805 	getpix = getpixel_for (surf);
806 
807 	// produce an alpha-only image in internal BYTE[] format
808 	//  from the SDL surface
809 	for (y = 0; y < surf->h; ++y)
810 	{
811 		BYTE *dst = outData + dataPitch * y;
812 
813 		for (x = 0; x < surf->w; ++x, ++dst)
814 		{
815 			p = getpix (surf, x, y);
816 			SDL_GetRGBA (p, fmt, &r, &g, &b, &a);
817 
818 			if (!fmt->Amask)
819 			{	// produce alpha from intensity (Y component)
820 				// using a fast approximation
821 				// contributions to Y are: R=2, G=4, B=1
822 				a = ((r * 2) + (g * 4) + b) / 7;
823 			}
824 
825 			*dst = a;
826 		}
827 	}
828 
829 	SDL_UnlockSurface (surf);
830 
831 	return TRUE;
832 }
833 
834 Color *
TFB_DrawCanvas_ExtractPalette(TFB_Canvas canvas)835 TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas)
836 {
837 	int i;
838 	Color *result;
839 	SDL_Surface *surf = canvas;
840 	SDL_Palette *palette = surf->format->palette;
841 
842 	if (!palette)
843 		return NULL;
844 
845 	// There may be less colors in the surface than 256. Init to 0 first.
846 	result = HCalloc (sizeof (Color) * 256);
847 	assert (palette->ncolors <= 256);
848 	for (i = 0; i < palette->ncolors; ++i)
849 		result[i] = NativeToColor (palette->colors[i]);
850 
851 	return result;
852 }
853 
854 TFB_Canvas
TFB_DrawCanvas_ToScreenFormat(TFB_Canvas canvas)855 TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas)
856 {
857 	SDL_Surface *result = TFB_DisplayFormatAlpha (canvas);
858 	if (result == NULL)
859 	{
860 		log_add (log_Debug, "WARNING: Could not convert"
861 				" sprite-canvas to display format.");
862 		return canvas;
863 	}
864 	else if (result == canvas)
865 	{	// no conversion was necessary
866 		return canvas;
867 	}
868 	else
869 	{	// converted
870 		TFB_DrawCanvas_Delete (canvas);
871 		return result;
872 	}
873 
874 	return canvas;
875 }
876 
877 BOOLEAN
TFB_DrawCanvas_IsPaletted(TFB_Canvas canvas)878 TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas)
879 {
880 	return ((SDL_Surface *)canvas)->format->palette != NULL;
881 }
882 
883 void
TFB_DrawCanvas_SetPalette(TFB_Canvas target,Color palette[256])884 TFB_DrawCanvas_SetPalette (TFB_Canvas target, Color palette[256])
885 {
886 	SDL_Color colors[256];
887 	int i;
888 
889 	for (i = 0; i < 256; ++i)
890 		colors[i] = ColorToNative (palette[i]);
891 
892 	TFB_SetColors (target, colors, 0, 256);
893 }
894 
895 int
TFB_DrawCanvas_GetTransparentIndex(TFB_Canvas canvas)896 TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas)
897 {
898 	Uint32 colorkey;
899 	if (TFB_GetColorKey (canvas, &colorkey))
900 	{
901 		return colorkey;
902 	}
903 	return -1;
904 }
905 
906 void
TFB_DrawCanvas_SetTransparentIndex(TFB_Canvas canvas,int index,BOOLEAN rleaccel)907 TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int index, BOOLEAN rleaccel)
908 {
909 	if (index >= 0)
910 	{
911 		TFB_SetColorKey (canvas, index, rleaccel);
912 
913 		if (!TFB_DrawCanvas_IsPaletted (canvas))
914 		{
915 			// disables surface alpha so color key transparency actually works
916 			TFB_DisableSurfaceAlphaMod (canvas);
917 		}
918 	}
919 	else
920 	{
921 		TFB_DisableColorKey (canvas);
922 	}
923 }
924 
925 void
TFB_DrawCanvas_CopyTransparencyInfo(TFB_Canvas src_canvas,TFB_Canvas dst_canvas)926 TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src_canvas,
927 		TFB_Canvas dst_canvas)
928 {
929 	SDL_Surface* src = src_canvas;
930 
931 	if (src->format->palette)
932 	{
933 		int index;
934 		index = TFB_DrawCanvas_GetTransparentIndex (src_canvas);
935 		TFB_DrawCanvas_SetTransparentIndex (dst_canvas, index, FALSE);
936 	}
937 	else
938 	{
939 		Color color;
940 		if (TFB_DrawCanvas_GetTransparentColor (src_canvas, &color))
941 			TFB_DrawCanvas_SetTransparentColor (dst_canvas, color, FALSE);
942 	}
943 }
944 
945 BOOLEAN
TFB_DrawCanvas_GetTransparentColor(TFB_Canvas canvas,Color * color)946 TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, Color *color)
947 {
948 	Uint32 colorkey;
949 	if (!TFB_DrawCanvas_IsPaletted (canvas)
950 			&& (TFB_GetColorKey ((SDL_Surface *)canvas, &colorkey) == 0))
951 	{
952 		Uint8 ur, ug, ub;
953 		SDL_GetRGB (colorkey, ((SDL_Surface *)canvas)->format, &ur, &ug, &ub);
954 		color->r = ur;
955 		color->g = ug;
956 		color->b = ub;
957 		color->a = 0xff;
958 		return TRUE;
959 	}
960 	return FALSE;
961 }
962 
963 void
TFB_DrawCanvas_SetTransparentColor(TFB_Canvas canvas,Color color,BOOLEAN rleaccel)964 TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, Color color,
965 		BOOLEAN rleaccel)
966 {
967 	Uint32 sdlColor;
968 	sdlColor = SDL_MapRGBA (((SDL_Surface *)canvas)->format,
969 			color.r, color.g, color.b, 0);
970 	TFB_SetColorKey (canvas, sdlColor, rleaccel);
971 
972 	if (!TFB_DrawCanvas_IsPaletted (canvas))
973 	{
974 		// disables surface alpha so color key transparency actually works
975 		TFB_DisableSurfaceAlphaMod (canvas);
976 	}
977 }
978 
979 void
TFB_DrawCanvas_GetScaledExtent(TFB_Canvas src_canvas,HOT_SPOT * src_hs,TFB_Canvas src_mipmap,HOT_SPOT * mm_hs,int scale,int type,EXTENT * size,HOT_SPOT * hs)980 TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs,
981 		TFB_Canvas src_mipmap, HOT_SPOT* mm_hs,
982 		int scale, int type, EXTENT *size, HOT_SPOT *hs)
983 {
984 	SDL_Surface *src = src_canvas;
985 	sint32 x, y, w, h;
986 	int frac;
987 
988 	if (!src_mipmap)
989 	{
990 		w = src->w * scale;
991 		h = src->h * scale;
992 		x = src_hs->x * scale;
993 		y = src_hs->y * scale;
994 	}
995 	else
996 	{
997 		// interpolates extents between src and mipmap to get smoother
998 		// transition when surface changes
999 		SDL_Surface *mipmap = src_mipmap;
1000 		int ratio = scale * 2 - GSCALE_IDENTITY;
1001 
1002 		assert (scale >= GSCALE_IDENTITY / 2);
1003 
1004 		w = mipmap->w * GSCALE_IDENTITY + (src->w - mipmap->w) * ratio;
1005 		h = mipmap->h * GSCALE_IDENTITY + (src->h - mipmap->h) * ratio;
1006 
1007 		// Seems it is better to use mipmap hotspot because some
1008 		// source and mipmap images have the same dimensions!
1009 		x = mm_hs->x * GSCALE_IDENTITY + (src_hs->x - mm_hs->x) * ratio;
1010 		y = mm_hs->y * GSCALE_IDENTITY + (src_hs->y - mm_hs->y) * ratio;
1011 	}
1012 
1013 	if (type != TFB_SCALE_NEAREST)
1014 	{
1015 		// align hotspot on an whole pixel
1016 		if (x & (GSCALE_IDENTITY - 1))
1017 		{
1018 			frac = GSCALE_IDENTITY - (x & (GSCALE_IDENTITY - 1));
1019 			x += frac;
1020 			w += frac;
1021 		}
1022 		if (y & (GSCALE_IDENTITY - 1))
1023 		{
1024 			frac = GSCALE_IDENTITY - (y & (GSCALE_IDENTITY - 1));
1025 			y += frac;
1026 			h += frac;
1027 		}
1028 		// pad the extent to accomodate fractional edge pixels
1029 		w += (GSCALE_IDENTITY - 1);
1030 		h += (GSCALE_IDENTITY - 1);
1031 	}
1032 
1033 	size->width = w / GSCALE_IDENTITY;
1034 	size->height = h / GSCALE_IDENTITY;
1035 	hs->x = x / GSCALE_IDENTITY;
1036 	hs->y = y / GSCALE_IDENTITY;
1037 
1038 	// Scaled image can be larger than the source by 1 pixel
1039 	// because of hotspot alignment and fractional edge pixels
1040 	assert (size->width <= src->w + 1 && size->height <= src->h + 1);
1041 
1042 	if (!size->width && src->w)
1043 		size->width = 1;
1044 	if (!size->height && src->h)
1045 		size->height = 1;
1046 }
1047 
1048 void
TFB_DrawCanvas_GetExtent(TFB_Canvas canvas,EXTENT * size)1049 TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size)
1050 {
1051 	SDL_Surface *src = canvas;
1052 
1053 	size->width = src->w;
1054 	size->height = src->h;
1055 }
1056 
1057 void
TFB_DrawCanvas_Rescale_Nearest(TFB_Canvas src_canvas,TFB_Canvas dst_canvas,int scale,HOT_SPOT * src_hs,EXTENT * size,HOT_SPOT * dst_hs)1058 TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
1059 		int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs)
1060 {
1061 	SDL_Surface *src = src_canvas;
1062 	SDL_Surface *dst = dst_canvas;
1063 	int x, y;
1064 	int fsx = 0, fsy = 0; // source fractional dx and dy increments
1065 	int ssx = 0, ssy = 0; // source fractional x and y starting points
1066 	int w, h;
1067 
1068 	if (scale > 0)
1069 	{
1070 		TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale,
1071 				TFB_SCALE_NEAREST, size, dst_hs);
1072 
1073 		w = size->width;
1074 		h = size->height;
1075 	}
1076 	else
1077 	{
1078 		// Just go with the dst surface dimensions
1079 		w = dst->w;
1080 		h = dst->h;
1081 	}
1082 
1083 	if (w > dst->w || h > dst->h)
1084 	{
1085 		log_add (log_Warning, "TFB_DrawCanvas_Rescale_Nearest: Tried to scale"
1086 				" image to size %d %d when dest_canvas has only"
1087 				" dimensions of %d %d! Failing.",
1088 				w, h, dst->w, dst->h);
1089 		return;
1090 	}
1091 
1092 	if (w > 1)
1093 		fsx = ((src->w - 1) << 16) / (w - 1);
1094 	if (h > 1)
1095 		fsy = ((src->h - 1) << 16) / (h - 1);
1096 	// We start with a value in 0..0.5 range to shift the bigger
1097 	// jumps towards the center of the image
1098 	ssx = 0x6000;
1099 	ssy = 0x6000;
1100 
1101 	SDL_LockSurface (src);
1102 	SDL_LockSurface (dst);
1103 
1104 	if (src->format->BytesPerPixel == 1 && dst->format->BytesPerPixel == 1)
1105 	{
1106 		Uint8 *sp, *csp, *dp, *cdp;
1107 		int sx, sy; // source fractional x and y positions
1108 
1109 		sp = csp = (Uint8 *) src->pixels;
1110 		dp = cdp = (Uint8 *) dst->pixels;
1111 
1112 		for (y = 0, sy = ssy; y < h; ++y)
1113 		{
1114 			csp += (sy >> 16) * src->pitch;
1115 			sp = csp;
1116 			dp = cdp;
1117 			for (x = 0, sx = ssx; x < w; ++x)
1118 			{
1119 				sp += (sx >> 16);
1120 				*dp = *sp;
1121 				sx &= 0xffff;
1122 				sx += fsx;
1123 				++dp;
1124 			}
1125 			sy &= 0xffff;
1126 			sy += fsy;
1127 			cdp += dst->pitch;
1128 		}
1129 	}
1130 	else if (src->format->BytesPerPixel == 4 && dst->format->BytesPerPixel == 4)
1131 	{
1132 		Uint32 *sp, *csp, *dp, *cdp;
1133 		int sx, sy; // source fractional x and y positions
1134 		int sgap, dgap;
1135 
1136 		sgap = src->pitch >> 2;
1137 		dgap = dst->pitch >> 2;
1138 
1139 		sp = csp = (Uint32 *) src->pixels;
1140 		dp = cdp = (Uint32 *) dst->pixels;
1141 
1142 		for (y = 0, sy = ssy; y < h; ++y)
1143 		{
1144 			csp += (sy >> 16) * sgap;
1145 			sp = csp;
1146 			dp = cdp;
1147 			for (x = 0, sx = ssx; x < w; ++x)
1148 			{
1149 				sp += (sx >> 16);
1150 				*dp = *sp;
1151 				sx &= 0xffff;
1152 				sx += fsx;
1153 				++dp;
1154 			}
1155 			sy &= 0xffff;
1156 			sy += fsy;
1157 			cdp += dgap;
1158 		}
1159 	}
1160 	else
1161 	{
1162 		log_add (log_Warning, "Tried to deal with unknown BPP: %d -> %d",
1163 				src->format->BitsPerPixel, dst->format->BitsPerPixel);
1164 	}
1165 	SDL_UnlockSurface (dst);
1166 	SDL_UnlockSurface (src);
1167 }
1168 
1169 typedef union
1170 {
1171 	Uint32 value;
1172 	Uint8 chan[4];
1173 	struct
1174 	{
1175 		Uint8 r, g, b, a;
1176 	} c;
1177 } pixel_t;
1178 
1179 static inline Uint8
dot_product_8_4(pixel_t * p,int c,Uint8 * v)1180 dot_product_8_4 (pixel_t* p, int c, Uint8* v)
1181 {	// math expanded for speed
1182 #if 0
1183 	return (
1184 			(Uint32)p[0].chan[c] * v[0] + (Uint32)p[1].chan[c] * v[1] +
1185 			(Uint32)p[2].chan[c] * v[2] + (Uint32)p[3].chan[c] * v[3]
1186 		) >> 8;
1187 #else
1188 	// mult-table driven version
1189 	return
1190 			btable[p[0].chan[c]][v[0]] + btable[p[1].chan[c]][v[1]] +
1191 			btable[p[2].chan[c]][v[2]] + btable[p[3].chan[c]][v[3]];
1192 #endif
1193 }
1194 
1195 static inline Uint8
weight_product_8_4(pixel_t * p,int c,Uint8 * w)1196 weight_product_8_4 (pixel_t* p, int c, Uint8* w)
1197 {	// math expanded for speed
1198 	return (
1199 			(Uint32)p[0].chan[c] * w[0] + (Uint32)p[1].chan[c] * w[1] +
1200 			(Uint32)p[2].chan[c] * w[2] + (Uint32)p[3].chan[c] * w[3]
1201 		) / (w[0] + w[1] + w[2] + w[3]);
1202 }
1203 
1204 static inline Uint8
blend_ratio_2(Uint8 c1,Uint8 c2,int ratio)1205 blend_ratio_2 (Uint8 c1, Uint8 c2, int ratio)
1206 {	// blend 2 color values according to ratio (0..256)
1207 	// identical to proper alpha blending
1208 	return (((c1 - c2) * ratio) >> 8) + c2;
1209 }
1210 
1211 static inline Uint32
scale_read_pixel(void * ppix,SDL_PixelFormat * fmt,SDL_Color * pal,Uint32 mask,Uint32 key)1212 scale_read_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal,
1213 				Uint32 mask, Uint32 key)
1214 {
1215 	pixel_t p;
1216 
1217 	// start off with non-present pixel
1218 	p.value = 0;
1219 
1220 	if (pal)
1221 	{	// paletted pixel; mask not used
1222 		Uint32 c = *(Uint8 *)ppix;
1223 
1224 		if (c != key)
1225 		{
1226 			p.c.r = pal[c].r;
1227 			p.c.g = pal[c].g;
1228 			p.c.b = pal[c].b;
1229 			p.c.a = SDL_ALPHA_OPAQUE;
1230 		}
1231 	}
1232 	else
1233 	{	// RGB(A) pixel; (pix & mask) != key
1234 		Uint32 c = *(Uint32 *)ppix;
1235 
1236 		if ((c & mask) != key)
1237 		{
1238 #if 0
1239 			SDL_GetRGBA (c, fmt, &p.c.r, &p.c.g, &p.c.b, &p.c.a);
1240 #else
1241 			// Assume 8 bits/channel; a safe assumption with 32bpp surfaces
1242 			p.c.r = (c >> fmt->Rshift) & 0xff;
1243 			p.c.g = (c >> fmt->Gshift) & 0xff;
1244 			p.c.b = (c >> fmt->Bshift) & 0xff;
1245 			if (fmt->Amask)
1246 				p.c.a = (c >> fmt->Ashift) & 0xff;
1247 			else
1248 				p.c.a = SDL_ALPHA_OPAQUE;
1249 		}
1250 #endif
1251 	}
1252 
1253 	return p.value;
1254 }
1255 
1256 static inline Uint32
scale_get_pixel(SDL_Surface * src,Uint32 mask,Uint32 key,int x,int y)1257 scale_get_pixel (SDL_Surface *src, Uint32 mask, Uint32 key, int x, int y)
1258 {
1259 	SDL_Color *pal = src->format->palette? src->format->palette->colors : 0;
1260 
1261 	if (x < 0 || x >= src->w || y < 0 || y >= src->h)
1262 		return 0;
1263 
1264 	return scale_read_pixel ((Uint8*)src->pixels + y * src->pitch +
1265 			x * src->format->BytesPerPixel, src->format, pal, mask, key);
1266 }
1267 
1268 void
TFB_DrawCanvas_Rescale_Trilinear(TFB_Canvas src_canvas,TFB_Canvas src_mipmap,TFB_Canvas dst_canvas,int scale,HOT_SPOT * src_hs,HOT_SPOT * mm_hs,EXTENT * size,HOT_SPOT * dst_hs)1269 TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, TFB_Canvas src_mipmap,
1270 		TFB_Canvas dst_canvas, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs,
1271 		EXTENT* size, HOT_SPOT* dst_hs)
1272 {
1273 	SDL_Surface *src = src_canvas;
1274 	SDL_Surface *dst = dst_canvas;
1275 	SDL_Surface *mm = src_mipmap;
1276 	SDL_PixelFormat *srcfmt = src->format;
1277 	SDL_PixelFormat *mmfmt = mm->format;
1278 	SDL_PixelFormat *dstfmt = dst->format;
1279 	SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0;
1280 	const int sbpp = srcfmt->BytesPerPixel;
1281 	const int mmbpp = mmfmt->BytesPerPixel;
1282 	const int slen = src->pitch;
1283 	const int mmlen = mm->pitch;
1284 	const int dst_has_alpha = (dstfmt->Amask != 0);
1285 	Uint32 transparent = 0;
1286 	const int alpha_threshold = dst_has_alpha ? 0 : 127;
1287 	// src v. mipmap importance factor
1288 	int ratio = scale * 2 - GSCALE_IDENTITY;
1289 	// source masks and keys
1290 	Uint32 mk0 = 0, ck0 = ~0, mk1 = 0, ck1 = ~0;
1291 	// source fractional x and y positions
1292 	int sx0, sy0, sx1, sy1;
1293 	// source fractional dx and dy increments
1294 	int fsx0 = 0, fsy0 = 0, fsx1 = 0, fsy1 = 0;
1295 	// source fractional x and y starting points
1296 	int ssx0 = 0, ssy0 = 0, ssx1 = 0, ssy1 = 0;
1297 	int x, y, w, h;
1298 
1299 	TFB_GetColorKey (dst, &transparent);
1300 
1301 	if (mmfmt->palette && !srcpal)
1302 	{
1303 		log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
1304 				"Mipmap is paletted, but source is not! Failing.");
1305 		return;
1306 	}
1307 
1308 	if (scale > 0)
1309 	{
1310 		int fw, fh;
1311 
1312 		// Use (scale / GSCALE_IDENTITY) sizing factor
1313 		TFB_DrawCanvas_GetScaledExtent (src, src_hs, mm, mm_hs, scale,
1314 				TFB_SCALE_TRILINEAR, size, dst_hs);
1315 
1316 		w = size->width;
1317 		h = size->height;
1318 
1319 		fw = mm->w * GSCALE_IDENTITY + (src->w - mm->w) * ratio;
1320 		fh = mm->h * GSCALE_IDENTITY + (src->h - mm->h) * ratio;
1321 
1322 		// This limits the effective source dimensions to 2048x2048,
1323 		// and we also lose 4 bits of precision out of 16 (no problem)
1324 		fsx0 = (src->w << 20) / fw;
1325 		fsx0 <<= 4;
1326 		fsy0 = (src->h << 20) / fh;
1327 		fsy0 <<= 4;
1328 
1329 		fsx1 = (mm->w << 20) / fw;
1330 		fsx1 <<= 4;
1331 		fsy1 = (mm->h << 20) / fh;
1332 		fsy1 <<= 4;
1333 
1334 		// position the hotspots directly over each other
1335 		ssx0 = (src_hs->x << 16) - fsx0 * dst_hs->x;
1336 		ssy0 = (src_hs->y << 16) - fsy0 * dst_hs->y;
1337 
1338 		ssx1 = (mm_hs->x << 16) - fsx1 * dst_hs->x;
1339 		ssy1 = (mm_hs->y << 16) - fsy1 * dst_hs->y;
1340 	}
1341 	else
1342 	{
1343 		// Just go with the dst surface dimensions
1344 		w = dst->w;
1345 		h = dst->h;
1346 
1347 		fsx0 = (src->w << 16) / w;
1348 		fsy0 = (src->h << 16) / h;
1349 
1350 		fsx1 = (mm->w << 16) / w;
1351 		fsy1 = (mm->h << 16) / h;
1352 
1353 		// give equal importance to both edges
1354 		ssx0 = (((src->w - 1) << 16) - fsx0 * (w - 1)) >> 1;
1355 		ssy0 = (((src->h - 1) << 16) - fsy0 * (h - 1)) >> 1;
1356 
1357 		ssx1 = (((mm->w - 1) << 16) - fsx1 * (w - 1)) >> 1;
1358 		ssy1 = (((mm->h - 1) << 16) - fsy1 * (h - 1)) >> 1;
1359 	}
1360 
1361 	if (w > dst->w || h > dst->h)
1362 	{
1363 		log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
1364 				"Tried to scale image to size %d %d when dest_canvas"
1365 				" has only dimensions of %d %d! Failing.",
1366 				w, h, dst->w, dst->h);
1367 		return;
1368 	}
1369 
1370 	if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) ||
1371 		(mmfmt->BytesPerPixel != 1 && mmfmt->BytesPerPixel != 4) ||
1372 		(dst->format->BytesPerPixel != 4))
1373 	{
1374 		log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
1375 				"Tried to deal with unknown BPP: %d -> %d, mipmap %d",
1376 				srcfmt->BitsPerPixel, dst->format->BitsPerPixel,
1377 				mmfmt->BitsPerPixel);
1378 		return;
1379 	}
1380 
1381 	// use colorkeys where appropriate
1382 	if (srcfmt->Amask)
1383 	{	// alpha transparency
1384 		mk0 = srcfmt->Amask;
1385 		ck0 = 0;
1386 	}
1387 	else if (TFB_GetColorKey (src, &ck0) == 0)
1388 	{	// colorkey transparency
1389 		mk0 = ~srcfmt->Amask;
1390 		ck0 &= mk0;
1391 	}
1392 
1393 	if (mmfmt->Amask)
1394 	{	// alpha transparency
1395 		mk1 = mmfmt->Amask;
1396 		ck1 = 0;
1397 	}
1398 	else if (TFB_GetColorKey (mm, &ck1) == 0)
1399 	{	// colorkey transparency
1400 		mk1 = ~mmfmt->Amask;
1401 		ck1 &= mk1;
1402 	}
1403 
1404 	SDL_LockSurface(src);
1405 	SDL_LockSurface(dst);
1406 	SDL_LockSurface(mm);
1407 
1408 	for (y = 0, sy0 = ssy0, sy1 = ssy1;
1409 			y < h;
1410 			++y, sy0 += fsy0, sy1 += fsy1)
1411 	{
1412 		Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch);
1413 		const int py0 = (sy0 >> 16);
1414 		const int py1 = (sy1 >> 16);
1415 		Uint8 *src_a0 = (Uint8*)src->pixels + py0 * slen;
1416 		Uint8 *src_a1 = (Uint8*)mm->pixels + py1 * mmlen;
1417 		// retrieve the fractional portions of y
1418 		const Uint8 v0 = (sy0 >> 8) & 0xff;
1419 		const Uint8 v1 = (sy1 >> 8) & 0xff;
1420 		Uint8 w0[4], w1[4]; // pixel weight vectors
1421 
1422 		for (x = 0, sx0 = ssx0, sx1 = ssx1;
1423 				x < w;
1424 				++x, ++dst_p, sx0 += fsx0, sx1 += fsx1)
1425 		{
1426 			const int px0 = (sx0 >> 16);
1427 			const int px1 = (sx1 >> 16);
1428 			// retrieve the fractional portions of x
1429 			const Uint8 u0 = (sx0 >> 8) & 0xff;
1430 			const Uint8 u1 = (sx1 >> 8) & 0xff;
1431 			// pixels are examined and numbered in pattern
1432 			//  0  1
1433 			//  2  3
1434 			// the ideal pixel (4) is somewhere between these four
1435 			// and is calculated from these using weight vector (w)
1436 			// with a dot product
1437 			pixel_t p0[5], p1[5];
1438 			Uint8 res_a;
1439 
1440 			w0[0] = btable[255 - u0][255 - v0];
1441 			w0[1] = btable[u0][255 - v0];
1442 			w0[2] = btable[255 - u0][v0];
1443 			w0[3] = btable[u0][v0];
1444 
1445 			w1[0] = btable[255 - u1][255 - v1];
1446 			w1[1] = btable[u1][255 - v1];
1447 			w1[2] = btable[255 - u1][v1];
1448 			w1[3] = btable[u1][v1];
1449 
1450 			// Collect interesting pixels from src image
1451 			// Optimization: speed is criticial on larger images;
1452 			// most pixel reads fall completely inside the image
1453 			if (px0 >= 0 && px0 + 1 < src->w && py0 >= 0 && py0 + 1 < src->h)
1454 			{
1455 				Uint8 *src_p = src_a0 + px0 * sbpp;
1456 
1457 				p0[0].value = scale_read_pixel (src_p, srcfmt,
1458 						srcpal, mk0, ck0);
1459 				p0[1].value = scale_read_pixel (src_p + sbpp, srcfmt,
1460 						srcpal, mk0, ck0);
1461 				p0[2].value = scale_read_pixel (src_p + slen, srcfmt,
1462 						srcpal, mk0, ck0);
1463 				p0[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt,
1464 						srcpal, mk0, ck0);
1465 			}
1466 			else
1467 			{
1468 				p0[0].value = scale_get_pixel (src, mk0, ck0, px0, py0);
1469 				p0[1].value = scale_get_pixel (src, mk0, ck0, px0 + 1, py0);
1470 				p0[2].value = scale_get_pixel (src, mk0, ck0, px0, py0 + 1);
1471 				p0[3].value = scale_get_pixel (src, mk0, ck0,
1472 						px0 + 1, py0 + 1);
1473 			}
1474 
1475 			// Collect interesting pixels from mipmap image
1476 			if (px1 >= 0 && px1 + 1 < mm->w && py1 >= 0 && py1 + 1 < mm->h)
1477 			{
1478 				Uint8 *mm_p = src_a1 + px1 * mmbpp;
1479 
1480 				p1[0].value = scale_read_pixel (mm_p, mmfmt,
1481 						srcpal, mk1, ck1);
1482 				p1[1].value = scale_read_pixel (mm_p + mmbpp, mmfmt,
1483 						srcpal, mk1, ck1);
1484 				p1[2].value = scale_read_pixel (mm_p + mmlen, mmfmt,
1485 						srcpal, mk1, ck1);
1486 				p1[3].value = scale_read_pixel (mm_p + mmbpp + mmlen, mmfmt,
1487 						srcpal, mk1, ck1);
1488 			}
1489 			else
1490 			{
1491 				p1[0].value = scale_get_pixel (mm, mk1, ck1, px1, py1);
1492 				p1[1].value = scale_get_pixel (mm, mk1, ck1, px1 + 1, py1);
1493 				p1[2].value = scale_get_pixel (mm, mk1, ck1, px1, py1 + 1);
1494 				p1[3].value = scale_get_pixel (mm, mk1, ck1,
1495 						px1 + 1, py1 + 1);
1496 			}
1497 
1498 			p0[4].c.a = dot_product_8_4 (p0, 3, w0);
1499 			p1[4].c.a = dot_product_8_4 (p1, 3, w1);
1500 
1501 			res_a = blend_ratio_2 (p0[4].c.a, p1[4].c.a, ratio);
1502 
1503 			if (res_a <= alpha_threshold)
1504 			{
1505 				*dst_p = transparent;
1506 			}
1507 			else if (!dst_has_alpha)
1508 			{	// RGB surface handling
1509 				p0[4].c.r = dot_product_8_4 (p0, 0, w0);
1510 				p0[4].c.g = dot_product_8_4 (p0, 1, w0);
1511 				p0[4].c.b = dot_product_8_4 (p0, 2, w0);
1512 
1513 				p1[4].c.r = dot_product_8_4 (p1, 0, w1);
1514 				p1[4].c.g = dot_product_8_4 (p1, 1, w1);
1515 				p1[4].c.b = dot_product_8_4 (p1, 2, w1);
1516 
1517 				p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio);
1518 				p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio);
1519 				p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio);
1520 
1521 				// TODO: we should handle alpha-blending here, but we do
1522 				//   not know the destination color for blending!
1523 
1524 				*dst_p =
1525 					(p0[4].c.r << dstfmt->Rshift) |
1526 					(p0[4].c.g << dstfmt->Gshift) |
1527 					(p0[4].c.b << dstfmt->Bshift);
1528 			}
1529 			else
1530 			{	// RGBA surface handling
1531 
1532 				// we do not want to blend with non-present pixels
1533 				// (pixels that have alpha == 0) as these will
1534 				// skew the result and make resulting alpha useless
1535 				if (p0[4].c.a != 0)
1536 				{
1537 					int i;
1538 					for (i = 0; i < 4; ++i)
1539 						if (p0[i].c.a == 0)
1540 							w0[i] = 0;
1541 
1542 					p0[4].c.r = weight_product_8_4 (p0, 0, w0);
1543 					p0[4].c.g = weight_product_8_4 (p0, 1, w0);
1544 					p0[4].c.b = weight_product_8_4 (p0, 2, w0);
1545 				}
1546 				if (p1[4].c.a != 0)
1547 				{
1548 					int i;
1549 					for (i = 0; i < 4; ++i)
1550 						if (p1[i].c.a == 0)
1551 							w1[i] = 0;
1552 
1553 					p1[4].c.r = weight_product_8_4 (p1, 0, w1);
1554 					p1[4].c.g = weight_product_8_4 (p1, 1, w1);
1555 					p1[4].c.b = weight_product_8_4 (p1, 2, w1);
1556 				}
1557 
1558 				if (p0[4].c.a != 0 && p1[4].c.a != 0)
1559 				{	// blend if both present
1560 					p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio);
1561 					p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio);
1562 					p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio);
1563 				}
1564 				else if (p1[4].c.a != 0)
1565 				{	// other pixel is present
1566 					p0[4].value = p1[4].value;
1567 				}
1568 
1569 				// error-correct alpha to fully opaque to remove
1570 				// the often unwanted and unnecessary blending
1571 				if (res_a > 0xf8)
1572 					res_a = 0xff;
1573 
1574 				*dst_p =
1575 					(p0[4].c.r << dstfmt->Rshift) |
1576 					(p0[4].c.g << dstfmt->Gshift) |
1577 					(p0[4].c.b << dstfmt->Bshift) |
1578 					(res_a << dstfmt->Ashift);
1579 			}
1580 		}
1581 	}
1582 
1583 	SDL_UnlockSurface(mm);
1584 	SDL_UnlockSurface(dst);
1585 	SDL_UnlockSurface(src);
1586 }
1587 
1588 void
TFB_DrawCanvas_Rescale_Bilinear(TFB_Canvas src_canvas,TFB_Canvas dst_canvas,int scale,HOT_SPOT * src_hs,EXTENT * size,HOT_SPOT * dst_hs)1589 TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
1590 		int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs)
1591 {
1592 	SDL_Surface *src = src_canvas;
1593 	SDL_Surface *dst = dst_canvas;
1594 	SDL_PixelFormat *srcfmt = src->format;
1595 	SDL_PixelFormat *dstfmt = dst->format;
1596 	SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0;
1597 	const int sbpp = srcfmt->BytesPerPixel;
1598 	const int slen = src->pitch;
1599 	const int dst_has_alpha = (dstfmt->Amask != 0);
1600 	Uint32 srckey = 0, transparent = 0;
1601 	const int alpha_threshold = dst_has_alpha ? 0 : 127;
1602 	// source masks and keys
1603 	Uint32 mk = 0, ck = ~0;
1604 	// source fractional x and y positions
1605 	int sx, sy;
1606 	// source fractional dx and dy increments
1607 	int fsx = 0, fsy = 0;
1608 	// source fractional x and y starting points
1609 	int ssx = 0, ssy = 0;
1610 	int x, y, w, h;
1611 
1612 	// Get destination transparent color if it exists
1613 	TFB_GetColorKey (dst, &transparent);
1614 
1615 	if (scale > 0)
1616 	{
1617 		// Use (scale / GSCALE_IDENTITY) sizing factor
1618 		TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale,
1619 				TFB_SCALE_BILINEAR, size, dst_hs);
1620 
1621 		w = size->width;
1622 		h = size->height;
1623 		fsx = (GSCALE_IDENTITY << 16) / scale;
1624 		fsy = (GSCALE_IDENTITY << 16) / scale;
1625 
1626 		// position the hotspots directly over each other
1627 		ssx = (src_hs->x << 16) - fsx * dst_hs->x;
1628 		ssy = (src_hs->y << 16) - fsy * dst_hs->y;
1629 	}
1630 	else
1631 	{
1632 		// Just go with the dst surface dimensions
1633 		w = dst->w;
1634 		h = dst->h;
1635 		fsx = (src->w << 16) / w;
1636 		fsy = (src->h << 16) / h;
1637 
1638 		// give equal importance to both edges
1639 		ssx = (((src->w - 1) << 16) - fsx * (w - 1)) >> 1;
1640 		ssy = (((src->h - 1) << 16) - fsy * (h - 1)) >> 1;
1641 	}
1642 
1643 	if (w > dst->w || h > dst->h)
1644 	{
1645 		log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: "
1646 				"Tried to scale image to size %d %d when dest_canvas"
1647 				" has only dimensions of %d %d! Failing.",
1648 				w, h, dst->w, dst->h);
1649 		return;
1650 	}
1651 
1652 	if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) ||
1653 		(dst->format->BytesPerPixel != 4))
1654 	{
1655 		log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: "
1656 				"Tried to deal with unknown BPP: %d -> %d",
1657 				srcfmt->BitsPerPixel, dst->format->BitsPerPixel);
1658 		return;
1659 	}
1660 
1661 	// use colorkeys where appropriate
1662 	if (srcfmt->Amask)
1663 	{	// alpha transparency
1664 		mk = srcfmt->Amask;
1665 		ck = 0;
1666 	}
1667 	else if (TFB_GetColorKey (src, &srckey) == 0)
1668 	{	// colorkey transparency
1669 		mk = ~srcfmt->Amask;
1670 		ck = srckey & mk;
1671 	}
1672 
1673 	SDL_LockSurface(src);
1674 	SDL_LockSurface(dst);
1675 
1676 	for (y = 0, sy = ssy; y < h; ++y, sy += fsy)
1677 	{
1678 		Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch);
1679 		const int py = (sy >> 16);
1680 		Uint8 *src_a = (Uint8*)src->pixels + py * slen;
1681 		// retrieve the fractional portions of y
1682 		const Uint8 v = (sy >> 8) & 0xff;
1683 		Uint8 weight[4]; // pixel weight vectors
1684 
1685 		for (x = 0, sx = ssx; x < w; ++x, ++dst_p, sx += fsx)
1686 		{
1687 			const int px = (sx >> 16);
1688 			// retrieve the fractional portions of x
1689 			const Uint8 u = (sx >> 8) & 0xff;
1690 			// pixels are examined and numbered in pattern
1691 			//  0  1
1692 			//  2  3
1693 			// the ideal pixel (4) is somewhere between these four
1694 			// and is calculated from these using weight vector (weight)
1695 			// with a dot product
1696 			pixel_t p[5];
1697 
1698 			weight[0] = btable[255 - u][255 - v];
1699 			weight[1] = btable[u][255 - v];
1700 			weight[2] = btable[255 - u][v];
1701 			weight[3] = btable[u][v];
1702 
1703 			// Collect interesting pixels from src image
1704 			// Optimization: speed is criticial on larger images;
1705 			// most pixel reads fall completely inside the image
1706 			if (px >= 0 && px + 1 < src->w && py >= 0 && py + 1 < src->h)
1707 			{
1708 				Uint8 *src_p = src_a + px * sbpp;
1709 
1710 				p[0].value = scale_read_pixel (src_p, srcfmt, srcpal, mk, ck);
1711 				p[1].value = scale_read_pixel (src_p + sbpp, srcfmt,
1712 						srcpal, mk, ck);
1713 				p[2].value = scale_read_pixel (src_p + slen, srcfmt,
1714 						srcpal, mk, ck);
1715 				p[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt,
1716 						srcpal, mk, ck);
1717 			}
1718 			else
1719 			{
1720 				p[0].value = scale_get_pixel (src, mk, ck, px, py);
1721 				p[1].value = scale_get_pixel (src, mk, ck, px + 1, py);
1722 				p[2].value = scale_get_pixel (src, mk, ck, px, py + 1);
1723 				p[3].value = scale_get_pixel (src, mk, ck, px + 1, py + 1);
1724 			}
1725 
1726 			p[4].c.a = dot_product_8_4 (p, 3, weight);
1727 
1728 			if (p[4].c.a <= alpha_threshold)
1729 			{
1730 				*dst_p = transparent;
1731 			}
1732 			else if (!dst_has_alpha)
1733 			{	// RGB surface handling
1734 				p[4].c.r = dot_product_8_4 (p, 0, weight);
1735 				p[4].c.g = dot_product_8_4 (p, 1, weight);
1736 				p[4].c.b = dot_product_8_4 (p, 2, weight);
1737 
1738 				// TODO: we should handle alpha-blending here, but we do
1739 				//   not know the destination color for blending!
1740 
1741 				*dst_p =
1742 					(p[4].c.r << dstfmt->Rshift) |
1743 					(p[4].c.g << dstfmt->Gshift) |
1744 					(p[4].c.b << dstfmt->Bshift);
1745 			}
1746 			else
1747 			{	// RGBA surface handling
1748 
1749 				// we do not want to blend with non-present pixels
1750 				// (pixels that have alpha == 0) as these will
1751 				// skew the result and make resulting alpha useless
1752 				int i;
1753 				for (i = 0; i < 4; ++i)
1754 					if (p[i].c.a == 0)
1755 						weight[i] = 0;
1756 
1757 				p[4].c.r = weight_product_8_4 (p, 0, weight);
1758 				p[4].c.g = weight_product_8_4 (p, 1, weight);
1759 				p[4].c.b = weight_product_8_4 (p, 2, weight);
1760 
1761 				// error-correct alpha to fully opaque to remove
1762 				// the often unwanted and unnecessary blending
1763 				if (p[4].c.a > 0xf8)
1764 					p[4].c.a = 0xff;
1765 
1766 				*dst_p =
1767 					(p[4].c.r << dstfmt->Rshift) |
1768 					(p[4].c.g << dstfmt->Gshift) |
1769 					(p[4].c.b << dstfmt->Bshift) |
1770 					(p[4].c.a << dstfmt->Ashift);
1771 			}
1772 		}
1773 	}
1774 
1775 	SDL_UnlockSurface(dst);
1776 	SDL_UnlockSurface(src);
1777 }
1778 
1779 void
TFB_DrawCanvas_Lock(TFB_Canvas canvas)1780 TFB_DrawCanvas_Lock (TFB_Canvas canvas)
1781 {
1782 	SDL_Surface *surf = canvas;
1783 	SDL_LockSurface (surf);
1784 }
1785 
1786 void
TFB_DrawCanvas_Unlock(TFB_Canvas canvas)1787 TFB_DrawCanvas_Unlock (TFB_Canvas canvas)
1788 {
1789 	SDL_Surface *surf = canvas;
1790 	SDL_UnlockSurface (surf);
1791 }
1792 
1793 void
TFB_DrawCanvas_GetScreenFormat(TFB_PixelFormat * fmt)1794 TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt)
1795 {
1796 	SDL_PixelFormat *sdl = SDL_Screen->format;
1797 
1798 	if (sdl->palette)
1799 	{
1800 		log_add (log_Warning, "TFB_DrawCanvas_GetScreenFormat() WARNING:"
1801 				"Paletted display format will be slow");
1802 
1803 		fmt->BitsPerPixel = 32;
1804 		fmt->Rmask = 0x000000ff;
1805 		fmt->Gmask = 0x0000ff00;
1806 		fmt->Bmask = 0x00ff0000;
1807 		fmt->Amask = 0xff000000;
1808 	}
1809 	else
1810 	{
1811 		fmt->BitsPerPixel = sdl->BitsPerPixel;
1812 		fmt->Rmask = sdl->Rmask;
1813 		fmt->Gmask = sdl->Gmask;
1814 		fmt->Bmask = sdl->Bmask;
1815 		fmt->Amask = sdl->Amask;
1816 	}
1817 }
1818 
1819 int
TFB_DrawCanvas_GetStride(TFB_Canvas canvas)1820 TFB_DrawCanvas_GetStride (TFB_Canvas canvas)
1821 {
1822 	SDL_Surface *surf = canvas;
1823 	return surf->pitch;
1824 }
1825 
1826 void*
TFB_DrawCanvas_GetLine(TFB_Canvas canvas,int line)1827 TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line)
1828 {
1829 	SDL_Surface *surf = canvas;
1830 	return (uint8 *)surf->pixels + surf->pitch * line;
1831 }
1832 
1833 Color
TFB_DrawCanvas_GetPixel(TFB_Canvas canvas,int x,int y)1834 TFB_DrawCanvas_GetPixel (TFB_Canvas canvas, int x, int y)
1835 {
1836 	SDL_Surface* surf = canvas;
1837 	Uint32 pixel;
1838 	GetPixelFn getpixel;
1839 	Color c = {0, 0, 0, 0};
1840 
1841 	if (x < 0 || x >= surf->w || y < 0 || y >= surf->h)
1842 	{	// outside bounds, return 0
1843 		return c;
1844 	}
1845 
1846 	SDL_LockSurface (surf);
1847 
1848 	getpixel = getpixel_for(surf);
1849 	pixel = (*getpixel)(surf, x, y);
1850 	SDL_GetRGBA (pixel, surf->format, &c.r, &c.g, &c.b, &c.a);
1851 
1852 	SDL_UnlockSurface (surf);
1853 
1854 	return c;
1855 }
1856 
1857 void
TFB_DrawCanvas_Rotate(TFB_Canvas src_canvas,TFB_Canvas dst_canvas,int angle,EXTENT size)1858 TFB_DrawCanvas_Rotate (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
1859 		int angle, EXTENT size)
1860 {
1861 	SDL_Surface *src = src_canvas;
1862 	SDL_Surface *dst = dst_canvas;
1863 	int ret;
1864 	Color color;
1865 
1866 	if (size.width > dst->w || size.height > dst->h)
1867 	{
1868 		log_add (log_Warning, "TFB_DrawCanvas_Rotate: Tried to rotate"
1869 				" image to size %d %d when dst_canvas has only dimensions"
1870 				" of %d %d! Failing.",
1871 				size.width, size.height, dst->w, dst->h);
1872 		return;
1873 	}
1874 
1875 	if (TFB_DrawCanvas_GetTransparentColor (src, &color))
1876 	{
1877 		TFB_DrawCanvas_SetTransparentColor (dst, color, FALSE);
1878 		/* fill destination with transparent color before rotating */
1879 		SDL_FillRect(dst, NULL, SDL_MapRGBA (dst->format,
1880 				color.r, color.g, color.b, 0));
1881 	}
1882 
1883 	ret = rotateSurface (src, dst, angle, 0);
1884 	if (ret != 0)
1885 	{
1886 		log_add (log_Warning, "TFB_DrawCanvas_Rotate: WARNING:"
1887 				" actual rotation func returned failure\n");
1888 	}
1889 }
1890 
1891 void
TFB_DrawCanvas_GetRotatedExtent(TFB_Canvas src_canvas,int angle,EXTENT * size)1892 TFB_DrawCanvas_GetRotatedExtent (TFB_Canvas src_canvas, int angle, EXTENT *size)
1893 {
1894 	int dstw, dsth;
1895 	SDL_Surface *src = src_canvas;
1896 
1897 	rotozoomSurfaceSize (src->w, src->h, angle, 1, &dstw, &dsth);
1898 	size->height = dsth;
1899 	size->width = dstw;
1900 }
1901 
1902 void
TFB_DrawCanvas_CopyRect(TFB_Canvas source,const RECT * srcRect,TFB_Canvas target,POINT dstPt)1903 TFB_DrawCanvas_CopyRect (TFB_Canvas source, const RECT *srcRect,
1904 		TFB_Canvas target, POINT dstPt)
1905 {
1906 	SDL_Rect sourceRect, targetRect;
1907 
1908 	if (source == 0 || target == 0)
1909 	{
1910 		log_add (log_Warning,
1911 				"ERROR: TFB_DrawCanvas_CopyRect passed null canvas ptr");
1912 		return;
1913 	}
1914 
1915 	sourceRect.x = srcRect->corner.x;
1916 	sourceRect.y = srcRect->corner.y;
1917 	sourceRect.w = srcRect->extent.width;
1918 	sourceRect.h = srcRect->extent.height;
1919 
1920 	targetRect.x = dstPt.x;
1921 	targetRect.y = dstPt.y;
1922 	// According to SDL docs, width and height are ignored, but
1923 	// we'll set them anyway, just in case.
1924 	targetRect.w = srcRect->extent.width;
1925 	targetRect.h = srcRect->extent.height;
1926 
1927 	SDL_BlitSurface (source, &sourceRect, target, &targetRect);
1928 }
1929 
1930 void
TFB_DrawCanvas_SetClipRect(TFB_Canvas canvas,const RECT * clipRect)1931 TFB_DrawCanvas_SetClipRect (TFB_Canvas canvas, const RECT *clipRect)
1932 {
1933 	if (canvas == 0)
1934 	{
1935 		log_add (log_Warning,
1936 				"ERROR: TFB_DrawCanvas_SetClipRect passed null canvas ptr");
1937 		return;
1938 	}
1939 
1940 	if (!clipRect)
1941 	{	// clipping disabled
1942 		SDL_SetClipRect (canvas, NULL);
1943 	}
1944 	else
1945 	{
1946 		SDL_Rect r;
1947 		r.x = clipRect->corner.x;
1948 		r.y = clipRect->corner.y;
1949 		r.w = clipRect->extent.width;
1950 		r.h = clipRect->extent.height;
1951 		SDL_SetClipRect (canvas, &r);
1952 	}
1953 }
1954 
1955 BOOLEAN
TFB_DrawCanvas_Intersect(TFB_Canvas canvas1,POINT c1org,TFB_Canvas canvas2,POINT c2org,const RECT * interRect)1956 TFB_DrawCanvas_Intersect (TFB_Canvas canvas1, POINT c1org,
1957 		TFB_Canvas canvas2, POINT c2org, const RECT *interRect)
1958 {
1959 	BOOLEAN ret = FALSE;
1960 	SDL_Surface *surf1 = canvas1;
1961 	SDL_Surface *surf2 = canvas2;
1962 	int x, y;
1963 	Uint32 s1key, s2key;
1964 	Uint32 s1mask, s2mask;
1965 	GetPixelFn getpixel1, getpixel2;
1966 
1967 	SDL_LockSurface (surf1);
1968 	SDL_LockSurface (surf2);
1969 
1970 	getpixel1 = getpixel_for (surf1);
1971 	getpixel2 = getpixel_for (surf2);
1972 
1973 	if (surf1->format->Amask)
1974 	{	// use alpha transparency info
1975 		s1mask = surf1->format->Amask;
1976 		// consider any not fully transparent pixel collidable
1977 		s1key = 0;
1978 	}
1979 	else
1980 	{	// colorkey transparency
1981 		Uint32 colorkey = 0;
1982 		TFB_GetColorKey(surf1, &colorkey);
1983 		s1mask = ~surf1->format->Amask;
1984 		s1key = colorkey & s1mask;
1985 	}
1986 
1987 	if (surf2->format->Amask)
1988 	{	// use alpha transparency info
1989 		s2mask = surf2->format->Amask;
1990 		// consider any not fully transparent pixel collidable
1991 		s2key = 0;
1992 	}
1993 	else
1994 	{	// colorkey transparency
1995 		Uint32 colorkey = 0;
1996 		TFB_GetColorKey(surf2, &colorkey);
1997 		s2mask = ~surf2->format->Amask;
1998 		s2key = colorkey & s2mask;
1999 	}
2000 
2001 	// convert surface origins to pixel offsets within
2002 	c1org.x = interRect->corner.x - c1org.x;
2003 	c1org.y = interRect->corner.y - c1org.y;
2004 	c2org.x = interRect->corner.x - c2org.x;
2005 	c2org.y = interRect->corner.y - c2org.y;
2006 
2007 	for (y = 0; y < interRect->extent.height; ++y)
2008 	{
2009 		for (x = 0; x < interRect->extent.width; ++x)
2010 		{
2011 			Uint32 p1 = getpixel1 (surf1, x + c1org.x, y + c1org.y) & s1mask;
2012 			Uint32 p2 = getpixel2 (surf2, x + c2org.x, y + c2org.y) & s2mask;
2013 
2014 			if (p1 != s1key && p2 != s2key)
2015 			{	// pixel collision
2016 				ret = TRUE;
2017 				break;
2018 			}
2019 		}
2020 	}
2021 
2022 	SDL_UnlockSurface (surf2);
2023 	SDL_UnlockSurface (surf1);
2024 
2025 	return ret;
2026 }
2027 
2028 // Read/write the canvas pixels in a Color format understood by the core.
2029 // The pixels array is assumed to be at least width * height large.
2030 // The pixels array can be wider/narrower or taller/shorter than the canvas,
2031 // and in that case, only the relevant pixels will be transfered.
2032 static BOOLEAN
TFB_DrawCanvas_TransferColors(TFB_Canvas canvas,BOOLEAN write,Color * pixels,int width,int height)2033 TFB_DrawCanvas_TransferColors (TFB_Canvas canvas, BOOLEAN write,
2034 		Color *pixels, int width, int height)
2035 {
2036 	SDL_Surface *surf = canvas;
2037 	SDL_PixelFormat *fmt;
2038 	GetPixelFn getpix;
2039 	PutPixelFn putpix;
2040 	int x, y, w, h;
2041 
2042 	if (canvas == 0)
2043 	{
2044 		log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferColors "
2045 				"passed null canvas");
2046 		return FALSE;
2047 	}
2048 
2049 	fmt = surf->format;
2050 	getpix = getpixel_for (surf);
2051 	putpix = putpixel_for (surf);
2052 
2053 	w = width < surf->w ? width : surf->w;
2054 	h = height < surf->h ? height : surf->h;
2055 
2056 	SDL_LockSurface (surf);
2057 
2058 	// This could be done faster if we assumed 32bpp surfaces
2059 	for (y = 0; y < h; ++y)
2060 	{
2061 		// pixels array pitch is width so as not to violate the interface
2062 		Color *c = pixels + y * width;
2063 
2064 		for (x = 0; x < w; ++x, ++c)
2065 		{
2066 			if (write)
2067 			{	// writing from data to surface
2068 				Uint32 p = SDL_MapRGBA (fmt, c->r, c->g, c->b, c->a);
2069 				putpix (surf, x, y, p);
2070 			}
2071 			else
2072 			{	// reading from surface to data
2073 				Uint32 p = getpix (surf, x, y);
2074 				SDL_GetRGBA (p, fmt, &c->r, &c->g, &c->b, &c->a);
2075 			}
2076 		}
2077 	}
2078 
2079 	SDL_UnlockSurface (surf);
2080 
2081 	return TRUE;
2082 }
2083 
2084 // Read the canvas pixels in a Color format understood by the core.
2085 // See TFB_DrawCanvas_TransferColors() for pixels array info
2086 BOOLEAN
TFB_DrawCanvas_GetPixelColors(TFB_Canvas canvas,Color * pixels,int width,int height)2087 TFB_DrawCanvas_GetPixelColors (TFB_Canvas canvas, Color *pixels,
2088 		int width, int height)
2089 {
2090 	return TFB_DrawCanvas_TransferColors (canvas, FALSE, pixels,
2091 			width, height);
2092 }
2093 
2094 // Write the canvas pixels from a Color format understood by the core.
2095 // See TFB_DrawCanvas_TransferColors() for pixels array info
2096 BOOLEAN
TFB_DrawCanvas_SetPixelColors(TFB_Canvas canvas,const Color * pixels,int width,int height)2097 TFB_DrawCanvas_SetPixelColors (TFB_Canvas canvas, const Color *pixels,
2098 		int width, int height)
2099 {
2100 	// unconst pixels, but it is safe -- it will not be written to
2101 	return TFB_DrawCanvas_TransferColors (canvas, TRUE, (Color *)pixels,
2102 			width, height);
2103 }
2104 
2105 // Read/write the indexed canvas pixels as palette indexes.
2106 // The data array is assumed to be at least width * height large.
2107 // The data array can be wider/narrower or taller/shorter than the canvas,
2108 // and in that case, only the relevant pixels will be transfered.
2109 static BOOLEAN
TFB_DrawCanvas_TransferIndexes(TFB_Canvas canvas,BOOLEAN write,BYTE * data,int width,int height)2110 TFB_DrawCanvas_TransferIndexes (TFB_Canvas canvas, BOOLEAN write,
2111 		BYTE *data, int width, int height)
2112 {
2113 	SDL_Surface *surf = canvas;
2114 	const SDL_PixelFormat *fmt;
2115 	int y, w, h;
2116 
2117 	if (canvas == 0)
2118 	{
2119 		log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes "
2120 				"passed null canvas");
2121 		return FALSE;
2122 	}
2123 	fmt = surf->format;
2124 	if (!TFB_DrawCanvas_IsPaletted (canvas) || fmt->BitsPerPixel != 8)
2125 	{
2126 		log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes "
2127 				"unimplemeted function: not an 8bpp indexed canvas");
2128 		return FALSE;
2129 	}
2130 
2131 	w = width < surf->w ? width : surf->w;
2132 	h = height < surf->h ? height : surf->h;
2133 
2134 	SDL_LockSurface (surf);
2135 
2136 	for (y = 0; y < h; ++y)
2137 	{
2138 		Uint8 *surf_p = (Uint8 *)surf->pixels + y * surf->pitch;
2139 		// pixels array pitch is width so as not to violate the interface
2140 		BYTE *data_p = data + y * width;
2141 
2142 		if (write)
2143 		{	// writing from data to surface
2144 			memcpy (surf_p, data_p, w * sizeof (BYTE));
2145 		}
2146 		else
2147 		{	// reading from surface to data
2148 			memcpy (data_p, surf_p, w * sizeof (BYTE));
2149 		}
2150 	}
2151 
2152 	SDL_UnlockSurface (surf);
2153 
2154 	return TRUE;
2155 }
2156 
2157 // Read the indexed canvas pixels as palette indexes.
2158 // See TFB_DrawCanvas_TransferIndexes() for data array info.
2159 BOOLEAN
TFB_DrawCanvas_GetPixelIndexes(TFB_Canvas canvas,BYTE * data,int width,int height)2160 TFB_DrawCanvas_GetPixelIndexes (TFB_Canvas canvas, BYTE *data,
2161 		int width, int height)
2162 {
2163 	return TFB_DrawCanvas_TransferIndexes (canvas, FALSE, data,
2164 			width, height);
2165 }
2166 
2167 // Write the indexed canvas pixels as palette indexes.
2168 // See TFB_DrawCanvas_TransferIndexes() for data array info.
2169 BOOLEAN
TFB_DrawCanvas_SetPixelIndexes(TFB_Canvas canvas,const BYTE * data,int width,int height)2170 TFB_DrawCanvas_SetPixelIndexes (TFB_Canvas canvas, const BYTE *data,
2171 		int width, int height)
2172 {
2173 	// unconst data, but it is safe -- it will not be written to
2174 	return TFB_DrawCanvas_TransferIndexes (canvas, TRUE, (BYTE *)data,
2175 			width, height);
2176 }
2177