1 /*
2  * Copyright (c) 2009-2013 Hypertriton, Inc. <http://hypertriton.com/>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <agar/core/core.h>
27 #include <agar/gui/surface.h>
28 #include <agar/gui/gui_math.h>
29 
30 #include <string.h>
31 
32 const char *agBlendFuncNames[] = {
33 	"dst+src",
34 	"src",
35 	"dst",
36 	"1-dst",
37 	"1-src",
38 	NULL
39 };
40 
41 AG_PixelFormat *agSurfaceFmt = NULL;  /* Recommended format for GUI surfaces */
42 
43 #define COMPUTE_SHIFTLOSS(mask, shift, loss) \
44 	shift = 0; \
45 	loss = 8; \
46 	if (mask) { \
47 		for (m = mask ; !(m & 0x01); m >>= 1) { \
48 			shift++; \
49 		}  \
50 		for (; (m & 0x01); m >>= 1) { \
51 			loss--; \
52 		} \
53 	}
54 
55 /* Specify a packed-pixel format from three 32-bit bitmasks. */
56 AG_PixelFormat *
AG_PixelFormatRGB(int bpp,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask)57 AG_PixelFormatRGB(int bpp, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask)
58 {
59 	AG_PixelFormat *pf;
60 	Uint32 m;
61 	Uint32 Amask = 0;
62 
63 	if ((pf = TryMalloc(sizeof(AG_PixelFormat))) == NULL) {
64 		return (NULL);
65 	}
66 	pf->BitsPerPixel = bpp;
67 	pf->BytesPerPixel = (bpp+7)/8;
68 	pf->colorkey = 0;
69 	pf->alpha = AG_ALPHA_OPAQUE;
70 	pf->palette = NULL;
71 	COMPUTE_SHIFTLOSS(Rmask, pf->Rshift, pf->Rloss);
72 	COMPUTE_SHIFTLOSS(Gmask, pf->Gshift, pf->Gloss);
73 	COMPUTE_SHIFTLOSS(Bmask, pf->Bshift, pf->Bloss);
74 	COMPUTE_SHIFTLOSS(Amask, pf->Ashift, pf->Aloss);
75 	pf->Rmask = Rmask;
76 	pf->Gmask = Gmask;
77 	pf->Bmask = Bmask;
78 	pf->Amask = 0;
79 	return (pf);
80 }
81 
82 /* Specify a packed-pixel format from four 32-bit bitmasks. */
83 AG_PixelFormat *
AG_PixelFormatRGBA(int bpp,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)84 AG_PixelFormatRGBA(int bpp, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
85     Uint32 Amask)
86 {
87 	AG_PixelFormat *pf;
88 	Uint32 m;
89 
90 	if ((pf = TryMalloc(sizeof(AG_PixelFormat))) == NULL) {
91 		return (NULL);
92 	}
93 	pf->BitsPerPixel = bpp;
94 	pf->BytesPerPixel = (bpp+7)/8;
95 	pf->colorkey = 0;
96 	pf->alpha = AG_ALPHA_OPAQUE;
97 	pf->palette = NULL;
98 	COMPUTE_SHIFTLOSS(Rmask, pf->Rshift, pf->Rloss);
99 	COMPUTE_SHIFTLOSS(Gmask, pf->Gshift, pf->Gloss);
100 	COMPUTE_SHIFTLOSS(Bmask, pf->Bshift, pf->Bloss);
101 	COMPUTE_SHIFTLOSS(Amask, pf->Ashift, pf->Aloss);
102 	pf->Rmask = Rmask;
103 	pf->Gmask = Gmask;
104 	pf->Bmask = Bmask;
105 	pf->Amask = Amask;
106 	return (pf);
107 }
108 
109 /*
110  * Specify an indexed pixel format. If bpp=2, the palette is initialized to
111  * [0 = white] and [1 = black], otherwise the palette is initialized to all
112  * black.
113  */
114 AG_PixelFormat *
AG_PixelFormatIndexed(int bpp)115 AG_PixelFormatIndexed(int bpp)
116 {
117 	AG_PixelFormat *pf;
118 	AG_Palette *pal;
119 
120 	if ((pf = TryMalloc(sizeof(AG_PixelFormat))) == NULL) {
121 		return (NULL);
122 	}
123 	pf->BitsPerPixel = bpp;
124 	pf->BytesPerPixel = (bpp+7)/8;
125 	pf->colorkey = 0;
126 	pf->alpha = AG_ALPHA_OPAQUE;
127 
128 	if ((pal = pf->palette = TryMalloc(sizeof(AG_Palette))) == NULL) {
129 		Free(pf);
130 		return (NULL);
131 	}
132 	pal->nColors = 1<<bpp;
133 	if ((pal->colors = TryMalloc(pal->nColors*sizeof(AG_Color))) == NULL) {
134 		Free(pf->palette);
135 		Free(pf);
136 		return (NULL);
137 	}
138 
139 	if (bpp == 2) {
140 		pal->colors[0].r = 255;
141 		pal->colors[0].g = 255;
142 		pal->colors[0].b = 255;
143 		pal->colors[1].r = 0;
144 		pal->colors[1].g = 0;
145 		pal->colors[1].b = 0;
146 	} else {
147 		memset(pal->colors, 0, pal->nColors*sizeof(AG_Color));
148 	}
149 
150 	pf->Rmask = pf->Gmask = pf->Bmask = pf->Amask = 0;
151 	pf->Rloss = pf->Gloss = pf->Bloss = pf->Aloss = 8;
152 	pf->Rshift = pf->Gshift = pf->Bshift = pf->Ashift = 0;
153 	return (pf);
154 }
155 
156 /* Return a newly-allocated duplicate an AG_PixelFormat structure. */
157 AG_PixelFormat *
AG_PixelFormatDup(const AG_PixelFormat * pf)158 AG_PixelFormatDup(const AG_PixelFormat *pf)
159 {
160 	AG_PixelFormat *pfd;
161 
162 	if ((pfd = TryMalloc(sizeof(AG_PixelFormat))) == NULL) {
163 		return (NULL);
164 	}
165 	if (pf->palette != NULL) {
166 		if ((pfd->palette = TryMalloc(sizeof(AG_Palette))) == NULL) {
167 			goto fail;
168 		}
169 		if ((pfd->palette->colors =
170 		    TryMalloc(pf->palette->nColors*sizeof(AG_Color))) == NULL) {
171 			Free(pfd->palette);
172 			goto fail;
173 		}
174 		pfd->palette->nColors = pf->palette->nColors;
175 		memcpy(pfd->palette->colors, pf->palette->colors,
176 		    pf->palette->nColors*sizeof(AG_Color));
177 	} else {
178 		pfd->palette = NULL;
179 	}
180 	pfd->BitsPerPixel = pf->BitsPerPixel;
181 	pfd->BytesPerPixel = pf->BytesPerPixel;
182 	pfd->colorkey = pf->colorkey;
183 	pfd->alpha = pf->alpha;
184 	pfd->Rloss = pf->Rloss;
185 	pfd->Gloss = pf->Gloss;
186 	pfd->Bloss = pf->Bloss;
187 	pfd->Aloss = pf->Aloss;
188 	pfd->Rshift = pf->Rshift;
189 	pfd->Gshift = pf->Gshift;
190 	pfd->Bshift = pf->Bshift;
191 	pfd->Ashift = pf->Ashift;
192 	pfd->Rmask = pf->Rmask;
193 	pfd->Gmask = pf->Gmask;
194 	pfd->Bmask = pf->Bmask;
195 	pfd->Amask = pf->Amask;
196 	return (pfd);
197 fail:
198 	Free(pfd);
199 	return (NULL);
200 }
201 
202 /* Release an AG_PixelFormat structure. */
203 void
AG_PixelFormatFree(AG_PixelFormat * pf)204 AG_PixelFormatFree(AG_PixelFormat *pf)
205 {
206 	if (pf->palette != NULL) {
207 		Free(pf->palette->colors);
208 		free(pf->palette);
209 	}
210 	free(pf);
211 }
212 
213 /* Compare two palettes. */
214 int
AG_PixelFormatComparePalettes(const AG_Palette * pal1,const AG_Palette * pal2)215 AG_PixelFormatComparePalettes(const AG_Palette *pal1, const AG_Palette *pal2)
216 {
217 	if (pal1->nColors != pal2->nColors) {
218 		return (1);
219 	}
220 	return memcmp(pal1->colors, pal2->colors,
221 	              pal1->nColors*sizeof(AG_Color));
222 }
223 
224 #undef COMPUTE_SHIFTLOSS
225 
226 /* Create a new surface of the specified pixel format. */
227 AG_Surface *
AG_SurfaceNew(enum ag_surface_type type,Uint w,Uint h,const AG_PixelFormat * pf,Uint flags)228 AG_SurfaceNew(enum ag_surface_type type, Uint w, Uint h,
229     const AG_PixelFormat *pf, Uint flags)
230 {
231 	AG_Surface *s;
232 	Uint pitch;
233 
234 	if ((s = TryMalloc(sizeof(AG_Surface))) == NULL) {
235 		return (NULL);
236 	}
237 	if ((s->format = AG_PixelFormatDup(pf)) == NULL) {
238 		Free(s);
239 		return (NULL);
240 	}
241 	s->type = type;
242 	s->flags = flags;
243 	s->w = w;
244 	s->h = h;
245 
246 	pitch = w*pf->BytesPerPixel;		/* 4-byte aligned */
247 	switch (pf->BitsPerPixel) {
248 	case 1:
249 		pitch = (pitch + 7)/8;
250 		break;
251 	case 4:
252 		pitch = (pitch + 1)/2;
253 		break;
254 	}
255 	s->pitch = (pitch + 3) & ~3;
256 	s->padding = s->pitch - w*pf->BytesPerPixel;
257 	s->clipRect = AG_RECT(0,0,w,h);
258 
259 	if (h*s->pitch > 0) {
260 		if ((s->pixels = TryMalloc(h*s->pitch)) == NULL)
261 			goto fail;
262 	} else {
263 		s->pixels = NULL;
264 	}
265 	return (s);
266 fail:
267 	AG_PixelFormatFree(s->format);
268 	Free(s);
269 	return (NULL);
270 }
271 
272 /* Create an empty surface. */
273 AG_Surface *
AG_SurfaceEmpty(void)274 AG_SurfaceEmpty(void)
275 {
276 	return AG_SurfaceNew(AG_SURFACE_PACKED, 0,0, agSurfaceFmt, 0);
277 }
278 
279 /* Create a new color-index surface of given dimensions and depth. */
280 AG_Surface *
AG_SurfaceIndexed(Uint w,Uint h,int bpp,Uint flags)281 AG_SurfaceIndexed(Uint w, Uint h, int bpp, Uint flags)
282 {
283 	AG_PixelFormat *pf;
284 	AG_Surface *s;
285 
286 	if ((pf = AG_PixelFormatIndexed(bpp)) == NULL) {
287 		return (NULL);
288 	}
289 	s = AG_SurfaceNew(AG_SURFACE_INDEXED, w,h, pf, 0);
290 	AG_PixelFormatFree(pf);
291 	return (s);
292 }
293 
294 /* Create a new packed-pixel surface with the specified RGB pixel format. */
295 AG_Surface *
AG_SurfaceRGB(Uint w,Uint h,int bpp,Uint flags,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask)296 AG_SurfaceRGB(Uint w, Uint h, int bpp, Uint flags, Uint32 Rmask, Uint32 Gmask,
297     Uint32 Bmask)
298 {
299 	AG_PixelFormat *pf;
300 	AG_Surface *s;
301 
302 	if ((pf = AG_PixelFormatRGB(bpp, Rmask, Gmask, Bmask)) == NULL) {
303 		return (NULL);
304 	}
305 	s = AG_SurfaceNew(AG_SURFACE_PACKED, w,h, pf, 0);
306 	AG_PixelFormatFree(pf);
307 	return (s);
308 }
309 
310 /*
311  * Create a new packed-pixel surface with the specified RGBA pixel format.
312  * The SRCALPHA flag is set implicitely.
313  */
314 AG_Surface *
AG_SurfaceRGBA(Uint w,Uint h,int bpp,Uint flags,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)315 AG_SurfaceRGBA(Uint w, Uint h, int bpp, Uint flags, Uint32 Rmask, Uint32 Gmask,
316     Uint32 Bmask, Uint32 Amask)
317 {
318 	AG_PixelFormat *pf;
319 	AG_Surface *s;
320 
321 	if ((pf = AG_PixelFormatRGBA(bpp, Rmask, Gmask, Bmask, Amask)) == NULL) {
322 		return (NULL);
323 	}
324 	s = AG_SurfaceNew(AG_SURFACE_PACKED, w,h, pf, AG_SRCALPHA);
325 	AG_PixelFormatFree(pf);
326 	return (s);
327 }
328 
329 /* Create a new surface from pixel data in the specified packed RGB format. */
330 AG_Surface *
AG_SurfaceFromPixelsRGB(const void * pixels,Uint w,Uint h,int bpp,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask)331 AG_SurfaceFromPixelsRGB(const void *pixels, Uint w, Uint h, int bpp,
332     Uint32 Rmask, Uint32 Gmask, Uint32 Bmask)
333 {
334 	AG_PixelFormat *pf;
335 	AG_Surface *s;
336 
337 	if ((pf = AG_PixelFormatRGB(bpp, Rmask, Gmask, Bmask)) == NULL) {
338 		return (NULL);
339 	}
340 	s = AG_SurfaceNew(AG_SURFACE_PACKED, w,h, pf, 0);
341 	memcpy(s->pixels, pixels, h*s->pitch);
342 	AG_PixelFormatFree(pf);
343 	return (s);
344 }
345 
346 /*
347  * Create a new surface from pixel data in the specified packed RGBA format.
348  * The SRCALPHA flag is set implicitely.
349  */
350 AG_Surface *
AG_SurfaceFromPixelsRGBA(const void * pixels,Uint w,Uint h,int bpp,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)351 AG_SurfaceFromPixelsRGBA(const void *pixels, Uint w, Uint h, int bpp,
352     Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
353 {
354 	AG_PixelFormat *pf;
355 	AG_Surface *s;
356 
357 	if ((pf = AG_PixelFormatRGBA(bpp, Rmask, Gmask, Bmask, Amask)) == NULL) {
358 		return (NULL);
359 	}
360 	s = AG_SurfaceNew(AG_SURFACE_PACKED, w,h, pf, AG_SRCALPHA);
361 	memcpy(s->pixels, pixels, h*s->pitch);
362 	AG_PixelFormatFree(pf);
363 	return (s);
364 }
365 
366 /* Load a surface from an image file. */
367 AG_Surface *
AG_SurfaceFromFile(const char * path)368 AG_SurfaceFromFile(const char *path)
369 {
370 	AG_Surface *su;
371 	const char *ext;
372 
373 	if ((ext = strrchr(path, '.')) == NULL) {
374 		AG_SetError("Invalid filename");
375 		return (NULL);
376 	}
377 	if (Strcasecmp(ext, ".bmp") == 0) {
378 		su = AG_SurfaceFromBMP(path);
379 	} else if (Strcasecmp(ext, ".png") == 0) {
380 		su = AG_SurfaceFromPNG(path);
381 	} else if (Strcasecmp(ext, ".jpg") == 0 || Strcasecmp(ext, ".jpeg") == 0) {
382 		su = AG_SurfaceFromJPEG(path);
383 	} else {
384 		AG_SetError(_("Unknown image extension: %s"), ext);
385 		return (NULL);
386 	}
387 	return (su);
388 }
389 
390 /* Export surface to an image file (format determined by extension). */
391 int
AG_SurfaceExportFile(const AG_Surface * su,const char * path)392 AG_SurfaceExportFile(const AG_Surface *su, const char *path)
393 {
394 	const char *ext;
395 
396 	if ((ext = strrchr(path, '.')) == NULL) {
397 		AG_SetError("Invalid filename");
398 		return (-1);
399 	}
400 	if (Strcasecmp(ext, ".bmp") == 0) {
401 		return AG_SurfaceExportBMP(su, path);
402 	} else if (Strcasecmp(ext, ".png") == 0) {
403 		return AG_SurfaceExportPNG(su, path, 0);
404 	} else if (Strcasecmp(ext, ".jpg") == 0 || Strcasecmp(ext, ".jpeg") == 0) {
405 		return AG_SurfaceExportJPEG(su, path, 100, 0);
406 	} else {
407 		AG_SetError(_("Unknown image extension: %s"), ext);
408 		return (-1);
409 	}
410 	return (0);
411 }
412 
413 /*
414  * Create a new surface suitable to be used as an OpenGL texture. The
415  * returned surface size may be different from requested (unless the
416  * NPOT extension is available).
417  */
418 AG_Surface *
AG_SurfaceStdGL(Uint rw,Uint rh)419 AG_SurfaceStdGL(Uint rw, Uint rh)
420 {
421 	AG_Surface *gsu;
422 	int w, h;
423 
424 	/* TODO check for GL_ARB_texture_non_power_of_two. */
425 	w = PowOf2i(rw);
426 	h = PowOf2i(rh);
427 	gsu = AG_SurfaceRGBA(w, h, 32, 0,
428 #if AG_BYTEORDER == AG_BIG_ENDIAN
429 		0xff000000,
430 		0x00ff0000,
431 		0x0000ff00,
432 		0x000000ff
433 #else
434 		0x000000ff,
435 		0x0000ff00,
436 		0x00ff0000,
437 		0xff000000
438 #endif
439 	);
440 	if (gsu == NULL) {
441 		return (NULL);
442 	}
443 	gsu->flags |= AG_SURFACE_GLTEXTURE;
444 	return (gsu);
445 }
446 
447 /* Set one or more entries in an indexed surface's palette. */
448 int
AG_SurfaceSetPalette(AG_Surface * su,AG_Color * c,Uint offs,Uint count)449 AG_SurfaceSetPalette(AG_Surface *su, AG_Color *c, Uint offs, Uint count)
450 {
451 	Uint i;
452 
453 	if (su->type != AG_SURFACE_INDEXED) {
454 		AG_SetError("Not an indexed surface");
455 		return (-1);
456 	}
457 	if (offs >= su->format->palette->nColors ||
458 	    offs+count >= su->format->palette->nColors) {
459 		AG_SetError("Bad palette offset/count");
460 		return (-1);
461 	}
462 	for (i = 0; i < count; i++) {
463 		su->format->palette->colors[offs+i] = c[i];
464 	}
465 	return (0);
466 }
467 
468 /*
469  * Return a newly-allocated duplicate of a surface.
470  * The source surface must be locked.
471  */
472 AG_Surface *
AG_SurfaceDup(const AG_Surface * ss)473 AG_SurfaceDup(const AG_Surface *ss)
474 {
475 	AG_Surface *s;
476 
477 	s = AG_SurfaceNew(ss->type,
478 	    ss->w, ss->h, ss->format,
479 	    (ss->flags & AG_SAVED_SURFACE_FLAGS));
480 	if (s == NULL) {
481 		return (NULL);
482 	}
483 	memcpy(s->pixels, ss->pixels, ss->h*ss->pitch);
484 	return (s);
485 }
486 
487 /* Return a newly-allocated duplicate of a surface, in specified format. */
488 AG_Surface *
AG_SurfaceConvert(const AG_Surface * ss,const AG_PixelFormat * pf)489 AG_SurfaceConvert(const AG_Surface *ss, const AG_PixelFormat *pf)
490 {
491 	AG_Surface *ds;
492 
493 	ds = AG_SurfaceNew(ss->type,
494 	    ss->w, ss->h, pf,
495 	    (ss->flags & AG_SAVED_SURFACE_FLAGS));
496 	AG_SurfaceCopy(ds, ss);
497 	return (ds);
498 }
499 
500 /*
501  * Copy pixel data from a source to a destination surface. Perform
502  * conversion if pixel format differs. Perform clipping if dimensions
503  * differ. Ignore the clipping rectangle and alpha/colorkey settings of
504  * the target surface.
505  */
506 void
AG_SurfaceCopy(AG_Surface * ds,const AG_Surface * ss)507 AG_SurfaceCopy(AG_Surface *ds, const AG_Surface *ss)
508 {
509 	int w, h, x, y, padDst, padSrc;
510 	const Uint8 *pSrc;
511 	Uint8 *pDst;
512 
513 	if (ds->w > ss->w) {
514 		w = ss->w;
515 		padDst = (ds->w - ss->w)*ds->format->BytesPerPixel;
516 		padSrc = 0;
517 	} else if (ds->w < ss->w) {
518 		w = ds->w;
519 		padDst = 0;
520 		padSrc = (ss->w - ds->w)*ss->format->BytesPerPixel;
521 	} else {
522 		w = ds->w;
523 		padSrc = 0;
524 		padDst = 0;
525 	}
526 	padSrc += ss->padding;
527 	padDst += ds->padding;
528 	h = MIN(ss->h, ds->h);
529 
530 	pSrc = (Uint8 *)ss->pixels;
531 	pDst = (Uint8 *)ds->pixels;
532 
533 	if (AG_PixelFormatCompare(ss->format, ds->format) == 0) {
534 		for (y = 0; y < h; y++) {
535 			memcpy(pDst, pSrc, w*ds->format->BytesPerPixel);
536 			pDst += w*ds->format->BytesPerPixel + padDst;
537 			pSrc += w*ss->format->BytesPerPixel + padSrc;
538 		}
539 	} else {					/* Format conversion */
540 		Uint32 px;
541 		AG_Color C;
542 
543 		for (y = 0; y < h; y++) {
544 			for (x = 0; x < w; x++) {
545 				px = AG_GET_PIXEL(ss,pSrc);
546 				C = AG_GetColorRGBA(px, ss->format);
547 				AG_PUT_PIXEL(ds,pDst,
548 				    AG_MapColorRGBA(ds->format, C));
549 				pSrc += ss->format->BytesPerPixel;
550 				pDst += ds->format->BytesPerPixel;
551 			}
552 			pDst += padDst;
553 			pSrc += padSrc;
554 		}
555 	}
556 }
557 
558 /*
559  * Copy a region of pixels srcRect from source surface ss to destination
560  * surface ds, at destination coordinates xDst,yDst. It is safe to exceed
561  * the dimensions of ds. Unlike AG_SurfaceCopy(), blending and colorkey
562  * tests are done. If srcRect is passed NULL, the entire surface is copied.
563  */
564 void
AG_SurfaceBlit(const AG_Surface * ss,const AG_Rect * srcRect,AG_Surface * ds,int xDst,int yDst)565 AG_SurfaceBlit(const AG_Surface *ss, const AG_Rect *srcRect, AG_Surface *ds,
566     int xDst, int yDst)
567 {
568 	Uint32 pixel;
569 	Uint8 *pSrc, *pDst;
570 	AG_Color C;
571 	AG_Rect sr, dr;
572 	Uint x, y;
573 
574 	/* Compute the effective source and destination rectangles. */
575 	if (srcRect != NULL) {
576 		sr = *srcRect;
577 		if (sr.x < 0) { sr.x = 0; }
578 		if (sr.y < 0) { sr.y = 0; }
579 		if (sr.x+sr.w >= ss->w) { sr.w = ss->w - sr.x; }
580 		if (sr.y+sr.h >= ss->h) { sr.h = ss->h - sr.y; }
581 	} else {
582 		sr.x = 0;
583 		sr.y = 0;
584 		sr.w = ss->w;
585 		sr.h = ss->h;
586 	}
587 	dr.x = MAX(xDst, ds->clipRect.x);
588 	dr.y = MAX(yDst, ds->clipRect.y);
589 	dr.w = (dr.x+sr.w > ds->clipRect.x+ds->clipRect.w) ?
590 	        (ds->clipRect.x+ds->clipRect.w - dr.x) : sr.w;
591 	dr.h = (dr.y+sr.h > ds->clipRect.y+ds->clipRect.h) ?
592 	        (ds->clipRect.y+ds->clipRect.h - dr.y) : sr.h;
593 
594 	/* XXX TODO optimized cases */
595 	for (y = 0; y < dr.h; y++) {
596 		pSrc = (Uint8 *)ss->pixels + (sr.y+y)*ss->pitch +
597 		    sr.x*ss->format->BytesPerPixel;
598 		pDst = (Uint8 *)ds->pixels + (dr.y+y)*ds->pitch +
599 		    dr.x*ds->format->BytesPerPixel;
600 		for (x = 0; x < dr.w; x++) {
601 			pixel = AG_GET_PIXEL(ss, pSrc);
602 			if ((ss->flags & AG_SRCCOLORKEY) &&
603 			    (ss->format->colorkey == pixel)) {
604 				pSrc += ss->format->BytesPerPixel;
605 				pDst += ds->format->BytesPerPixel;
606 				continue;
607 			}
608 			C = AG_GetColorRGBA(pixel, ss->format);
609 			if (ss->format->alpha != AG_ALPHA_OPAQUE) {
610 				/* Apply per-surface alpha */
611 				C.a *= ss->format->alpha/255;
612 				AG_SurfaceBlendPixel(ds, pDst, C, AG_ALPHA_SRC);
613 			} else {
614 				if ((C.a != AG_ALPHA_OPAQUE) &&
615 				    (ss->flags & AG_SRCALPHA)) {
616 					AG_SurfaceBlendPixel(ds, pDst, C, AG_ALPHA_SRC);
617 				} else {
618 					AG_PUT_PIXEL(ds, pDst,
619 					    AG_MapColorRGB(ds->format, C));
620 				}
621 			}
622 			pSrc += ss->format->BytesPerPixel;
623 			pDst += ds->format->BytesPerPixel;
624 		}
625 	}
626 }
627 
628 /* Resize a surface; pixels are left uninitialized. */
629 int
AG_SurfaceResize(AG_Surface * s,Uint w,Uint h)630 AG_SurfaceResize(AG_Surface *s, Uint w, Uint h)
631 {
632 	Uint8 *pixelsNew;
633 	int pitchNew = w*s->format->BytesPerPixel;
634 
635 	if ((pixelsNew = TryRealloc(s->pixels, h*pitchNew)) == NULL) {
636 		return (-1);
637 	}
638 	s->pixels = pixelsNew;
639 	s->pitch = pitchNew;
640 	s->w = w;
641 	s->h = h;
642 	s->clipRect = AG_RECT(0,0,w,h);
643 	return (0);
644 }
645 
646 /* Free the specified surface. */
647 void
AG_SurfaceFree(AG_Surface * s)648 AG_SurfaceFree(AG_Surface *s)
649 {
650 	AG_PixelFormatFree(s->format);
651 	Free(s->pixels);
652 	Free(s);
653 }
654 
655 /*
656  * Blend the specified components with the pixel at s:[x,y], using the
657  * given alpha function. No clipping is done.
658  */
659 void
AG_SurfaceBlendPixel(AG_Surface * s,Uint8 * pDst,AG_Color Cnew,AG_BlendFn fn)660 AG_SurfaceBlendPixel(AG_Surface *s, Uint8 *pDst, AG_Color Cnew, AG_BlendFn fn)
661 {
662 	Uint32 pxDst;
663 	AG_Color Cdst;
664 /*	Uint8 a; */
665 
666 	pxDst = AG_GET_PIXEL(s, pDst);
667 	if ((s->flags & AG_SRCCOLORKEY) && (pxDst == s->format->colorkey)) {
668 	 	AG_SurfacePutPixel(s, pDst,
669 		    AG_MapColorRGBA(s->format, Cnew));
670 	} else {
671 		Cdst = AG_GetColorRGBA(pxDst, s->format);
672 #if 0
673 		switch (fn) {
674 		case AG_ALPHA_DST:
675 			a = Cdst.a;
676 			break;
677 		case AG_ALPHA_SRC:
678 			a = Cnew.a;
679 			break;
680 		case AG_ALPHA_ZERO:
681 			a = 0;
682 			break;
683 		case AG_ALPHA_OVERLAY:
684 			a = (Uint8)((Cdst.a+Cnew.a) > 255) ? 255 :
685 			            (Cdst.a+Cnew.a);
686 			break;
687 		case AG_ALPHA_ONE_MINUS_DST:
688 			a = 255-Cdst.a;
689 			break;
690 		case AG_ALPHA_ONE_MINUS_SRC:
691 			a = 255-Cnew.a;
692 			break;
693 		case AG_ALPHA_ONE:
694 		default:
695 			a = 255;
696 			break;
697 		}
698 #endif
699 		AG_SurfacePutPixel(s, pDst,
700 		    AG_MapPixelRGBA(s->format,
701 		    (((Cnew.r - Cdst.r)*Cnew.a) >> 8) + Cdst.r,
702 		    (((Cnew.g - Cdst.g)*Cnew.a) >> 8) + Cdst.g,
703 		    (((Cnew.b - Cdst.b)*Cnew.a) >> 8) + Cdst.b,
704 		    Cdst.a));
705 	}
706 }
707 
708 /*
709  * Obtain the hue/saturation/value of a given RGB triplet.
710  * Note that the hue is lost as saturation approaches 0.
711  */
712 void
AG_RGB2HSV(Uint8 r,Uint8 g,Uint8 b,float * h,float * s,float * v)713 AG_RGB2HSV(Uint8 r, Uint8 g, Uint8 b, float *h, float *s, float *v)
714 {
715 	float vR, vG, vB;
716 	float vMin, vMax, deltaMax;
717 	float deltaR, deltaG, deltaB;
718 
719 	vR = (float)r/255.0F;
720 	vG = (float)g/255.0F;
721 	vB = (float)b/255.0F;
722 
723 	vMin = MIN3(vR, vG, vB);
724 	vMax = MAX3(vR, vG, vB);
725 	deltaMax = vMax - vMin;
726 	*v = vMax;
727 
728 	if (deltaMax == 0.0) {
729 		/* This is a gray color (zero hue, no saturation). */
730 		*h = 0.0;
731 		*s = 0.0;
732 	} else {
733 		*s = deltaMax / vMax;
734 		deltaR = ((vMax - vR)/6.0F + deltaMax/2.0F) / deltaMax;
735 		deltaG = ((vMax - vG)/6.0F + deltaMax/2.0F) / deltaMax;
736 		deltaB = ((vMax - vB)/6.0F + deltaMax/2.0F) / deltaMax;
737 
738 		if (vR == vMax) {
739 			*h = (deltaB - deltaG)*360.0F;
740 		} else if (vG == vMax) {
741 			*h = 120.0F + (deltaR - deltaB)*360.0F;	/* 1/3 */
742 		} else if (vB == vMax) {
743 			*h = 240.0F + (deltaG - deltaR)*360.0F;	/* 2/3 */
744 		}
745 
746 		if (*h < 0.0F)		(*h)++;
747 		if (*h > 360.0F)	(*h)--;
748 	}
749 }
750 
751 /* Convert hue/saturation/value to RGB. */
752 void
AG_HSV2RGB(float h,float s,float v,Uint8 * r,Uint8 * g,Uint8 * b)753 AG_HSV2RGB(float h, float s, float v, Uint8 *r, Uint8 *g, Uint8 *b)
754 {
755 	float var[3];
756 	float vR, vG, vB, hv;
757 	int iv;
758 
759 	if (s == 0.0) {
760 		*r = (Uint8)v*255;
761 		*g = (Uint8)v*255;
762 		*b = (Uint8)v*255;
763 		return;
764 	}
765 
766 	hv = h/60.0F;
767 	iv = Floor(hv);
768 	var[0] = v * (1.0F - s);
769 	var[1] = v * (1.0F - s*(hv - iv));
770 	var[2] = v * (1.0F - s*(1.0F - (hv - iv)));
771 
772 	switch (iv) {
773 	case 0:		vR = v;		vG = var[2];	vB = var[0];	break;
774 	case 1:		vR = var[1];	vG = v;		vB = var[0];	break;
775 	case 2:		vR = var[0];	vG = v;		vB = var[2];	break;
776 	case 3:		vR = var[0];	vG = var[1];	vB = v;		break;
777 	case 4:		vR = var[2];	vG = var[0];	vB = v;		break;
778 	default:	vR = v;		vG = var[0];	vB = var[1];	break;
779 	}
780 
781 	*r = vR*255;
782 	*g = vG*255;
783 	*b = vB*255;
784 }
785 
786 /*
787  * Allocate a new surface containing a pixmap of ss scaled to wxh.
788  * XXX TODO optimize; filtering
789  */
790 int
AG_ScaleSurface(const AG_Surface * ss,Uint16 w,Uint16 h,AG_Surface ** ds)791 AG_ScaleSurface(const AG_Surface *ss, Uint16 w, Uint16 h, AG_Surface **ds)
792 {
793 	Uint8 *pDst;
794 	int x, y;
795 	int sameFormat;
796 
797 	if (*ds == NULL) {
798 		*ds = AG_SurfaceNew(
799 		    AG_SURFACE_PACKED,
800 		    w, h, ss->format,
801 		    ss->flags & (AG_SRCALPHA|AG_SRCCOLORKEY));
802 		if (*ds == NULL) {
803 			return (-1);
804 		}
805 		(*ds)->format->alpha = ss->format->alpha;
806 		(*ds)->format->colorkey = ss->format->colorkey;
807 		sameFormat = 1;
808 	} else {
809 		//sameFormat = !AG_PixelFormatCompare((*ds)->format, ss->format);
810 		sameFormat = 0;
811 	}
812 
813 	if (ss->w == w && ss->h == h) {
814 		AG_SurfaceCopy(*ds, ss);
815 		return (0);
816 	}
817 
818 	pDst = (Uint8 *)(*ds)->pixels;
819 	for (y = 0; y < (*ds)->h; y++) {
820 		for (x = 0; x < (*ds)->w; x++) {
821 			Uint8 *pSrc = (Uint8 *)ss->pixels +
822 			    (y*ss->h/(*ds)->h)*ss->pitch +
823 			    (x*ss->w/(*ds)->w)*ss->format->BytesPerPixel;
824 			Uint32 pxSrc, pxDst;
825 			AG_Color C;
826 
827 			pxSrc = AG_GET_PIXEL(ss,pSrc);
828 			if (sameFormat) {
829 				pxDst = pxSrc;
830 			} else {
831 				C = AG_GetColorRGBA(pxSrc, ss->format);
832 				pxDst = AG_MapColorRGBA((*ds)->format, C);
833 			}
834 			AG_SurfacePutPixel((*ds), pDst, pxDst);
835 			pDst += (*ds)->format->BytesPerPixel;
836 		}
837 	}
838 	return (0);
839 }
840 
841 /* Set the alpha value of all pixels in a surface where a != 0. */
842 void
AG_SetAlphaPixels(AG_Surface * su,Uint8 alpha)843 AG_SetAlphaPixels(AG_Surface *su, Uint8 alpha)
844 {
845 	Uint8 *pDst = (Uint8 *)su->pixels;
846 	int x, y;
847 	AG_Color C;
848 
849 	for (y = 0; y < su->h; y++) {
850 		for (x = 0; x < su->w; x++) {
851 			/* XXX unnecessary conversion */
852 			C = AG_GetColorRGBA(AG_GET_PIXEL(su,pDst), su->format);
853 			if (C.a != 0) { C.a = alpha; }
854 			AG_SurfacePutPixel(su, pDst,
855 			    AG_MapColorRGBA(su->format, C));
856 			pDst += su->format->BytesPerPixel;
857 		}
858 	}
859 }
860 
861 /* Fill a rectangle with pixels of the specified color. */
862 void
AG_FillRect(AG_Surface * su,const AG_Rect * rDst,AG_Color C)863 AG_FillRect(AG_Surface *su, const AG_Rect *rDst, AG_Color C)
864 {
865 	int x, y;
866 	Uint32 px;
867 	AG_Rect r;
868 
869 	if (rDst != NULL) {
870 		r = *rDst;
871 		if (r.x < su->clipRect.x) { r.x = su->clipRect.x; }
872 		if (r.y < su->clipRect.y) { r.y = su->clipRect.y; }
873 		if (r.x+r.w >= su->clipRect.x+su->clipRect.w)
874 			r.w = su->clipRect.x+su->clipRect.w - r.x;
875 		if (r.y+r.h >= su->clipRect.y+su->clipRect.h)
876 			r.h = su->clipRect.y+su->clipRect.h - r.y;
877 	} else {
878 		r = su->clipRect;
879 	}
880 	px = AG_MapColorRGBA(su->format, C);
881 
882 	/* XXX TODO optimize */
883 	for (y = 0; y < r.h; y++) {
884 		for (x = 0; x < r.w; x++) {
885 			AG_PUT_PIXEL2(su,
886 			    r.x + x,
887 			    r.y + y,
888 			    px);
889 		}
890 	}
891 }
892 
893 /* Called by AG_MapPixelRGB() for color-index surfaces. */
894 Uint32
AG_MapPixelIndexedRGB(const AG_PixelFormat * pf,Uint8 r,Uint8 g,Uint8 b)895 AG_MapPixelIndexedRGB(const AG_PixelFormat *pf, Uint8 r, Uint8 g, Uint8 b)
896 {
897 	Uint i, iMin = 0;
898 	int err, errMin = 255*3;
899 
900 	for (i = 0; i < pf->palette->nColors; i++) {
901 		AG_Color *C = &pf->palette->colors[i];
902 
903 		err = Fabs(C->r - r) + Fabs(C->g - g) + Fabs(C->b - b);
904 		if (err < errMin) {
905 			errMin = err;
906 			iMin = i;
907 		}
908 	}
909 	return (Uint32)iMin;
910 }
911 
912 /* Called by AG_MapPixelRGBA() for color-index surfaces. */
913 Uint32
AG_MapPixelIndexedRGBA(const AG_PixelFormat * pf,Uint8 r,Uint8 g,Uint8 b,Uint8 a)914 AG_MapPixelIndexedRGBA(const AG_PixelFormat *pf, Uint8 r, Uint8 g, Uint8 b,
915     Uint8 a)
916 {
917 	Uint i, iMin = 0;
918 	int err, errMin = 255*4;
919 
920 	for (i = 0; i < pf->palette->nColors; i++) {
921 		AG_Color *C = &pf->palette->colors[i];
922 
923 		err = Fabs(C->r - r) + Fabs(C->g - g) + Fabs(C->b - b) +
924 		      Fabs(C->a - a);
925 		if (err < errMin) {
926 			errMin = err;
927 			iMin = i;
928 		}
929 	}
930 	return (Uint32)iMin;
931 }
932 
933 #ifdef AG_LEGACY
AG_SurfaceLock(AG_Surface * su)934 void        AG_SurfaceLock(AG_Surface *su) { /* No-op */ }
AG_SurfaceUnlock(AG_Surface * su)935 void        AG_SurfaceUnlock(AG_Surface *su) { /* No-op */ }
AG_MapRGB(const AG_PixelFormat * pf,Uint8 r,Uint8 g,Uint8 b)936 Uint32      AG_MapRGB(const AG_PixelFormat *pf, Uint8 r, Uint8 g, Uint8 b) { return AG_MapPixelRGB(pf, r,g,b); }
AG_MapRGBA(const AG_PixelFormat * pf,Uint8 r,Uint8 g,Uint8 b,Uint8 a)937 Uint32      AG_MapRGBA(const AG_PixelFormat *pf, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { return AG_MapPixelRGBA(pf, r,g,b,a); }
AG_DupSurface(AG_Surface * su)938 AG_Surface *AG_DupSurface(AG_Surface *su) { return AG_SurfaceDup((const AG_Surface *)su); }
AG_GetRGB(Uint32 px,const AG_PixelFormat * pf,Uint8 * r,Uint8 * g,Uint8 * b)939 void        AG_GetRGB(Uint32 px, const AG_PixelFormat *pf, Uint8 *r, Uint8 *g, Uint8 *b) { AG_GetPixelRGB(px, pf, r,g,b); }
AG_GetRGBA(Uint32 px,const AG_PixelFormat * pf,Uint8 * r,Uint8 * g,Uint8 * b,Uint8 * a)940 void        AG_GetRGBA(Uint32 px, const AG_PixelFormat *pf, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) { AG_GetPixelRGBA(px, pf, r,g,b,a); }
AG_SamePixelFmt(const AG_Surface * s1,const AG_Surface * s2)941 int         AG_SamePixelFmt(const AG_Surface *s1, const AG_Surface *s2) { return (AG_PixelFormatCompare(s1->format, s2->format)) == 0; }
942 #endif /* AG_LEGACY */
943