1 /******************************************************************************
2  *  Warmux is a convivial mass murder game.
3  *  Copyright (C) 2001-2011 Warmux Team.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A ARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU GeneralPublic License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18  ******************************************************************************
19  * Handle a SDL Surface
20  *****************************************************************************/
21 
22 #include <iostream>
23 #include <SDL.h>
24 #include <SDL_gfxPrimitives.h>
25 #include <SDL_image.h>
26 #include <SDL_rotozoom.h>
27 #include <png.h>
28 #include <zlib.h>
29 
30 #include "graphic/surface.h"
31 #include "tool/math_tools.h"
32 
33 /* texturedPolygon import from SDL_gfx v2.0.15 */
34 #if (SDL_GFXPRIMITIVES_MAJOR == 2) && (SDL_GFXPRIMITIVES_MINOR == 0) && (SDL_GFXPRIMITIVES_MICRO < 14)
35 #include "graphic/textured_polygon.h"
36 #endif /* texturedPolygon import from SDL_gfx v2.0.15 */
37 
38 #include "graphic/fading_effect.h"
39 
40 /**
41  * Constructor building a surface by reading the image from a file.
42  *
43  * @param filename_str A string containing the path to the graphic file.
44  */
Surface(const std::string & filename)45 Surface::Surface(const std::string &filename)
46 {
47   surface = NULL;
48   autoFree = true;
49   if (!ImgLoad(filename))
50     Error(Format("Unable to open image file '%s': %s", filename.c_str(), IMG_GetError()));
51 }
52 
53 /**
54  * Copy constructor: build a surface from an other surface.
55  *
56  * The two surfaces share the same graphic data.
57  */
Surface(const Surface & src)58 Surface::Surface(const Surface &src)
59 {
60   surface = src.surface;
61   autoFree = true;
62   if (!IsNull())
63     surface->refcount++;
64 }
65 
operator =(const Surface & src)66 Surface &Surface::operator=(const Surface & src)
67 {
68   AutoFree();
69   surface = src.surface;
70   autoFree = true;
71   if (!IsNull())
72     surface->refcount++;
73 
74   return *this;
75 }
76 
77 /**
78  * Free the memory occupied by the surface.
79  *
80  * The memory is really freed if the reference counter reach 0.
81  */
Free()82 void Surface::Free()
83 {
84   if (!IsNull()) {
85     SDL_FreeSurface(surface);
86     surface = NULL;
87   }
88 }
89 
90 /**
91  * Create a new surface.
92  *
93  * @param size
94  * @param flags
95  * @param useAlpha
96  */
NewSurface(const Point2i & size,Uint32 flags,bool useAlpha)97 void Surface::NewSurface(const Point2i &size, Uint32 flags, bool useAlpha)
98 {
99   if (autoFree)
100     Free();
101 
102   const SDL_PixelFormat* fmt = SDL_GetVideoSurface()->format;
103   // If no alpha, use default parameters
104   if (!useAlpha) {
105     surface = SDL_CreateRGBSurface(flags, size.x, size.y,
106                                    fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, 0);
107   } else {
108     // Code below taken from SDL_DisplayFormatAlpha
109     // Why the default parameters to SDL_CreateRGBSurface when using 32bits are
110     // not equivalent to this !?
111     Uint32 amask = 0xff000000;
112     Uint32 rmask = 0x00ff0000;
113     Uint32 gmask = 0x0000ff00;
114     Uint32 bmask = 0x000000ff;
115 
116     switch(fmt->BytesPerPixel) {
117     case 2:
118       /* For XGY5[56]5, use, AXGY8888, where {X, Y} = {R, B}.
119          For anything else (like ARGB4444) it doesn't matter
120          since we have no special code for it anyway */
121       if (fmt->Rmask == 0x1f && (fmt->Bmask==0xf800 || fmt->Bmask==0x7c00)) {
122         rmask = 0xff;
123         bmask = 0xff0000;
124       }
125       break;
126 
127     case 3:
128     case 4:
129       /* Keep the video format, as long as the high 8 bits are
130          unused or alpha */
131       if (fmt->Rmask == 0xff && fmt->Bmask == 0xff0000) {
132         rmask = 0xff;
133         bmask = 0xff0000;
134       }
135       break;
136 
137     default:
138       /* We have no other optimised formats right now. When/if a new
139          optimised alpha format is written, add the converter here */
140       break;
141     }
142     surface = SDL_CreateRGBSurface(flags|SDL_SRCALPHA, size.x, size.y, 32,
143                                    rmask, gmask, bmask, amask);
144   }
145   if (!surface)
146     Error(std::string("Can't create SDL RGB(A) surface: ") + SDL_GetError());
147 }
148 
149 /**
150  * Set the alpha value of a surface.
151  *
152  */
SetAlpha(Uint32 flags,Uint8 alpha)153 int Surface::SetAlpha(Uint32 flags, Uint8 alpha)
154 {
155   return SDL_SetAlpha(surface, flags, alpha);
156 }
157 
158 /**
159  * Lock the surface to permit direct access.
160  *
161  */
Lock()162 void Surface::Lock()
163 {
164   if (SDL_MUSTLOCK(surface)) {
165     if (SDL_LockSurface(surface) < 0) {
166       fprintf(stderr, "Failed to lock surface: %s\n", SDL_GetError());
167       exit(-1);
168     }
169   }
170 }
171 
172 /**
173  * Unlock the surface.
174  *
175  */
Unlock()176 void Surface::Unlock()
177 {
178   SDL_UnlockSurface(surface);
179 }
180 
SwapClipRect(Rectanglei & rect)181 void Surface::SwapClipRect(Rectanglei& rect)
182 {
183   SDL_Rect newClipRect = GetSDLRect(rect);
184   SDL_Rect oldClipRect;
185 
186   SDL_GetClipRect(surface, &oldClipRect);
187   SDL_SetClipRect(surface, &newClipRect);
188 
189   rect.SetPositionX(oldClipRect.x);
190   rect.SetPositionY(oldClipRect.y);
191   rect.SetSizeX(oldClipRect.w);
192   rect.SetSizeY(oldClipRect.h);
193 }
194 
195 
Blit(const Surface & src,SDL_Rect * srcRect,SDL_Rect * dstRect)196 int Surface::Blit(const Surface& src, SDL_Rect *srcRect, SDL_Rect *dstRect)
197 {
198   int ret = SDL_BlitSurface(src.surface, srcRect, surface, dstRect);
199   if (ret < 0) {
200     printf("Blit failed (code=%i): %s\n", ret, SDL_GetError());
201   }
202   return ret;
203 }
204 
205 /**
206  * Blit a surface (src) on the current surface at a certain position (dst)
207  *
208  * @src The source surface.
209  * @dst A point defining the destination coordinate on the current surface.
210  */
Blit(const Surface & src,const Point2i & dst)211 int Surface::Blit(const Surface& src, const Point2i &dst)
212 {
213   SDL_Rect dstRect = GetSDLRect(dst);
214 
215   return Blit(src, NULL, &dstRect);
216 }
217 
218 /**
219  * Blit a part (srcRect) of surface (src) at a certaint position (dst) of the current surface/
220  *
221  * @param src
222  * @param srcRect
223  * @param dstPoint
224  */
Blit(const Surface & src,const Rectanglei & srcRect,const Point2i & dstPoint)225 int Surface::Blit(const Surface& src, const Rectanglei &srcRect, const Point2i &dstPoint)
226 {
227   SDL_Rect sdlSrcRect = GetSDLRect(srcRect);
228   SDL_Rect sdlDstRect = GetSDLRect(dstPoint);
229 
230   return Blit(src, &sdlSrcRect, &sdlDstRect);
231 }
232 
233 /**
234  * Merge a sprite (spr) with current Surface at a given position.
235  *
236  * No more buggy but slow ! :) Don't use it for quick blit. Needed by the ground generator.
237  *
238  * @param spr
239  * @param position
240  */
MergeSurface(Surface & spr,const Point2i & pos)241 void Surface::MergeSurface(Surface &spr, const Point2i &pos)
242 {
243   spr.Lock();
244   Lock();
245 
246   SDL_PixelFormat* cur_fmt = surface->format;
247   SDL_PixelFormat * spr_fmt = spr.surface->format;
248 
249   // for each pixel lines of a source image
250   if (cur_fmt->BytesPerPixel == spr_fmt->BytesPerPixel && cur_fmt->BytesPerPixel == 4) {
251     int     cur_pitch = (surface->pitch>>2);
252     Uint32* cur_ptr   = (Uint32*)surface->pixels;
253     int     spr_pitch = (spr.surface->pitch>>2);
254     Uint32* spr_ptr   = (Uint32*)spr.surface->pixels;
255     Uint32  spr_pix, cur_pix, a, p_a;
256     Point2i offset;
257 
258     offset.y = (pos.y > 0) ? 0 : -pos.y;
259 
260     cur_ptr += pos.x + (pos.y + offset.y) * cur_pitch;
261     spr_ptr += offset.y * spr_pitch;
262 
263     // Same masks: use more optimized version
264     if (cur_fmt->Amask == spr_fmt->Amask) {
265       Uint32  ashift    = cur_fmt->Ashift;
266       Uint32  amask     = cur_fmt->Amask;
267       // shift necessary to move the RGB triplet into the LSBs
268       Uint32  shift     = (ashift) ? 0 : 8;
269 
270       for (; offset.y < spr.GetHeight() && pos.y + offset.y < GetHeight(); offset.y++) {
271         for (offset.x = (pos.x > 0 ? 0 : -pos.x); offset.x < spr.GetWidth() && pos.x + offset.x < GetWidth(); offset.x++) {
272           // Retrieving a pixel of sprite to merge
273           spr_pix = spr_ptr[offset.x];
274           cur_pix = cur_ptr[offset.x];
275 
276           a   = (spr_pix&amask)>>ashift;
277           p_a = (cur_pix&amask)>>ashift;
278 
279           if (a == SDL_ALPHA_OPAQUE || (!p_a && a)) // new pixel with no alpha or nothing on previous pixel
280             cur_ptr[offset.x] = spr_pix;
281           else if (a) { // alpha is lower => merge color with previous value
282             uint f_a  = a + 1;
283             uint f_ca = 256 - f_a;
284 
285             // A will be discarded either by this shift or the bitmasks used
286             cur_pix >>= shift;
287             spr_pix >>= shift;
288             // Only do 2 components at a time, and avoid one component overflowing
289             // to bleed into other components
290             Uint32 tmp = ((cur_pix&0xFF00FF)*f_ca + (spr_pix&0xFF00FF)*f_a)>>8;
291             tmp &= 0xFF00FF;
292 
293             tmp |= (((cur_pix&0xFF00)*f_ca + (spr_pix&0xFF00)*f_a)>>8) & 0xFF00;
294 
295             a = (a > p_a) ? a : p_a;
296             cur_ptr[offset.x] = (tmp<<shift) | (a<<ashift);
297           }
298         }
299 
300         spr_ptr += spr_pitch;
301         cur_ptr += cur_pitch;
302       }
303     } else {
304       // Troublesome masks: use generic version
305       for (; offset.y < spr.GetHeight() && pos.y + offset.y < GetHeight(); offset.y++) {
306         for (offset.x = (pos.x > 0 ? 0 : -pos.x); offset.x < spr.GetWidth() && pos.x + offset.x < GetWidth(); offset.x++) {
307           // Retrieving a pixel of sprite to merge
308           spr_pix = spr_ptr[offset.x];
309           cur_pix = cur_ptr[offset.x];
310 
311           a   = (spr_pix&spr_fmt->Amask)>>spr_fmt->Ashift;
312           p_a = (cur_pix&cur_fmt->Amask)>>cur_fmt->Ashift;
313 
314           if (a == SDL_ALPHA_OPAQUE || (!p_a && a)) {
315             // new pixel with no alpha or nothing on previous pixel
316             cur_ptr[offset.x] = spr_pix;
317           } else if (a) {
318             // alpha is lower => merge color with previous value
319             uint f_a  = a + 1;
320             uint f_ca = 256 - f_a;
321 
322             Uint32 r = (((cur_pix&cur_fmt->Rmask)>>cur_fmt->Rshift)*f_ca +
323                         ((spr_pix&spr_fmt->Rmask)>>spr_fmt->Rshift)*f_a)>>8;
324             Uint32 g = (((cur_pix&cur_fmt->Gmask)>>cur_fmt->Gshift)*f_ca +
325                         ((spr_pix&spr_fmt->Gmask)>>spr_fmt->Gshift)*f_a)>>8;
326             Uint32 b = (((cur_pix&cur_fmt->Bmask)>>cur_fmt->Bshift)*f_ca +
327                         ((spr_pix&spr_fmt->Bmask)>>spr_fmt->Bshift)*f_a)>>8;
328 
329             a = (a > p_a) ? a : p_a;
330             cur_ptr[offset.x] = (r<<cur_fmt->Rshift)|(g<<cur_fmt->Gshift)|
331                                 (b<<cur_fmt->Bshift)|(a<<cur_fmt->Ashift);
332           }
333         }
334 
335         spr_ptr += spr_pitch;
336         cur_ptr += cur_pitch;
337       }
338     }
339   } else {
340     fprintf(stderr, "Not handling: spr=(bpp=%u,rmask=%X) vs surf=(bpp=%u,rmask=%X)\n",
341             spr_fmt->BytesPerPixel, spr_fmt->Rmask, cur_fmt->BytesPerPixel, cur_fmt->Rmask);
342     Blit(spr, pos);
343   }
344 
345   Unlock();
346   spr.Unlock();
347 }
348 
SetColorKey(Uint32 flag,Uint32 key)349 int Surface::SetColorKey(Uint32 flag, Uint32 key)
350 {
351   return SDL_SetColorKey(surface, flag, key);
352 }
353 
GetRGBA(Uint32 color,Uint8 & r,Uint8 & g,Uint8 & b,Uint8 & a) const354 void Surface::GetRGBA(Uint32 color, Uint8 &r, Uint8 &g, Uint8 &b, Uint8 &a) const
355 {
356   SDL_GetRGBA(color, surface->format, &r, &g, &b, &a);
357 }
358 
MapRGBA(Uint8 r,Uint8 g,Uint8 b,Uint8 a) const359 Uint32 Surface::MapRGBA(Uint8 r, Uint8 g, Uint8 b, Uint8 a) const
360 {
361   return SDL_MapRGBA(surface->format, r, g, b, a);
362 }
363 
GetColor(Uint32 color) const364 Color Surface::GetColor(Uint32 color) const
365 {
366   Uint8 r, g, b, a;
367   GetRGBA(color, r, g, b, a);
368   return Color(r, g, b, a);
369 }
370 
MapColor(const Color & color) const371 Uint32 Surface::MapColor(const Color& color) const
372 {
373   return MapRGBA(color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
374 }
375 
Flip()376 void Surface::Flip()
377 {
378   SDL_Flip(surface);
379 }
380 
BoxColor(const Rectanglei & rect,const Color & color)381   int Surface::BoxColor(const Rectanglei &rect, const Color &color)
382 {
383   if (rect.IsSizeZero())
384     return 0;
385 
386   Point2i ptBR = rect.GetBottomRightPoint();
387 
388   return boxRGBA(surface, rect.GetPositionX(), rect.GetPositionY(), ptBR.GetX(), ptBR.GetY(), color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
389 }
390 
RectangleColor(const Rectanglei & rect,const Color & color,const uint & border_size)391 int Surface::RectangleColor(const Rectanglei &rect, const Color &color,
392                             const uint &border_size)
393 {
394   if (rect.IsSizeZero())
395     return 0;
396 
397   Point2i ptBR = rect.GetBottomRightPoint();
398 
399   if (border_size == 1)
400     return rectangleRGBA(surface, rect.GetPositionX(), rect.GetPositionY(), ptBR.GetX(), ptBR.GetY(), color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
401 
402   // top border
403   boxRGBA (surface,
404            rect.GetPositionX(), rect.GetPositionY(), ptBR.GetX(), rect.GetPositionY()+border_size,
405            color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
406 
407   // bottom border
408   boxRGBA (surface,
409            rect.GetPositionX(), ptBR.GetY() - border_size, ptBR.GetX(), ptBR.GetY(),
410            color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
411 
412   // left border
413   boxRGBA (surface,
414            rect.GetPositionX(), rect.GetPositionY() + border_size, rect.GetPositionX()+border_size, ptBR.GetY()-border_size,
415            color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
416 
417   // right border
418   boxRGBA (surface,
419            ptBR.GetX() - border_size, rect.GetPositionY() + border_size, ptBR.GetX(), ptBR.GetY()-border_size,
420            color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
421 
422   return 1;
423 }
424 
VlineColor(const uint & x,const uint & y1,const uint & y2,const Color & color)425 int Surface::VlineColor(const uint &x, const uint &y1, const uint &y2, const Color &color)
426 {
427   return vlineRGBA(surface, x, y1, y2, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
428 }
429 
HlineColor(const uint & x1,const uint & x2,const uint & y,const Color & color)430 int Surface::HlineColor(const uint &x1, const uint &x2, const uint &y, const Color &color)
431 {
432   return hlineRGBA(surface, x1, x2, y, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
433 }
434 
LineColor(const uint & x1,const uint & x2,const uint & y1,const uint & y2,const Color & color)435 int Surface::LineColor(const uint &x1, const uint &x2, const uint &y1, const uint &y2, const Color &color)
436 {
437   return lineRGBA(surface, x1, y1, x2, y2, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
438 }
439 
AALineColor(const uint & x1,const uint & x2,const uint & y1,const uint & y2,const Color & color)440 int Surface::AALineColor(const uint &x1, const uint &x2, const uint &y1, const uint &y2, const Color &color)
441 {
442   return aalineRGBA(surface, x1, y1, x2, y2, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
443 }
444 
AAFadingLineColor(const uint & x1,const uint & x2,const uint & y1,const uint & y2,const Color & color1,const Color & color2)445 int Surface::AAFadingLineColor(const uint &x1, const uint &x2, const uint &y1, const uint &y2, const Color &color1, const Color &color2)
446 {
447   return aafadingLineColor(surface, x1, y1, x2, y2,color1.GetColor(), color2.GetColor());
448 }
449 
CircleColor(const uint & x,const uint & y,const uint & rad,const Color & color)450 int Surface::CircleColor(const uint &x, const uint &y, const uint &rad, const Color &color)
451 {
452   return circleRGBA(surface, x, y, rad, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
453 }
454 
FilledCircleColor(const uint & x,const uint & y,const uint & rad,const Color & color)455 int Surface::FilledCircleColor(const uint &x, const uint &y, const uint &rad, const Color &color)
456 {
457   return filledCircleRGBA(surface, x, y, rad, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
458 }
459 
PieColor(const uint & x,const uint & y,const uint & rad,const int & start,const int & end,const Color & color)460 int Surface::PieColor(const uint &x, const uint &y, const uint &rad, const int &start, const int &end, const Color &color)
461 {
462   return pieRGBA(surface, x, y, rad, start, end, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
463 }
464 
FilledPieColor(const uint & x,const uint & y,const uint & rad,const int & start,const int & end,const Color & color)465 int Surface::FilledPieColor(const uint &x, const uint &y, const uint &rad, const int &start, const int &end, const Color &color)
466 {
467   return filledPieRGBA(surface, x, y, rad, start, end, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
468 }
469 
AAPolygonColor(const Sint16 * vx,const Sint16 * vy,const int n,const Color & color)470 int Surface::AAPolygonColor(const Sint16 * vx, const Sint16 * vy, const int n, const Color & color) {
471   return aapolygonRGBA(surface, vx, vy, n, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
472 }
473 
AAPolygonColor(std::list<Point2i> polygon,const Color & color)474 int Surface::AAPolygonColor(std::list<Point2i> polygon, const Color & color)
475 {
476   Sint16 * vx, * vy;
477   vx = new Sint16[polygon.size()];
478   vy = new Sint16[polygon.size()];
479   int i = 0;
480   for (std::list<Point2i>::iterator point = polygon.begin(); point != polygon.end(); point++, i++) {
481     vx[i] = point->x;
482     vy[i] = point->y;
483   }
484   int result = aapolygonRGBA(surface, vx, vy, polygon.size(), color.GetRed(),
485                              color.GetGreen(), color.GetBlue(), color.GetAlpha());
486   delete[] vx;
487   delete[] vy;
488   return result;
489 }
490 
FilledPolygon(const Sint16 * vx,const Sint16 * vy,const int n,const Color & color)491 int Surface::FilledPolygon(const Sint16 * vx, const Sint16 * vy, const int n, const Color & color)
492 {
493   // Internal static leak in sdl_gfx
494   return filledPolygonRGBA(surface, vx, vy, n, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha());
495 }
496 
FilledPolygon(std::list<Point2i> polygon,const Color & color)497 int Surface::FilledPolygon(std::list<Point2i> polygon, const Color & color)
498 {
499   Sint16 * vx, * vy;
500   vx = new Sint16[polygon.size()];
501   vy = new Sint16[polygon.size()];
502   int i = 0;
503   for (std::list<Point2i>::iterator point = polygon.begin(); point != polygon.end(); point++, i++) {
504     vx[i] = point->x;
505     vy[i] = point->y;
506   }
507   int result = filledPolygonRGBA(surface, vx, vy, polygon.size(), color.GetRed(),
508                                  color.GetGreen(), color.GetBlue(), color.GetAlpha());
509   delete[] vx;
510   delete[] vy;
511   return result;
512 }
513 
TexturedPolygon(const Sint16 * vx,const Sint16 * vy,const int n,const Surface * texture,const int texture_dx,const int texture_dy)514 int Surface::TexturedPolygon(const Sint16 * vx, const Sint16 * vy, const int n, const Surface *texture, const int texture_dx, const int texture_dy)
515 {
516   return texturedPolygon(surface, vx, vy, n, texture->surface, texture_dx, texture_dy);
517 }
518 
TexturedPolygon(std::list<Point2i> polygon,const Surface * texture)519 int Surface::TexturedPolygon(std::list<Point2i> polygon, const Surface * texture)
520 {
521   Sint16 * vx, * vy;
522   vx = new Sint16[polygon.size()];
523   vy = new Sint16[polygon.size()];
524   int i = 0;
525   for (std::list<Point2i>::iterator point = polygon.begin(); point != polygon.end(); point++, i++) {
526     vx[i] = point->x;
527     vy[i] = point->y;
528   }
529   int result = texturedPolygon(surface, vx, vy, polygon.size(), texture->surface, 0, 0);
530   delete[] vx;
531   delete[] vy;
532   return result;
533 }
534 
535 /**
536  *
537  * @param color
538  */
Fill(Uint32 color) const539 int Surface::Fill(Uint32 color) const
540 {
541   return SDL_FillRect(surface, NULL, color);
542 }
543 
Fill(const Color & color) const544 int Surface::Fill(const Color &color) const
545 {
546   return Fill(MapColor(color));
547 }
548 
549 /**
550  *
551  * @param dstRect
552  * @param color
553  */
FillRect(const Rectanglei & dstRect,Uint32 color) const554 int Surface::FillRect(const Rectanglei &dstRect, Uint32 color) const
555 {
556   SDL_Rect sdlDstRect = GetSDLRect(dstRect);
557 
558   return SDL_FillRect(surface, &sdlDstRect, color);
559 }
560 
561 /**
562  *
563  * @param dstRect
564  * @param color
565  */
FillRect(const Rectanglei & dstRect,const Color & color) const566 int Surface::FillRect(const Rectanglei &dstRect, const Color &color) const
567 {
568   return FillRect(dstRect, MapColor(color));
569 }
570 
571 /**
572  *
573  * @param filename
574  */
ImgLoad(const std::string & filename)575 int Surface::ImgLoad(const std::string& filename)
576 {
577   AutoFree();
578   surface = IMG_Load(filename.c_str());
579 
580   return !IsNull();
581 }
582 
583 /**
584  *
585  * @param filename
586  */
ImgSave(const std::string & filename,bool bmp)587 bool Surface::ImgSave(const std::string& filename, bool bmp)
588 {
589   if (bmp) {
590     return (surface) ? SDL_SaveBMP(surface, filename.c_str())==0 : false;
591   }
592 
593   FILE            *f        = NULL;
594   png_structp      png_ptr  = NULL;
595   png_infop        info_ptr = NULL;
596   SDL_PixelFormat *spr_fmt  = surface->format;
597   bool             ret      = false;
598   Uint8           *tmp_line = NULL;
599 
600   // Creating a png ...
601   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
602   if (png_ptr == NULL) // Structure and ...
603     return 1;
604   info_ptr = png_create_info_struct(png_ptr);
605   if (info_ptr == NULL) // Information.
606     goto end;
607 
608   // Opening a new file
609   f = fopen(filename.c_str(), "wb");
610   if (f == NULL)
611     goto end;
612 
613   png_init_io(png_ptr, f); // Associate png struture with a file
614   png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8,
615                PNG_COLOR_TYPE_RGB_ALPHA,      PNG_INTERLACE_NONE,
616                PNG_COMPRESSION_TYPE_DEFAULT,  PNG_FILTER_TYPE_DEFAULT);
617   png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
618 
619   // Creating the png file
620   png_write_info(png_ptr, info_ptr);
621 
622   tmp_line = new Uint8[surface->w * spr_fmt->BytesPerPixel];
623   Lock();
624   for (int y = 0; y < surface->h; y++) {
625     for (int x = 0; x < surface->w; x++) {
626       Uint8   r, g, b, a;
627       // Retrieving a pixel of sprite to merge
628       Uint32  spr_pix = ((Uint32*)surface->pixels)[y * surface->w  + x];
629 
630       // Retreiving each chanel of the pixel using pixel format
631       SDL_GetRGBA(spr_pix, surface->format, &r, &g, &b, &a);
632       tmp_line[x * spr_fmt->BytesPerPixel + 0] = r;
633       tmp_line[x * spr_fmt->BytesPerPixel + 1] = g;
634       tmp_line[x * spr_fmt->BytesPerPixel + 2] = b;
635       tmp_line[x * spr_fmt->BytesPerPixel + 3] = a;
636     }
637     png_write_row(png_ptr, (Uint8 *)tmp_line);
638   }
639   Unlock();
640   delete[] tmp_line;
641   png_write_flush(png_ptr);
642   png_write_end(png_ptr, info_ptr);
643   ret = true;
644 
645 end:
646   if (info_ptr) png_destroy_info_struct(png_ptr, &info_ptr);
647   if (png_ptr) png_destroy_write_struct(&png_ptr, NULL);
648   if (f) fclose(f);
649   return ret;
650 }
651 
652 #if SDL_GFXPRIMITIVES_MICRO > 20
653 template<typename pixel>
654 static void
mirror(void * d,uint dpitch,const void * s,uint spitch,int w,int h)655 mirror(void *d, uint dpitch,
656        const void* s, uint spitch,
657        int w, int h)
658 {
659   pixel *dst = (pixel*)d;
660   const pixel *src = ((pixel*)s)+(w-1);
661 
662   dpitch /= sizeof(pixel);
663   spitch /= sizeof(pixel);
664 
665   while (h--) {
666     for (int x=0; x<w; x++)
667       dst[x] = src[-x];
668     dst += dpitch;
669     src += spitch;
670   }
671 }
672 #endif
673 
Mirror()674 Surface Surface::Mirror()
675 {
676 #if SDL_GFXPRIMITIVES_MICRO > 20
677   const SDL_PixelFormat *fmt = surface->format;
678   SDL_Surface *surf = SDL_CreateRGBSurface(surface->flags, surface->w, surface->h, fmt->BitsPerPixel,
679                                            fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
680 
681   if (SDL_MUSTLOCK(surface))
682     SDL_LockSurface(surface);
683   if (SDL_MUSTLOCK(surf))
684     SDL_LockSurface(surf);
685 
686   switch (fmt->BitsPerPixel)
687   {
688   case 8:
689     mirror<Uint8>(surf->pixels, surf->pitch, surface->pixels, surface->pitch,
690                   surf->w, surf->h);
691     break;
692   case 16:
693     mirror<Uint16>(surf->pixels, surf->pitch, surface->pixels, surface->pitch,
694                    surf->w, surf->h);
695     break;
696   case 32:
697     mirror<Uint32>(surf->pixels, surf->pitch, surface->pixels, surface->pitch,
698                    surf->w, surf->h);
699     break;
700   case 24:
701     {
702       uint8_t *dst = (uint8_t*)surf->pixels;
703       const uint8_t *src = (uint8_t*)surface->pixels;
704       src += 3*(surf->w-1);
705       for (int y=0; y<surf->h; y++) {
706         for (int x=0; x<3*surf->w; x+=3) {
707           dst[x+0] = src[0-x];
708           dst[x+1] = src[1-x];
709           dst[x+2] = src[2-x];
710         }
711         dst += surf->pitch;
712         src += surface->pitch;
713       }
714       break;
715     }
716   default: fprintf(stderr, "Unsupported bpp %i\n", fmt->BitsPerPixel); exit(1);
717   }
718 
719   SDL_UnlockSurface(surf);
720   SDL_UnlockSurface(surface);
721 
722   if (surface->flags & SDL_SRCALPHA)
723     SDL_SetAlpha(surf, SDL_SRCALPHA, surface->format->alpha);
724   if (surface->flags & SDL_SRCCOLORKEY)
725     SDL_SetColorKey(surf, SDL_SRCCOLORKEY|SDL_RLEACCEL, surface->format->colorkey);
726 
727   return Surface(surf);
728 #else
729   return Surface(zoomSurface(surface, -1, 1, 1)).DisplayFormatAlpha();
730 #endif
731 }
732 
733 /**
734  *
735  * @param angle in radian
736  * @param zoomx
737  * @param zoomy
738  * @param smooth
739 * Warning rotozoomSurfaceXY uses degrees so the rotation of image use degrees here,
740 * but when accessing thanks to GetSurfaceForAngle the index is using radian
741 * (because we juste need an index in array, not an angle) */
742 static const Double ratio_deg_to_rad = 180 / PI;
RotoZoom(Double angle,Double zoomx,Double zoomy)743 Surface Surface::RotoZoom(Double angle, Double zoomx, Double zoomy)
744 {
745   SDL_Surface *surf;
746 
747   if (EqualsZero(angle)) {
748     if (zoomx!=ONE || zoomy!=ONE)
749       surf = zoomSurface(surface, zoomx.toDouble(), zoomy.toDouble(), 1);
750     else {
751       return *this;
752     }
753   } else if (zoomx == zoomy && zoomx > ZERO) {
754     surf = rotozoomSurface(surface, (angle * ratio_deg_to_rad).toDouble() , zoomx.toDouble(), 1);
755   } else {
756     surf = rotozoomSurfaceXY(surface, (angle * ratio_deg_to_rad).toDouble() , zoomx.toDouble(), zoomy.toDouble(), 1);
757   }
758 
759   if (!surf)
760     Error("Unable to make a rotozoom on the surface !");
761 
762   return surface->format->Amask ? Surface(surf).DisplayFormatAlpha() : Surface(surf).DisplayFormat();
763 }
764 
DisplayFormatAlpha()765 Surface Surface::DisplayFormatAlpha()
766 {
767   if (surface->format->BitsPerPixel == 24)
768     return DisplayFormat();
769 
770   const SDL_PixelFormat *fo = SDL_GetVideoSurface()->format,
771                         *fi = surface->format;
772   if (fi->Rmask==fo->Rmask && fi->Bmask==fo->Bmask && fi->Amask==fo->Amask)
773     return *this;
774   SDL_Surface *surf = SDL_DisplayFormatAlpha(surface);
775 
776   if (!surf)
777     Error("Unable to convert the surface to a surface compatible with the display format with alpha.");
778 
779   return Surface(surf);
780 }
781 
DisplayFormat()782 Surface Surface::DisplayFormat()
783 {
784   const SDL_PixelFormat *fo = SDL_GetVideoSurface()->format,
785                         *fi = surface->format;
786   if (fi->Rmask==fo->Rmask && fi->Bmask==fo->Bmask && fi->Amask==0)
787     return *this;
788 
789   SDL_Surface *surf = SDL_DisplayFormat(surface);
790 
791   if (!surf)
792     Error("Unable to convert the surface to a surface compatible with the display format.");
793 
794   return Surface(surf);
795 }
796 
797 
798 /**
799  * GetPixel.
800  *
801  * From the SDL wiki.
802  * @param x
803  * @param y
804  */
GetPixel(int x,int y) const805 Uint32 Surface::GetPixel(int x, int y) const
806 {
807   int bpp = surface->format->BytesPerPixel;
808   /* Here p is the address to the pixel we want to retrieve */
809   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
810 
811   switch(bpp) {
812   case 1:
813     return *p;
814 
815   case 2:
816     return *(Uint16 *)p;
817 
818   case 3:
819 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
820     return p[0] << 16 | p[1] << 8 | p[2];
821 #else
822     return p[0] | p[1] << 8 | p[2] << 16;
823 #endif
824   case 4:
825     return *(Uint32 *)p;
826 
827   default:
828     Error("Unknow bpp!");
829     return 0;   // To make gcc happy
830   }
831 }
832 
833 /**
834  *
835  * @param x
836  * @param y
837  * @param pixel
838  */
PutPixel(int x,int y,Uint32 pixel) const839 void Surface::PutPixel(int x, int y, Uint32 pixel) const
840 {
841   int bpp = surface->format->BytesPerPixel;
842   /* Here p is the address to the pixel we want to set */
843   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
844 
845   switch(bpp) {
846   case 1:
847     *p = pixel;
848     break;
849 
850   case 2:
851     *(Uint16 *)p = pixel;
852     break;
853 
854   case 3:
855 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
856     p[0] = (pixel >> 16) & 0xff;
857     p[1] = (pixel >> 8) & 0xff;
858     p[2] = pixel & 0xff;
859 #else
860     p[0] = pixel & 0xff;
861     p[1] = (pixel >> 8) & 0xff;
862     p[2] = (pixel >> 16) & 0xff;
863 #endif
864     break;
865 
866   case 4:
867     *(Uint32 *)p = pixel;
868     break;
869   }
870 }
871 
GetSDLRect(const Rectanglei & r)872 SDL_Rect Surface::GetSDLRect(const Rectanglei &r)
873 {
874   SDL_Rect sdlRect;
875 
876   sdlRect.x = r.GetPositionX();
877   sdlRect.y = r.GetPositionY();
878   sdlRect.w = r.GetSizeX();
879   sdlRect.h = r.GetSizeY();
880 
881   return sdlRect;
882 }
883 
GetSDLRect(const Point2i & pt)884 SDL_Rect Surface::GetSDLRect(const Point2i &pt)
885 {
886   SDL_Rect sdlRect;
887 
888   sdlRect.x = pt.GetX();
889   sdlRect.y = pt.GetY();
890   sdlRect.w = 0;
891   sdlRect.h = 0;
892 
893   return sdlRect;
894 }
895 
DisplayFormatColorKey(const uint32_t * data,SDL_PixelFormat * sfmt,int w,int h,int stride,uint8_t threshold,bool rle)896 Surface Surface::DisplayFormatColorKey(const uint32_t* data, SDL_PixelFormat *sfmt,
897                                        int w, int h, int stride,
898                                        uint8_t threshold, bool rle)
899 {
900   SDL_PixelFormat *fmt   = SDL_GetVideoSurface()->format;
901   uint             bpp   = fmt->BitsPerPixel==16 ? 16 : 24;
902   Surface          surf(SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp, 0, 0, 0, 0));
903   const uint32_t  *src   = data;
904   int              pitch = stride>>2;
905   Uint32           ckey  = SDL_MapRGB(surf.surface->format, 0xFF, 0, 0xFF);
906 
907   surf.Lock();
908 
909   // Set pixels considered as transparent as colorkey
910   for (int y=0; y<h; y++) {
911     for (int x=0; x<w; x++) {
912       uint8_t r, g, b, a;
913       SDL_GetRGBA(*(src + x), sfmt, &r, &g, &b, &a);
914       surf.PutPixel(x, y, (a < threshold) ? ckey : SDL_MapRGB(surf.surface->format, r, g, b));
915     }
916 
917     src += pitch;
918   }
919 
920   surf.Unlock();
921   if (rle)
922     surf.SetColorKey(SDL_SRCCOLORKEY|SDL_RLEACCEL, ckey);
923   else
924     surf.SetColorKey(SDL_SRCCOLORKEY, ckey);
925 
926   return surf;
927 }
928 
DisplayFormatColorKey(uint8_t alpha_threshold,bool rle)929 Surface Surface::DisplayFormatColorKey(uint8_t alpha_threshold, bool rle)
930 {
931   Lock();
932   Surface tmp = DisplayFormatColorKey((uint32_t*)surface->pixels, surface->format,
933                                       surface->w, surface->h, surface->pitch,
934                                       alpha_threshold, rle);
935   Unlock();
936   return tmp;
937 }
938 
Crop(const Rectanglei & area) const939 Surface Surface::Crop(const Rectanglei& area) const
940 {
941   Surface sub(area.GetSize(), SDL_SWSURFACE, surface->format->Amask!=0);
942   SDL_SetAlpha(surface, 0, 0);
943   sub.Blit(*this, -area.GetPosition());
944   return sub;
945 }
946