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