FIFE  2008.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
sdlimage.cpp
1 /***************************************************************************
2  * Copyright (C) 2005-2008 by the FIFE team *
3  * http://www.fifengine.de *
4  * This file is part of FIFE. *
5  * *
6  * FIFE is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU Lesser General Public *
8  * License as published by the Free Software Foundation; either *
9  * version 2.1 of the License, or (at your option) any later version. *
10  * *
11  * This library is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14  * Lesser General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU Lesser General Public *
17  * License along with this library; if not, write to the *
18  * Free Software Foundation, Inc., *
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 #include <cassert>
24 #include <iostream>
25 
26 // 3rd party library includes
27 
28 // FIFE includes
29 // These includes are split up in two parts, separated by one empty line
30 // First block: files included from the FIFE root src directory
31 // Second block: files included from the same folder
32 #include "util/log/logger.h"
33 #include "util/structures/rect.h"
34 #include "video/renderbackend.h"
35 
36 #include "renderbackendsdl.h"
37 #include "sdlblendingfunctions.h"
38 #include "sdlimage.h"
39 
40 namespace FIFE {
41  static Logger _log(LM_VIDEO);
42 
43  SDLImage::SDLImage(SDL_Surface* surface):
44  Image(surface) {
45  resetSdlimage();
46  }
47 
48  SDLImage::SDLImage(const uint8_t* data, unsigned int width, unsigned int height):
49  Image(data, width, height) {
50  resetSdlimage();
51  }
52 
53  void SDLImage::resetSdlimage() {
54  m_last_alpha = 255;
55  m_finalized = false;
56  m_isalphaoptimized = false;
57  m_colorkey = RenderBackend::instance()->getColorKey();
58  m_scale_x = 1.0;
59  m_scale_y = 1.0;
60  m_zoom_surface = NULL;
61  }
62 
63  SDLImage::~SDLImage() {
64  if (m_zoom_surface) {
65  SDL_FreeSurface(m_zoom_surface);
66  }
67  }
68 
69  void SDL_BlitSurfaceWithAlpha( const SDL_Surface* src, const SDL_Rect* srcRect,
70  SDL_Surface* dst, SDL_Rect* dstRect, unsigned char alpha ) {
71  if( 0 == alpha ) {
72  return;
73  }
74 
75  int screenX, screenY;
76  if( dstRect ) {
77  screenX = dstRect->x;
78  screenY = dstRect->y;
79  } else {
80  screenX = dst->clip_rect.x;
81  screenY = dst->clip_rect.y;
82  }
83 
84  int width, height, tX, tY;
85  if( srcRect ) {
86  tX = srcRect->x;
87  tY = srcRect->y;
88  width = srcRect->w;
89  height = srcRect->h;
90  } else {
91  tX = src->clip_rect.x;
92  tY = src->clip_rect.y;
93  width = src->clip_rect.w;
94  height = src->clip_rect.h;
95  }
96 
97  // Clipping.
98  if( ( screenX >= ( dst->clip_rect.x + dst->clip_rect.w ) ) ||
99  ( screenY >= ( dst->clip_rect.y + dst->clip_rect.h ) ) ||
100  ( ( screenX + width ) <= dst->clip_rect.x ) ||
101  ( ( screenY + height ) <= dst->clip_rect.y ) ) {
102  return;
103  }
104 
105  if( screenX < dst->clip_rect.x ) {
106  int dX = dst->clip_rect.x - screenX;
107  screenX += dX;
108  width -= dX;
109  tX += dX;
110  }
111 
112  if( ( screenX + width ) > ( dst->clip_rect.x + dst->clip_rect.w ) ) {
113  int dX = ( screenX + width ) - ( dst->clip_rect.x + dst->clip_rect.w );
114  width -= dX;
115  }
116 
117  if( screenY < dst->clip_rect.y ) {
118  int dY = dst->clip_rect.y - screenY;
119  screenY += dY;
120  height -= dY;
121  tY += dY;
122  }
123 
124  if( ( screenY + height ) > ( dst->clip_rect.y + dst->clip_rect.h ) ) {
125  int dY = ( screenY + height ) - ( dst->clip_rect.y + dst->clip_rect.h );
126  height -= dY;
127  }
128 
129  if( ( 0 >= height ) || ( 0 >= width ) ) {
130  return;
131  }
132 
133  SDL_LockSurface( dst );
134 
135  unsigned char* srcData = reinterpret_cast< unsigned char* > ( src->pixels );
136  unsigned char* dstData = reinterpret_cast< unsigned char* > ( dst->pixels );
137 
138  // move data pointers to the start of the pixels we're copying
139  srcData += tY * src->pitch + tX * src->format->BytesPerPixel;
140  dstData += screenY * dst->pitch + screenX * dst->format->BytesPerPixel;
141 
142  switch( src->format->BitsPerPixel ) {
143  case 32: {
144  switch( dst->format->BitsPerPixel ) {
145  case 16: {
146  if( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) {
147  for( int y = height; y > 0; --y ) {
148  SDL_BlendRow_RGBA8_to_RGB565( srcData, dstData, alpha, width );
149  srcData += src->pitch;
150  dstData += dst->pitch;
151  }
152  }
153  }
154  break;
155 
156  case 24: {
157  for( int y = height; y > 0; --y ) {
158  SDL_BlendRow_RGBA8_to_RGB8( srcData, dstData, alpha, width );
159  srcData += src->pitch;
160  dstData += dst->pitch;
161  }
162  }
163  break;
164 
165  case 32: {
166  for( int y = height; y > 0; --y ) {
167  SDL_BlendRow_RGBA8_to_RGBA8( srcData, dstData, alpha, width );
168  srcData += src->pitch;
169  dstData += dst->pitch;
170  }
171  }
172  break;
173 
174  default:
175  break;
176  }
177  }
178  break;
179 
180  case 16: {
181  if( 0x000F == src->format->Amask ) {
182  if( ( 16 == dst->format->BitsPerPixel ) &&
183  ( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) ) {
184  for( int y = height; y > 0; --y ) {
185  SDL_BlendRow_RGBA4_to_RGB565( srcData, dstData, alpha, width );
186  srcData += src->pitch;
187  dstData += dst->pitch;
188  }
189  }
190  }
191  }
192  break;
193 
194  default:
195  break;
196  }
197 
198  SDL_UnlockSurface( dst );
199  }
200 
201  void zoomSurface(SDL_Surface* src, SDL_Surface* dst) {
202  SDL_Color* src_pointer = (SDL_Color*)src->pixels;
203  SDL_Color* src_help_pointer = src_pointer;
204  SDL_Color* dst_pointer = (SDL_Color*)dst->pixels;
205 
206  int x, y, *sx_ca, *sy_ca;
207  int dst_gap = dst->pitch - dst->w * dst->format->BytesPerPixel;
208  int sx = static_cast<int>(0xffff * src->w / dst->w);
209  int sy = static_cast<int>(0xffff * src->h / dst->h);
210  int sx_c = 0;
211  int sy_c = 0;
212 
213  // Allocates memory and calculates row wide&height
214  int* sx_a = (int*)malloc((dst->w + 1) * sizeof(Uint32));
215  if (sx_a == NULL) {
216  return;
217  } else {
218  sx_ca = sx_a;
219  for (x = 0; x <= dst->w; x++) {
220  *sx_ca = sx_c;
221  sx_ca++;
222  sx_c &= 0xffff;
223  sx_c += sx;
224  }
225  }
226  int* sy_a = (int*)malloc((dst->h + 1) * sizeof(Uint32));
227  if (sy_a == NULL) {
228  free(sx_a);
229  return;
230  } else {
231  sy_ca = sy_a;
232  for (y = 0; y <= dst->h; y++) {
233  *sy_ca = sy_c;
234  sy_ca++;
235  sy_c &= 0xffff;
236  sy_c += sy;
237  }
238  sy_ca = sy_a;
239  }
240 
241  // Transfers the image data
242 
243  if(SDL_MUSTLOCK(src))
244  SDL_LockSurface(src);
245  if(SDL_MUSTLOCK(dst))
246  SDL_LockSurface(dst);
247 
248  for (y = 0; y < dst->h; y++) {
249  src_pointer = src_help_pointer;
250  sx_ca = sx_a;
251  for (x = 0; x < dst->w; x++) {
252  *dst_pointer = *src_pointer;
253  sx_ca++;
254  src_pointer += (*sx_ca >> 16);
255  dst_pointer++;
256  }
257  sy_ca++;
258  src_help_pointer = (SDL_Color*)((Uint8*)src_help_pointer + (*sy_ca >> 16) * src->pitch);
259  dst_pointer = (SDL_Color*)((Uint8*)dst_pointer + dst_gap);
260  }
261 
262  if(SDL_MUSTLOCK(dst))
263  SDL_UnlockSurface(dst);
264  if(SDL_MUSTLOCK(src))
265  SDL_UnlockSurface(src);
266 
267  // Free memory
268  free(sx_a);
269  free(sy_a);
270  }
271 
272  SDL_Surface* getZoomedSurface(SDL_Surface * src, double zoomx, double zoomy) {
273  if (src == NULL)
274  return NULL;
275 
276  SDL_Surface *zoom_src;
277  SDL_Surface *zoom_dst;
278  int dst_w = static_cast<int>(round(src->w * zoomx));
279  int dst_h = static_cast<int>(round(src->h * zoomy));
280  if (dst_w < 1)
281  dst_w = 1;
282  if (dst_h < 1)
283  dst_h = 1;
284 
285  // If source surface has no alpha channel then convert it
286  if (src->format->Amask == 0) {
287  zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
288  RMASK, GMASK,
289  BMASK, AMASK);
290  SDL_BlitSurface(src, NULL, zoom_src, NULL);
291  } else {
292  zoom_src = src;
293  }
294  // Create destination surface
295  zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_w, dst_h, 32,
296  zoom_src->format->Rmask, zoom_src->format->Gmask,
297  zoom_src->format->Bmask, zoom_src->format->Amask);
298 
299  // Zoom surface
300  zoomSurface(zoom_src, zoom_dst);
301 
302  return zoom_dst;
303  }
304 
305  bool nearlyEqual(float a, float b) {
306  return ABS(a - b) <= 0.00001;
307  }
308 
309  void SDLImage::render(const Rect& rect, SDL_Surface* screen, unsigned char alpha) {
310  if (alpha == 0) {
311  return;
312  }
313 
314  if (rect.right() < 0 || rect.x > static_cast<int>(screen->w) || rect.bottom() < 0 || rect.y > static_cast<int>(screen->h)) {
315  return;
316  }
317  finalize();
318 
319  SDL_Surface* surface = screen;
320  SDL_Rect r;
321  r.x = rect.x;
322  r.y = rect.y;
323  r.w = rect.w;
324  r.h = rect.h;
325 
326  float scale_x = static_cast<float>(rect.w) / static_cast<float>(m_surface->w);
327  float scale_y = static_cast<float>(rect.h) / static_cast<float>(m_surface->h);
328  bool zoomed = false;
329  bool equal = false;
330 
331  if (!nearlyEqual(scale_x, 1.0) && !nearlyEqual(scale_y, 1.0)) {
332  zoomed = true;
333  if(nearlyEqual(m_scale_x, scale_x) && nearlyEqual(m_scale_y, scale_y)) {
334  equal = true;
335  } else {
336  m_scale_x = scale_x;
337  m_scale_y = scale_y;
338  }
339  }
340 
341  if (m_surface->format->Amask == 0) {
342  // Image has no alpha channel. This allows us to use the per-surface alpha.
343  if (m_last_alpha != alpha) {
344  m_last_alpha = alpha;
345  SDL_SetAlpha(m_surface, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
346  }
347  if (!zoomed) {
348  SDL_BlitSurface(m_surface, 0, surface, &r);
349  } else if (equal && m_zoom_surface) {
350  SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
351  } else {
352  SDL_FreeSurface(m_zoom_surface);
353  m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
354  SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
355  }
356  } else {
357  if( 255 != alpha ) {
358  // Special blitting routine with alpha blending:
359  // dst.rgb = ( src.rgb * src.a * alpha ) + ( dst.rgb * (255 - ( src.a * alpha ) ) );
360  if (!zoomed) {
361  SDL_BlitSurfaceWithAlpha( m_surface, 0, surface, &r, alpha );
362  } else if (equal && m_zoom_surface) {
363  SDL_BlitSurfaceWithAlpha(m_zoom_surface, 0, surface, &r, alpha );
364  } else {
365  SDL_FreeSurface(m_zoom_surface);
366  m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
367  SDL_BlitSurfaceWithAlpha(m_zoom_surface, 0, surface, &r, alpha );
368  }
369  } else {
370  if (!zoomed) {
371  SDL_BlitSurface(m_surface, 0, surface, &r);
372  } else if (equal && m_zoom_surface) {
373  SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
374  } else {
375  SDL_FreeSurface(m_zoom_surface);
376  m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
377  SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
378  }
379  }
380  }
381  }
382 
383  void SDLImage::finalize() {
384  if( m_finalized ) {
385  return;
386  }
387  m_finalized = true;
388  SDL_Surface *old_surface = m_surface;
389  Uint32 key = SDL_MapRGB(m_surface->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
390 
391  if (m_surface->format->Amask == 0) {
392  // only use color key if feature is enabled
393  if (RenderBackend::instance()->isColorKeyEnabled()) {
394  SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
395  }
396 
397  m_surface = SDL_DisplayFormat(m_surface);
398  } else {
399  RenderBackendSDL* be = static_cast<RenderBackendSDL*>(RenderBackend::instance());
400  m_isalphaoptimized = be->isAlphaOptimizerEnabled();
401  if( m_isalphaoptimized ) {
402  m_surface = optimize(m_surface);
403  } else {
404  SDL_SetAlpha(m_surface, SDL_SRCALPHA, 255);
405 
406  // only use color key if feature is enabled
407  if (RenderBackend::instance()->isColorKeyEnabled()) {
408  SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
409  }
410 
411  m_surface = SDL_DisplayFormatAlpha(m_surface);
412  }
413  }
414  SDL_FreeSurface(old_surface);
415  }
416 
417  SDL_Surface* SDLImage::optimize(SDL_Surface* src) {
418  // The algorithm is originally by "Tim Goya" <tuxdev103@gmail.com>
419  // Few modifications and adaptions by the FIFE team.
420  //
421  // It tries to determine whether an image with a alpha channel
422  // actually uses that. Often PNGs contains an alpha channels
423  // as they don't provide a colorkey feature(?) - so to speed
424  // up SDL rendering we try to remove the alpha channel.
425 
426  // As a reminder: src->format->Amask != 0 here
427 
428  int transparent = 0;
429  int opaque = 0;
430  int semitransparent = 0;
431  int alphasum = 0;
432  int alphasquaresum = 0;
433  bool colors[(1 << 12)];
434  memset(colors, 0, (1 << 12) * sizeof(bool));
435 
436  int bpp = src->format->BytesPerPixel;
437  if(SDL_MUSTLOCK(src)) {
438  SDL_LockSurface(src);
439  }
440  /* In the first pass through we calculate avg(alpha), avg(alpha^2)
441  and the number of semitransparent pixels.
442  We also try to find a useable color.
443  */
444  for(int y = 0;y < src->h;y++) {
445  for(int x = 0;x < src->w;x++) {
446  Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
447  Uint32 mapped = 0;
448  switch(bpp) {
449  case 1:
450  mapped = *pixel;
451  break;
452  case 2:
453  mapped = *(Uint16 *)pixel;
454  break;
455  case 3:
456 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
457  mapped |= pixel[0] << 16;
458  mapped |= pixel[1] << 8;
459  mapped |= pixel[2] << 0;
460 #else
461  mapped |= pixel[0] << 0;
462  mapped |= pixel[1] << 8;
463  mapped |= pixel[2] << 16;
464 #endif
465  break;
466  case 4:
467  mapped = *(Uint32 *)pixel;
468  break;
469  }
470  Uint8 red, green, blue, alpha;
471  SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
472  if(alpha < 16) {
473  transparent++;
474  } else if (alpha > 240) {
475  opaque++;
476  alphasum += alpha;
477  alphasquaresum += alpha*alpha;
478  } else {
479  semitransparent++;
480  alphasum += alpha;
481  alphasquaresum += alpha*alpha;
482  }
483  // mark the color as used.
484  if( alpha != 0 ) {
485  colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
486  }
487  }
488  }
489  int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
490  int alphavariance = 0;
491 
492  if(SDL_MUSTLOCK(src)) {
493  SDL_UnlockSurface(src);
494  }
495  alphasquaresum /= (opaque + semitransparent) ? (opaque + semitransparent) : 1;
496  alphavariance = alphasquaresum - avgalpha*avgalpha;
497  if(semitransparent > ((transparent + opaque + semitransparent) / 8)
498  && alphavariance > 16) {
499  FL_DBG(_log, LMsg("sdlimage")
500  << "Trying to alpha-optimize image. FAILED: real alpha usage. "
501  << " alphavariance=" << alphavariance
502  << " total=" << (transparent + opaque + semitransparent)
503  << " semitransparent=" << semitransparent
504  << "(" << (float(semitransparent)/(transparent + opaque + semitransparent))
505  << ")");
506  return SDL_DisplayFormatAlpha(src);
507  }
508 
509  // check availability of a suitable color as colorkey
510  int keycolor = -1;
511  for(int i = 0;i < (1 << 12);i++) {
512  if(!colors[i]) {
513  keycolor = i;
514  break;
515  }
516  }
517  if(keycolor == -1) {
518  FL_DBG(_log, LMsg("sdlimage") << "Trying to alpha-optimize image. FAILED: no free color");
519  return SDL_DisplayFormatAlpha(src);
520  }
521 
522  SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA) | SDL_SWSURFACE,
523  src->w, src->h,
524  src->format->BitsPerPixel,
525  src->format->Rmask, src->format->Gmask,
526  src->format->Bmask, 0);
527  bpp = dst->format->BytesPerPixel;
528 
529  Uint32 key = SDL_MapRGB(dst->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
530 
531  // if the global color key feature is disabled, then use the manually found color key
532  if (!RenderBackend::instance()->isColorKeyEnabled()) {
533  key = SDL_MapRGB(dst->format,
534  (((keycolor & 0xf00) >> 4) | 0xf),
535  ((keycolor & 0xf0) | 0xf),
536  (((keycolor & 0xf) << 4) | 0xf));
537  }
538 
539  if(SDL_MUSTLOCK(src)) {
540  SDL_LockSurface(src);
541  }
542  if(SDL_MUSTLOCK(dst)) {
543  SDL_LockSurface(dst);
544  }
545  for(int y = 0;y < dst->h;y++) {
546  for(int x = 0;x < dst->w;x++) {
547  Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
548  Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
549  Uint32 mapped = 0;
550  switch(bpp) {
551  case 1:
552  mapped = *srcpixel;
553  break;
554  case 2:
555  mapped = *(Uint16 *)srcpixel;
556  break;
557  case 3:
558 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
559  mapped |= srcpixel[0] << 16;
560  mapped |= srcpixel[1] << 8;
561  mapped |= srcpixel[2] << 0;
562 #else
563  mapped |= srcpixel[0] << 0;
564  mapped |= srcpixel[1] << 8;
565  mapped |= srcpixel[2] << 16;
566 #endif
567  break;
568  case 4:
569  mapped = *(Uint32 *)srcpixel;
570  break;
571  }
572  Uint8 red, green, blue, alpha;
573  SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
574  if(alpha < (avgalpha / 4)) {
575  mapped = key;
576  } else {
577  mapped = SDL_MapRGB(dst->format, red, green, blue);
578  }
579  switch(bpp) {
580  case 1:
581  *dstpixel = mapped;
582  break;
583  case 2:
584  *(Uint16 *)dstpixel = mapped;
585  break;
586  case 3:
587 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
588  dstpixel[0] = (mapped >> 16) & 0xff;
589  dstpixel[1] = (mapped >> 8) & 0xff;
590  dstpixel[2] = (mapped >> 0) & 0xff;
591 #else
592  dstpixel[0] = (mapped >> 0) & 0xff;
593  dstpixel[1] = (mapped >> 8) & 0xff;
594  dstpixel[2] = (mapped >> 16) & 0xff;
595 #endif
596  break;
597  case 4:
598  *(Uint32 *)dstpixel = mapped;
599  break;
600  }
601  }
602  }
603  if(SDL_MUSTLOCK(dst)) {
604  SDL_UnlockSurface(dst);
605  }
606  if(SDL_MUSTLOCK(src)) {
607  SDL_UnlockSurface(src);
608  }
609  // Using the per surface alpha value does not
610  // work out for mostly transparent pixels.
611  // Thus disabling the part here - this needs a
612  // more complex refactoring.
613  // if(avgalpha < 240) {
614  // SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
615  //}
616  SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
617  SDL_Surface *convert = SDL_DisplayFormat(dst);
618  SDL_FreeSurface(dst);
619  FL_DBG(_log, LMsg("sdlimage ")
620  << "Trying to alpha-optimize image. SUCCESS: colorkey is " << key);
621  return convert;
622  } // end optimize
623 
624  bool SDLImage::putPixel(int x, int y, int r, int g, int b, int a) {
625  if ((x < 0) || (x >= m_surface->w) || (y < 0) || (y >= m_surface->h)) {
626  return false;
627  }
628 
629  int bpp = m_surface->format->BytesPerPixel;
630  SDL_LockSurface(m_surface);
631  Uint8* p = (Uint8*)m_surface->pixels + y * m_surface->pitch + x * bpp;
632  Uint32 pixel = SDL_MapRGB(m_surface->format, r, g, b);
633  switch(bpp)
634  {
635  case 1:
636  *p = pixel;
637  break;
638 
639  case 2:
640  *(Uint16 *)p = pixel;
641  break;
642 
643  case 3:
644  if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
645  p[0] = (pixel >> 16) & 0xff;
646  p[1] = (pixel >> 8) & 0xff;
647  p[2] = pixel & 0xff;
648  }
649  else {
650  p[0] = pixel & 0xff;
651  p[1] = (pixel >> 8) & 0xff;
652  p[2] = (pixel >> 16) & 0xff;
653  }
654  break;
655 
656  case 4:
657  *(Uint32 *)p = pixel;
658  break;
659  }
660  SDL_UnlockSurface(m_surface);
661  return true;
662  }
663 
664  void SDLImage::drawLine(const Point& p1, const Point& p2, int r, int g, int b, int a) {
665  // Draw a line with Bresenham, imitated from guichan
666  int x1 = p1.x;
667  int x2 = p2.x;
668  int y1 = p1.y;
669  int y2 = p2.y;
670  int dx = ABS(x2 - x1);
671  int dy = ABS(y2 - y1);
672 
673  if (dx > dy) {
674  if (x1 > x2) {
675  // swap x1, x2
676  x1 ^= x2;
677  x2 ^= x1;
678  x1 ^= x2;
679 
680  // swap y1, y2
681  y1 ^= y2;
682  y2 ^= y1;
683  y1 ^= y2;
684  }
685 
686  if (y1 < y2) {
687  int y = y1;
688  int p = 0;
689 
690  for (int x = x1; x <= x2; x++) {
691  putPixel(x, y, r, g, b, a);
692  p += dy;
693  if (p * 2 >= dx) {
694  y++;
695  p -= dx;
696  }
697  }
698  }
699  else {
700  int y = y1;
701  int p = 0;
702 
703  for (int x = x1; x <= x2; x++) {
704  putPixel(x, y, r, g, b, a);
705 
706  p += dy;
707  if (p * 2 >= dx) {
708  y--;
709  p -= dx;
710  }
711  }
712  }
713  }
714  else {
715  if (y1 > y2) {
716  // swap y1, y2
717  y1 ^= y2;
718  y2 ^= y1;
719  y1 ^= y2;
720 
721  // swap x1, x2
722  x1 ^= x2;
723  x2 ^= x1;
724  x1 ^= x2;
725  }
726 
727  if (x1 < x2) {
728  int x = x1;
729  int p = 0;
730 
731  for (int y = y1; y <= y2; y++) {
732  putPixel(x, y, r, g, b, a);
733  p += dx;
734  if (p * 2 >= dy) {
735  x++;
736  p -= dy;
737  }
738  }
739  }
740  else {
741  int x = x1;
742  int p = 0;
743 
744  for (int y = y1; y <= y2; y++) {
745  putPixel(x, y, r, g, b, a);
746  p += dx;
747  if (p * 2 >= dy) {
748  x--;
749  p -= dy;
750  }
751  }
752  }
753  }
754  }
755 
756  void SDLImage::drawTriangle(const Point& p1, const Point& p2, const Point& p3, int r, int g, int b, int a) {
757  drawLine(p1, p2, r, g, b, a);
758  drawLine(p2, p3, r, g, b, a);
759  drawLine(p3, p1, r, g, b, a);
760  }
761 
762  void SDLImage::drawRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
763  Point p1, p2, p3, p4;
764 
765  p1.x = p.x;
766  p1.y = p.y;
767  p2.x = p.x+w;
768  p2.y = p.y;
769  p3.x = p.x+w;
770  p3.y = p.y+h;
771  p4.x = p.x;
772  p4.y = p.y+h;
773 
774  drawLine(p1, p2, r, g, b, a);
775  drawLine(p2, p3, r, g, b, a);
776  drawLine(p3, p4, r, g, b, a);
777  drawLine(p4, p1, r, g, b, a);
778  }
779 
780  void SDLImage::fillRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
781  SDL_Rect rect;
782  rect.x = p.x;
783  rect.y = p.y;
784  rect.w = w;
785  rect.h = h;
786 
787  Uint32 color = SDL_MapRGBA(m_surface->format, r, g, b, a);
788  SDL_FillRect(m_surface, &rect, color);
789  }
790 
791  void SDLImage::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4, int r, int g, int b, int a) {
792  drawLine(p1, p2, r, g, b, a);
793  drawLine(p2, p3, r, g, b, a);
794  drawLine(p3, p4, r, g, b, a);
795  drawLine(p4, p1, r, g, b, a);
796  }
797 
798  void SDLImage::drawVertex(const Point& p, const uint8_t size, int r, int g, int b, int a){
799  Point p1 = Point(p.x-size, p.y+size);
800  Point p2 = Point(p.x+size, p.y+size);
801  Point p3 = Point(p.x+size, p.y-size);
802  Point p4 = Point(p.x-size, p.y-size);
803 
804  drawLine(p1, p2, r, g, b, a);
805  drawLine(p2, p3, r, g, b, a);
806  drawLine(p3, p4, r, g, b, a);
807  drawLine(p4, p1, r, g, b, a);
808  }
809 
810  void SDLImage::drawLightPrimitive(const Point& p, uint8_t intensity, float radius, int subdivisions, float xstretch, float ystretch, uint8_t red, uint8_t green, uint8_t blue) {
811  }
812 
813  void SDLImage::saveImage(const std::string& filename) {
814  if(m_surface) {
815  const unsigned int swidth = getWidth();
816  const unsigned int sheight = getHeight();
817  SDL_Surface *surface = NULL;
818 
819  surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth,
820  sheight, 24,
821  RMASK, GMASK, BMASK, 0);
822 
823  if(surface == NULL) {
824  return;
825  }
826 
827  SDL_BlitSurface(m_surface, NULL, surface, NULL);
828 
829  saveAsPng(filename, *surface);
830  SDL_FreeSurface(surface);
831  }
832  }
833 
834  void SDLImage::setClipArea(const Rect& cliparea, bool clear) {
835  SDL_Rect rect;
836  rect.x = cliparea.x;
837  rect.y = cliparea.y;
838  rect.w = cliparea.w;
839  rect.h = cliparea.h;
840  SDL_SetClipRect(m_surface, &rect);
841  if (clear) {
842  SDL_FillRect(m_surface, &rect, 0x00);
843  }
844  }
845 }