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