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