The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
utils.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Support-routines for the SDL-graphics-library.
18  */
19 
20 #include "sdl/utils.hpp"
21 #include "sdl/rect.hpp"
22 #include "color.hpp"
23 #include "xBRZ/xbrz.hpp"
24 
25 #include <algorithm>
26 #include <cassert>
27 #include <cstring>
28 #include <iostream>
29 
30 #include <boost/circular_buffer.hpp>
31 #include <boost/math/constants/constants.hpp>
32 
34 {
35  SDL_version sdl_version;
36  SDL_GetVersion(&sdl_version);
37  return version_info(sdl_version.major, sdl_version.minor, sdl_version.patch);
38 }
39 
40 bool is_neutral(const surface& surf)
41 {
42  return (surf->format->BytesPerPixel == 4 &&
43  surf->format->Rmask == SDL_RED_MASK &&
44  (surf->format->Amask | SDL_ALPHA_MASK) == SDL_ALPHA_MASK);
45 }
46 
47 static SDL_PixelFormat& get_neutral_pixel_format()
48 {
49  static bool first_time = true;
50  static SDL_PixelFormat format;
51 
52  if(first_time) {
53  first_time = false;
54 
55 #if SDL_VERSION_ATLEAST(2, 0, 6)
56  surface surf(
57  SDL_CreateRGBSurfaceWithFormat(0, 1, 1, 32, SDL_PIXELFORMAT_ARGB8888));
58 #else
59  surface surf(
60  SDL_CreateRGBSurface(0, 1, 1, 32, SDL_RED_MASK, SDL_GREEN_MASK, SDL_BLUE_MASK, SDL_ALPHA_MASK));
61 #endif
62 
63  format = *surf->format;
64  format.palette = nullptr;
65  }
66 
67  return format;
68 }
69 
71 {
72  if(surf == nullptr) {
73  std::cerr << "null neutral surface...\n";
74  return nullptr;
75  }
76 
77  surface result = SDL_ConvertSurface(surf,&get_neutral_pixel_format(),0);
78 
79  return result;
80 }
81 
83 {
84  if (w < 0 || h < 0) {
85  std::cerr << "error : neutral surface with negative dimensions\n";
86  return nullptr;
87  }
88 
89  SDL_PixelFormat format = get_neutral_pixel_format();
90 
91 #if SDL_VERSION_ATLEAST(2, 0, 6)
92  surface result = SDL_CreateRGBSurfaceWithFormat(0, w, h, format.BitsPerPixel, format.format);
93 #else
94  surface result = SDL_CreateRGBSurface(0, w, h,
95  format.BitsPerPixel,
96  format.Rmask,
97  format.Gmask,
98  format.Bmask,
99  format.Amask);
100 #endif
101 
102  return result;
103 }
104 
106  const surface& surf, const unsigned w)
107 {
108  // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
109  assert(SDL_ALPHA_TRANSPARENT==0);
110 
111  if(surf == nullptr)
112  return nullptr;
113 
114  if(static_cast<int>(w) == surf->w) {
115  return surf;
116  }
117  assert(w > 0);
118 
119  surface dst(create_neutral_surface(w, surf->h));
120 
121  surface src(make_neutral_surface(surf));
122  // Now both surfaces are always in the "neutral" pixel format
123 
124  if(src == nullptr || dst == nullptr) {
125  std::cerr << "Could not create surface to scale onto\n";
126  return nullptr;
127  }
128 
129  {
130  // Extra scoping used for the surface_lock.
131  const_surface_lock src_lock(src);
132  surface_lock dst_lock(dst);
133 
134  const uint32_t* const src_pixels = src_lock.pixels();
135  uint32_t* dst_pixels = dst_lock.pixels();
136 
137  for(unsigned y = 0; y < static_cast<unsigned>(src->h); ++y) {
138  const uint32_t pixel = src_pixels [y * src->w];
139  for(unsigned x = 0; x < w; ++x) {
140 
141  *dst_pixels++ = pixel;
142 
143  }
144  }
145  }
146 
147  return dst;
148 }
149 
151  const surface& surf, const unsigned h)
152 {
153  // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
154  assert(SDL_ALPHA_TRANSPARENT==0);
155 
156  if(surf == nullptr)
157  return nullptr;
158 
159  if(static_cast<int>(h) == surf->h) {
160  return surf;
161  }
162  assert(h > 0);
163 
164  surface dst(create_neutral_surface(surf->w, h));
165 
166  surface src(make_neutral_surface(surf));
167  // Now both surfaces are always in the "neutral" pixel format
168 
169  if(src == nullptr || dst == nullptr) {
170  std::cerr << "Could not create surface to scale onto\n";
171  return nullptr;
172  }
173 
174  {
175  // Extra scoping used for the surface_lock.
176  const_surface_lock src_lock(src);
177  surface_lock dst_lock(dst);
178 
179  const uint32_t* const src_pixels = src_lock.pixels();
180  uint32_t* dst_pixels = dst_lock.pixels();
181 
182  for(unsigned y = 0; y < static_cast<unsigned>(h); ++y) {
183  for(unsigned x = 0; x < static_cast<unsigned>(src->w); ++x) {
184 
185  *dst_pixels++ = src_pixels[x];
186  }
187  }
188  }
189 
190  return dst;
191 }
192 
193 surface scale_surface_xbrz(const surface & surf, size_t z)
194 {
195  if(surf == nullptr)
196  return nullptr;
197 
198  if (z > 5) {
199  std::cerr << "Cannot use xbrz scaling with zoom factor > 5." << std::endl;
200  z = 1;
201  }
202 
203  if (z == 1) {
204  surface temp = surf; // TODO: no temp surface
205  return temp;
206  }
207 
208  surface dst(create_neutral_surface(surf->w *z, surf->h * z));
209 
210  if (z == 0) {
211  std::cerr << "Create an empty image\n";
212  return dst;
213  }
214 
215  surface src(make_neutral_surface(surf));
216 
217  if(src == nullptr || dst == nullptr) {
218  std::cerr << "Could not create surface to scale onto\n";
219  return nullptr;
220  }
221 
222  {
223  const_surface_lock src_lock(src);
224  surface_lock dst_lock(dst);
225 
226  xbrz::scale(z, src_lock.pixels(), dst_lock.pixels(), surf->w, surf->h);
227  }
228 
229  return dst;
230 }
231 
232 surface scale_surface_nn (const surface & surf, int w, int h)
233 {
234  // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
235  assert(SDL_ALPHA_TRANSPARENT==0);
236 
237  if (surf == nullptr)
238  return nullptr;
239 
240  if(w == surf->w && h == surf->h) {
241  return surf;
242  }
243  assert(w >= 0);
244  assert(h >= 0);
245 
247 
248  if (w == 0 || h ==0) {
249  std::cerr << "Create an empty image\n";
250  return dst;
251  }
252 
253  surface src(make_neutral_surface(surf));
254  // Now both surfaces are always in the "neutral" pixel format
255 
256  if(src == nullptr || dst == nullptr) {
257  std::cerr << "Could not create surface to scale onto\n";
258  return nullptr;
259  }
260 
261  {
262  const_surface_lock src_lock(src);
263  surface_lock dst_lock(dst);
264 
265  xbrz::nearestNeighborScale(src_lock.pixels(), surf->w, surf->h, dst_lock.pixels(), w, h);
266  }
267 
268  return dst;
269 }
270 
271 // NOTE: Don't pass this function 0 scaling arguments.
272 surface scale_surface(const surface &surf, int w, int h)
273 {
274  // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
275  assert(SDL_ALPHA_TRANSPARENT==0);
276 
277  if(surf == nullptr)
278  return nullptr;
279 
280  if(w == surf->w && h == surf->h) {
281  return surf;
282  }
283  assert(w >= 0);
284  assert(h >= 0);
285 
287 
288  if (w == 0 || h ==0) {
289  std::cerr << "Create an empty image\n";
290  return dst;
291  }
292 
293  surface src(make_neutral_surface(surf));
294  // Now both surfaces are always in the "neutral" pixel format
295 
296  if(src == nullptr || dst == nullptr) {
297  std::cerr << "Could not create surface to scale onto\n";
298  return nullptr;
299  }
300 
301  {
302  const_surface_lock src_lock(src);
303  surface_lock dst_lock(dst);
304 
305  const uint32_t* const src_pixels = src_lock.pixels();
306  uint32_t* const dst_pixels = dst_lock.pixels();
307 
308  fixed_t xratio = fxpdiv(surf->w,w);
309  fixed_t yratio = fxpdiv(surf->h,h);
310 
311  fixed_t ysrc = ftofxp(0.0);
312  for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
313  fixed_t xsrc = ftofxp(0.0);
314  for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
315  const int xsrcint = fxptoi(xsrc);
316  const int ysrcint = fxptoi(ysrc);
317 
318  const uint32_t* const src_word = src_pixels + ysrcint*src->w + xsrcint;
319  uint32_t* const dst_word = dst_pixels + ydst*dst->w + xdst;
320  const int dx = (xsrcint + 1 < src->w) ? 1 : 0;
321  const int dy = (ysrcint + 1 < src->h) ? src->w : 0;
322 
323  uint8_t r,g,b,a;
324  uint32_t rr,gg,bb,aa, temp;
325 
326  uint32_t pix[4], bilin[4];
327 
328  // This next part is the fixed point
329  // equivalent of "take everything to
330  // the right of the decimal point."
331  // These fundamental weights decide
332  // the contributions from various
333  // input pixels. The labels assume
334  // that the upper left corner of the
335  // screen ("northeast") is 0,0 but the
336  // code should still be consistent if
337  // the graphics origin is actually
338  // somewhere else.
339  //
340  // That is, the bilin array holds the
341  // "geometric" weights. I.E. If I'm scaling
342  // a 2 x 2 block a 10 x 10 block, then for
343  // pixel (2,2) of output, the upper left
344  // pixel should be 10:1 more influential than
345  // the upper right, and also 10:1 more influential
346  // than lower left, and 100:1 more influential
347  // than lower right.
348 
349  const fixed_t e = 0x000000FF & xsrc;
350  const fixed_t s = 0x000000FF & ysrc;
351  const fixed_t n = 0xFF - s;
352  // Not called "w" to avoid hiding a function parameter
353  // (would cause a compiler warning in MSVC2015 with /W4)
354  const fixed_t we = 0xFF - e;
355 
356  pix[0] = *src_word; // northwest
357  pix[1] = *(src_word + dx); // northeast
358  pix[2] = *(src_word + dy); // southwest
359  pix[3] = *(src_word + dx + dy); // southeast
360 
361  bilin[0] = n*we;
362  bilin[1] = n*e;
363  bilin[2] = s*we;
364  bilin[3] = s*e;
365 
366  int loc;
367  rr = bb = gg = aa = 0;
368  for (loc=0; loc<4; loc++) {
369  a = pix[loc] >> 24;
370  r = pix[loc] >> 16;
371  g = pix[loc] >> 8;
372  b = pix[loc] >> 0;
373 
374  //We also have to implement weighting by alpha for the RGB components
375  //If a unit has some parts solid and some parts translucent,
376  //i.e. a red cloak but a dark shadow, then when we scale in
377  //the shadow shouldn't appear to become red at the edges.
378  //This part also smoothly interpolates between alpha=0 being
379  //transparent and having no contribution, vs being opaque.
380  temp = (a * bilin[loc]);
381  rr += r * temp;
382  gg += g * temp;
383  bb += b * temp;
384  aa += temp;
385  }
386 
387  a = aa >> (16); // we average the alphas, they don't get weighted by any other factor besides bilin
388  if (a != 0) {
389  rr /= a; // finish alpha weighting: divide by sum of alphas
390  gg /= a;
391  bb /= a;
392  }
393  r = rr >> (16); // now shift over by 16 for the bilin part
394  g = gg >> (16);
395  b = bb >> (16);
396  *dst_word = (a << 24) + (r << 16) + (g << 8) + b;
397  }
398  }
399  }
400 
401  return dst;
402 }
403 
404 surface scale_surface_legacy(const surface &surf, int w, int h)
405 {
406  // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
407  assert(SDL_ALPHA_TRANSPARENT==0);
408 
409  if(surf == nullptr)
410  return nullptr;
411 
412  if(w == surf->w && h == surf->h) {
413  return surf;
414  }
415  assert(w >= 0);
416  assert(h >= 0);
417 
419 
420  surface src(make_neutral_surface(surf));
421  // Now both surfaces are always in the "neutral" pixel format
422 
423  if(src == nullptr || dst == nullptr) {
424  std::cerr << "Could not create surface to scale onto\n";
425  return nullptr;
426  }
427 
428  {
429  const_surface_lock src_lock(src);
430  surface_lock dst_lock(dst);
431 
432  const uint32_t* const src_pixels = src_lock.pixels();
433  uint32_t* const dst_pixels = dst_lock.pixels();
434 
435  fixed_t xratio = fxpdiv(surf->w,w);
436  fixed_t yratio = fxpdiv(surf->h,h);
437 
438  fixed_t ysrc = ftofxp(0.0);
439  for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
440  fixed_t xsrc = ftofxp(0.0);
441  for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
442  const int xsrcint = fxptoi(xsrc);
443  const int ysrcint = fxptoi(ysrc);
444 
445  const uint32_t* const src_word = src_pixels + ysrcint*src->w + xsrcint;
446  uint32_t* const dst_word = dst_pixels + ydst*dst->w + xdst;
447  const int dx = (xsrcint + 1 < src->w) ? 1 : 0;
448  const int dy = (ysrcint + 1 < src->h) ? src->w : 0;
449 
450  uint8_t r,g,b,a;
451  uint32_t rr,gg,bb,aa;
452  uint16_t avg_r, avg_g, avg_b;
453  uint32_t pix[4], bilin[4];
454 
455  // This next part is the fixed point
456  // equivalent of "take everything to
457  // the right of the decimal point."
458  // These fundamental weights decide
459  // the contributions from various
460  // input pixels. The labels assume
461  // that the upper left corner of the
462  // screen ("northeast") is 0,0 but the
463  // code should still be consistent if
464  // the graphics origin is actually
465  // somewhere else.
466 
467  const fixed_t east = 0x000000FF & xsrc;
468  const fixed_t south = 0x000000FF & ysrc;
469  const fixed_t north = 0xFF - south;
470  const fixed_t west = 0xFF - east;
471 
472  pix[0] = *src_word; // northwest
473  pix[1] = *(src_word + dx); // northeast
474  pix[2] = *(src_word + dy); // southwest
475  pix[3] = *(src_word + dx + dy); // southeast
476 
477  bilin[0] = north*west;
478  bilin[1] = north*east;
479  bilin[2] = south*west;
480  bilin[3] = south*east;
481 
482  // Scope out the neighboorhood, see
483  // what the pixel values are like.
484 
485  int count = 0;
486  avg_r = avg_g = avg_b = 0;
487  int loc;
488  for (loc=0; loc<4; loc++) {
489  a = pix[loc] >> 24;
490  r = pix[loc] >> 16;
491  g = pix[loc] >> 8;
492  b = pix[loc] >> 0;
493  if (a != 0) {
494  avg_r += r;
495  avg_g += g;
496  avg_b += b;
497  count++;
498  }
499  }
500  if (count>0) {
501  avg_r /= count;
502  avg_b /= count;
503  avg_g /= count;
504  }
505 
506  // Perform modified bilinear interpolation.
507  // Don't trust any color information from
508  // an RGBA sample when the alpha channel
509  // is set to fully transparent.
510  //
511  // Some of the input images are hex tiles,
512  // created using a hexagon shaped alpha channel
513  // that is either set to full-on or full-off.
514 
515  rr = gg = bb = aa = 0;
516  for (loc=0; loc<4; loc++) {
517  a = pix[loc] >> 24;
518  r = pix[loc] >> 16;
519  g = pix[loc] >> 8;
520  b = pix[loc] >> 0;
521  if (a == 0) {
522  r = static_cast<uint8_t>(avg_r);
523  g = static_cast<uint8_t>(avg_g);
524  b = static_cast<uint8_t>(avg_b);
525  }
526  rr += r * bilin[loc];
527  gg += g * bilin[loc];
528  bb += b * bilin[loc];
529  aa += a * bilin[loc];
530  }
531  r = rr >> 16;
532  g = gg >> 16;
533  b = bb >> 16;
534  a = aa >> 16;
535  *dst_word = (a << 24) + (r << 16) + (g << 8) + b;
536  }
537  }
538  }
539 
540  return dst;
541 }
542 
543 
544 surface scale_surface_sharp(const surface& surf, int w, int h)
545 {
546  // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
547  assert(SDL_ALPHA_TRANSPARENT==0);
548 
549  if(surf == nullptr)
550  return nullptr;
551 
552  if(w == surf->w && h == surf->h) {
553  return surf;
554  }
555  assert(w >= 0);
556  assert(h >= 0);
557 
559 
560  if (w == 0 || h ==0) {
561  std::cerr << "Create an empty image\n";
562  return dst;
563  }
564 
565  surface src(make_neutral_surface(surf));
566  // Now both surfaces are always in the "neutral" pixel format
567 
568  if(src == nullptr || dst == nullptr) {
569  std::cerr << "Could not create surface to scale onto\n";
570  return nullptr;
571  }
572 
573  {
574  const_surface_lock src_lock(src);
575  surface_lock dst_lock(dst);
576 
577  const uint32_t* const src_pixels = src_lock.pixels();
578  uint32_t* const dst_pixels = dst_lock.pixels();
579 
580  float xratio = static_cast<float>(surf->w) / w;
581  float yratio = static_cast<float>(surf->h) / h;
582 
583  float ysrc = 0.0f;
584  for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
585  float xsrc = 0.0f;
586  for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
587  float red = 0.0f, green = 0.0f, blue = 0.0f, alpha = 0.0f;
588 
589  float summation = 0.0f;
590 
591  // We now have a rectangle, (xsrc,ysrc,xratio,yratio)
592  // which we want to derive the pixel from
593  for(float xloc = xsrc; xloc < xsrc+xratio; xloc += 1) {
594  const float xsize = std::min<float>(std::floor(xloc+1)-xloc,xsrc+xratio-xloc);
595 
596  for(float yloc = ysrc; yloc < ysrc+yratio; yloc += 1) {
597  const int xsrcint = std::max<int>(0,std::min<int>(src->w-1,static_cast<int>(xsrc)));
598  const int ysrcint = std::max<int>(0,std::min<int>(src->h-1,static_cast<int>(ysrc)));
599  const float ysize = std::min<float>(std::floor(yloc+1)-yloc,ysrc+yratio-yloc);
600 
601  uint8_t r,g,b,a;
602 
603  SDL_GetRGBA(src_pixels[ysrcint*src->w + xsrcint],src->format,&r,&g,&b,&a);
604  float value = xsize * ysize;
605  summation += value;
606  if (!a) continue;
607  value *= a;
608  alpha += value;
609  red += r * value;
610  green += g * value;
611  blue += b * value;
612  }
613  }
614 
615  if (alpha != 0) {
616  red = red / alpha + 0.5f;
617  green = green / alpha + 0.5f;
618  blue = blue / alpha + 0.5f;
619  alpha = alpha / summation + 0.5f;
620  }
621 
622  dst_pixels[ydst*dst->w + xdst] = SDL_MapRGBA(
623  dst->format
624  , static_cast<uint8_t>(red)
625  , static_cast<uint8_t>(green)
626  , static_cast<uint8_t>(blue)
627  , static_cast<uint8_t>(alpha));
628  }
629 
630  }
631  }
632 
633  return dst;
634 }
635 
636 
637 surface tile_surface(const surface& surf, int w, int h, bool centered)
638 {
639  if (surf->w == w && surf->h == h) {
640  return surf;
641  }
642 
643  surface dest(create_neutral_surface(w, h));
644  surface src(make_neutral_surface(surf));
645 
646  if (src == nullptr || dest == nullptr) {
647  std::cerr << "failed to make neutral surface\n";
648  return nullptr;
649  }
650 
651  {
652  const_surface_lock srclock(src);
653  surface_lock destlock(dest);
654 
655  const uint32_t* srcpixels = srclock.pixels();
656  uint32_t* destpixels = destlock.pixels();
657 
658  const int& sw = src->w;
659  const int& sh = src->h;
660 
661  const int xoff = centered ? (w - sw) / 2 : 0;
662  const int yoff = centered ? (h - sh) / 2 : 0;
663 
664  for (int i = 0; i<w*h; ++i) {
665  int x = ((i % w) - xoff);
666  int y = ((i / w) - yoff);
667 
668  while ((x += sw) < 0) { /* DO NOTHING */ }
669  while ((y += sh) < 0) { /* DO NOTHING */ }
670 
671  const int sx = x % sw;
672  const int sy = y % sh;
673 
674  destpixels[i] = srcpixels[sy*sw + sx];
675  }
676  }
677 
678  return dest;
679 }
680 
681 surface adjust_surface_color(const surface &surf, int red, int green, int blue)
682 {
683  if(surf == nullptr)
684  return nullptr;
685 
686  if((red == 0 && green == 0 && blue == 0)) {
687  surface temp = surf; // TODO: remove temp surface
688  return temp;
689  }
690 
691  surface nsurf(make_neutral_surface(surf));
692 
693  if(nsurf == nullptr) {
694  std::cerr << "failed to make neutral surface\n";
695  return nullptr;
696  }
697 
698  {
699  surface_lock lock(nsurf);
700  uint32_t* beg = lock.pixels();
701  uint32_t* end = beg + nsurf->w*surf->h;
702 
703  while(beg != end) {
704  uint8_t alpha = (*beg) >> 24;
705 
706  if(alpha) {
707  uint8_t r, g, b;
708  r = (*beg) >> 16;
709  g = (*beg) >> 8;
710  b = (*beg) >> 0;
711 
712  r = std::max<int>(0,std::min<int>(255,int(r)+red));
713  g = std::max<int>(0,std::min<int>(255,int(g)+green));
714  b = std::max<int>(0,std::min<int>(255,int(b)+blue));
715 
716  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
717  }
718 
719  ++beg;
720  }
721  }
722 
723  return nsurf;
724 }
725 
727 {
728  if(surf == nullptr)
729  return nullptr;
730 
731  surface nsurf(make_neutral_surface(surf));
732  if(nsurf == nullptr) {
733  std::cerr << "failed to make neutral surface\n";
734  return nullptr;
735  }
736 
737  {
738  surface_lock lock(nsurf);
739  uint32_t* beg = lock.pixels();
740  uint32_t* end = beg + nsurf->w*surf->h;
741 
742  while(beg != end) {
743  uint8_t alpha = (*beg) >> 24;
744 
745  if(alpha) {
746  uint8_t r, g, b;
747  r = (*beg) >> 16;
748  g = (*beg) >> 8;
749  b = (*beg);
750  //const uint8_t avg = (red+green+blue)/3;
751 
752  // Use the correct formula for RGB to grayscale conversion.
753  // Ok, this is no big deal :)
754  // The correct formula being:
755  // gray=0.299red+0.587green+0.114blue
756  const uint8_t avg = static_cast<uint8_t>((
757  77 * static_cast<uint16_t>(r) +
758  150 * static_cast<uint16_t>(g) +
759  29 * static_cast<uint16_t>(b) ) / 256);
760 
761  *beg = (alpha << 24) | (avg << 16) | (avg << 8) | avg;
762  }
763 
764  ++beg;
765  }
766  }
767 
768  return nsurf;
769 }
770 
771 surface monochrome_image(const surface &surf, const int threshold)
772 {
773  if(surf == nullptr)
774  return nullptr;
775 
776  surface nsurf(make_neutral_surface(surf));
777  if(nsurf == nullptr) {
778  std::cerr << "failed to make neutral surface\n";
779  return nullptr;
780  }
781 
782  {
783  surface_lock lock(nsurf);
784  uint32_t* beg = lock.pixels();
785  uint32_t* end = beg + nsurf->w*surf->h;
786 
787  while(beg != end) {
788  uint8_t alpha = (*beg) >> 24;
789 
790  if(alpha) {
791  uint8_t r, g, b, result;
792  r = (*beg) >> 16;
793  g = (*beg) >> 8;
794  b = (*beg);
795 
796  // first convert the pixel to grayscale
797  // if the resulting value is above the threshold make it black
798  // else make it white
799  result = static_cast<uint8_t>(0.299 * r + 0.587 * g + 0.114 * b) > threshold ? 255 : 0;
800 
801  *beg = (alpha << 24) | (result << 16) | (result << 8) | result;
802  }
803 
804  ++beg;
805  }
806  }
807 
808  return nsurf;
809 }
810 
812 {
813  if(surf == nullptr)
814  return nullptr;
815 
816  surface nsurf(make_neutral_surface(surf));
817  if(nsurf == nullptr) {
818  std::cerr << "failed to make neutral surface\n";
819  return nullptr;
820  }
821 
822  {
823  surface_lock lock(nsurf);
824  uint32_t* beg = lock.pixels();
825  uint32_t* end = beg + nsurf->w*surf->h;
826 
827  while(beg != end) {
828  uint8_t alpha = (*beg) >> 24;
829 
830  if(alpha) {
831  uint8_t r, g, b;
832  r = (*beg) >> 16;
833  g = (*beg) >> 8;
834  b = (*beg);
835 
836  // this is the formula for applying a sepia effect
837  // that can be found on various web sites
838  // for example here: https://software.intel.com/sites/default/files/article/346220/sepiafilter-intelcilkplus.pdf
839  uint8_t outRed = std::min(255, static_cast<int>((r * 0.393) + (g * 0.769) + (b * 0.189)));
840  uint8_t outGreen = std::min(255, static_cast<int>((r * 0.349) + (g * 0.686) + (b * 0.168)));
841  uint8_t outBlue = std::min(255, static_cast<int>((r * 0.272) + (g * 0.534) + (b * 0.131)));
842 
843  *beg = (alpha << 24) | (outRed << 16) | (outGreen << 8) | (outBlue);
844  }
845 
846  ++beg;
847  }
848  }
849 
850  return nsurf;
851 }
852 
853 surface negative_image(const surface &surf, const int thresholdR, const int thresholdG, const int thresholdB)
854 {
855  if(surf == nullptr)
856  return nullptr;
857 
858  surface nsurf(make_neutral_surface(surf));
859  if(nsurf == nullptr) {
860  std::cerr << "failed to make neutral surface\n";
861  return nullptr;
862  }
863 
864  {
865  surface_lock lock(nsurf);
866  uint32_t* beg = lock.pixels();
867  uint32_t* end = beg + nsurf->w*surf->h;
868 
869  while(beg != end) {
870  uint8_t alpha = (*beg) >> 24;
871 
872  if(alpha) {
873  uint8_t r, g, b, newR, newG, newB;
874  r = (*beg) >> 16;
875  g = (*beg) >> 8;
876  b = (*beg);
877 
878  // invert he channel only if its value is greater than the supplied threshold
879  // this can be used for solarization effects
880  // for a full negative effect, use a value of -1
881  // 255 is a no-op value (doesn't do anything, since a uint8_t cannot contain a greater value than that)
882  newR = r > thresholdR ? 255 - r : r;
883  newG = g > thresholdG ? 255 - g : g;
884  newB = b > thresholdB ? 255 - b : b;
885 
886  *beg = (alpha << 24) | (newR << 16) | (newG << 8) | (newB);
887  }
888 
889  ++beg;
890  }
891  }
892 
893  return nsurf;
894 }
895 
897 {
898  if(surf == nullptr)
899  return nullptr;
900 
901  surface nsurf(make_neutral_surface(surf));
902  if(nsurf == nullptr) {
903  std::cerr << "failed to make neutral surface\n";
904  return nullptr;
905  }
906 
907  {
908  surface_lock lock(nsurf);
909  uint32_t* beg = lock.pixels();
910  uint32_t* end = beg + nsurf->w*surf->h;
911 
912  while(beg != end) {
913  uint8_t alpha = (*beg) >> 24;
914 
915  *beg = (0xff << 24) | (alpha << 16) | (alpha << 8) | alpha;
916 
917  ++beg;
918  }
919  }
920 
921  return nsurf;
922 }
923 
925 {
926  if(surf == nullptr)
927  return nullptr;
928 
929  surface nsurf(make_neutral_surface(surf));
930  if(nsurf == nullptr) {
931  std::cerr << "failed to make neutral surface\n";
932  return nullptr;
933  }
934 
935  {
936  surface_lock lock(nsurf);
937  uint32_t* beg = lock.pixels();
938  uint32_t* end = beg + nsurf->w*surf->h;
939 
940  while(beg != end) {
941 
942  *beg = 0xff000000 | *beg;
943 
944  ++beg;
945  }
946  }
947 
948  return nsurf;
949 }
950 
951 
953 {
954  if(surf == nullptr)
955  return nullptr;
956 
957  // we blur it, and reuse the neutral surface created by the blur function
958  surface nsurf (blur_alpha_surface(surf, 2));
959 
960  if(nsurf == nullptr) {
961  std::cerr << "failed to blur the shadow surface\n";
962  return nullptr;
963  }
964 
965  {
966  surface_lock lock(nsurf);
967  uint32_t* beg = lock.pixels();
968  uint32_t* end = beg + nsurf->w*surf->h;
969 
970  while(beg != end) {
971  uint8_t alpha = (*beg) >> 24;
972 
973  if(alpha) {
974  // increase alpha and color in black (RGB=0)
975  // with some stupid optimization for handling maximum values
976  if (alpha < 255/4)
977  *beg = (alpha*4) << 24;
978  else
979  *beg = 0xFF000000; // we hit the maximum
980  }
981 
982  ++beg;
983  }
984  }
985 
986  return nsurf;
987 }
988 
990  if(surf == nullptr)
991  return nullptr;
992 
993  surface nsurf(make_neutral_surface(surf));
994  if(nsurf == nullptr) {
995  std::cerr << "failed to make neutral surface\n";
996  return nullptr;
997  }
998 
999  {
1000  surface_lock lock(nsurf);
1001  uint32_t* beg = lock.pixels();
1002  uint32_t* end = beg + nsurf->w*surf->h;
1003 
1004  while(beg != end) {
1005  uint8_t alpha = (*beg) >> 24;
1006 
1007  if(alpha) {
1008  uint8_t red, green, blue, newRed, newGreen, newBlue, newAlpha;
1009  red = (*beg) >> 16;
1010  green = (*beg) >> 8;
1011  blue = (*beg);
1012 
1013  switch (r) {
1014  case RED:
1015  newRed = red;
1016  break;
1017  case GREEN:
1018  newRed = green;
1019  break;
1020  case BLUE:
1021  newRed = blue;
1022  break;
1023  case ALPHA:
1024  newRed = alpha;
1025  break;
1026  default:
1027  return nullptr;
1028  }
1029 
1030  switch (g) {
1031  case RED:
1032  newGreen = red;
1033  break;
1034  case GREEN:
1035  newGreen = green;
1036  break;
1037  case BLUE:
1038  newGreen = blue;
1039  break;
1040  case ALPHA:
1041  newGreen = alpha;
1042  break;
1043  default:
1044  return nullptr;
1045  }
1046 
1047  switch (b) {
1048  case RED:
1049  newBlue = red;
1050  break;
1051  case GREEN:
1052  newBlue = green;
1053  break;
1054  case BLUE:
1055  newBlue = blue;
1056  break;
1057  case ALPHA:
1058  newBlue = alpha;
1059  break;
1060  default:
1061  return nullptr;
1062  }
1063 
1064  switch (a) {
1065  case RED:
1066  newAlpha = red;
1067  break;
1068  case GREEN:
1069  newAlpha = green;
1070  break;
1071  case BLUE:
1072  newAlpha = blue;
1073  break;
1074  case ALPHA:
1075  newAlpha = alpha;
1076  break;
1077  default:
1078  return nullptr;
1079  }
1080 
1081  *beg = (newAlpha << 24) | (newRed << 16) | (newGreen << 8) | newBlue;
1082  }
1083 
1084  ++beg;
1085  }
1086  }
1087 
1088  return nsurf;
1089 }
1090 
1092 {
1093  if(surf == nullptr)
1094  return nullptr;
1095 
1096  if(map_rgb.empty()) {
1097  return surf;
1098  }
1099 
1100  surface nsurf(make_neutral_surface(surf));
1101  if(nsurf == nullptr) {
1102  std::cerr << "failed to make neutral surface" << std::endl;
1103  return nullptr;
1104  }
1105 
1106  surface_lock lock(nsurf);
1107  uint32_t* beg = lock.pixels();
1108  uint32_t* end = beg + nsurf->w*surf->h;
1109 
1110  while(beg != end) {
1111  uint8_t alpha = (*beg) >> 24;
1112 
1113  // Don't recolor invisible pixels.
1114  if(alpha) {
1115  // Palette use only RGB channels, so remove alpha
1116  uint32_t oldrgb = (*beg) | 0xFF000000;
1117 
1118  auto i = map_rgb.find(color_t::from_argb_bytes(oldrgb));
1119  if(i != map_rgb.end()) {
1120  *beg = (alpha << 24) | (i->second.to_argb_bytes() & 0x00FFFFFF);
1121  }
1122  }
1123 
1124  ++beg;
1125  }
1126 
1127  return nsurf;
1128 }
1129 
1131 {
1132  if(surf == nullptr) {
1133  return nullptr;
1134  }
1135 
1136  surface nsurf(make_neutral_surface(surf));
1137 
1138  if(nsurf == nullptr) {
1139  std::cerr << "could not make neutral surface...\n";
1140  return nullptr;
1141  }
1142 
1143  {
1144  surface_lock lock(nsurf);
1145  uint32_t* beg = lock.pixels();
1146  uint32_t* end = beg + nsurf->w*surf->h;
1147 
1148  if (amount < 0) amount = 0;
1149  while(beg != end) {
1150  uint8_t alpha = (*beg) >> 24;
1151 
1152  if(alpha) {
1153  uint8_t r, g, b;
1154  r = (*beg) >> 16;
1155  g = (*beg) >> 8;
1156  b = (*beg);
1157 
1158  r = std::min<unsigned>(unsigned(fxpmult(r, amount)),255);
1159  g = std::min<unsigned>(unsigned(fxpmult(g, amount)),255);
1160  b = std::min<unsigned>(unsigned(fxpmult(b, amount)),255);
1161 
1162  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
1163  }
1164 
1165  ++beg;
1166  }
1167  }
1168 
1169  return nsurf;
1170 }
1171 
1173 {
1174  if(surf == nullptr) {
1175  return;
1176  }
1177 
1178  SDL_SetSurfaceAlphaMod(surf, uint8_t(amount));
1179 }
1180 
1181 surface adjust_surface_alpha_add(const surface &surf, int amount)
1182 {
1183  if(surf== nullptr) {
1184  return nullptr;
1185  }
1186 
1187  surface nsurf(make_neutral_surface(surf));
1188 
1189  if(nsurf == nullptr) {
1190  std::cerr << "could not make neutral surface...\n";
1191  return nullptr;
1192  }
1193 
1194  {
1195  surface_lock lock(nsurf);
1196  uint32_t* beg = lock.pixels();
1197  uint32_t* end = beg + nsurf->w*surf->h;
1198 
1199  while(beg != end) {
1200  uint8_t alpha = (*beg) >> 24;
1201 
1202  if(alpha) {
1203  uint8_t r, g, b;
1204  r = (*beg) >> 16;
1205  g = (*beg) >> 8;
1206  b = (*beg);
1207 
1208  alpha = uint8_t(std::max<int>(0,std::min<int>(255,int(alpha) + amount)));
1209  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
1210  }
1211 
1212  ++beg;
1213  }
1214  }
1215 
1216  return nsurf;
1217 }
1218 
1219 surface mask_surface(const surface &surf, const surface &mask, bool* empty_result, const std::string& filename)
1220 {
1221  if(surf == nullptr) {
1222  return nullptr;
1223  }
1224  if(mask == nullptr) {
1225  return surf;
1226  }
1227 
1228  surface nsurf = make_neutral_surface(surf);
1229  surface nmask(make_neutral_surface(mask));
1230 
1231  if(nsurf == nullptr || nmask == nullptr) {
1232  std::cerr << "could not make neutral surface...\n";
1233  return nullptr;
1234  }
1235  if (nsurf->w != nmask->w) {
1236  // we don't support efficiently different width.
1237  // (different height is not a real problem)
1238  // This function is used on all hexes and usually only for that
1239  // so better keep it simple and efficient for the normal case
1240  std::stringstream ss;
1241  ss << "Detected an image with bad dimensions: ";
1242  if(!filename.empty()) ss << filename << ": ";
1243  ss << nsurf->w << "x" << nsurf->h << "\n";
1244  std::cerr << ss.str();
1245  std::cerr << "It will not be masked, please use: "<< nmask->w << "x" << nmask->h << "\n";
1246  return nsurf;
1247  }
1248 
1249  bool empty = true;
1250  {
1251  surface_lock lock(nsurf);
1252  const_surface_lock mlock(nmask);
1253 
1254  uint32_t* beg = lock.pixels();
1255  uint32_t* end = beg + nsurf->w*surf->h;
1256  const uint32_t* mbeg = mlock.pixels();
1257  const uint32_t* mend = mbeg + nmask->w*nmask->h;
1258 
1259  while(beg != end && mbeg != mend) {
1260  uint8_t alpha = (*beg) >> 24;
1261 
1262  if(alpha) {
1263  uint8_t r, g, b;
1264  r = (*beg) >> 16;
1265  g = (*beg) >> 8;
1266  b = (*beg);
1267 
1268  uint8_t malpha = (*mbeg) >> 24;
1269  if (alpha > malpha) {
1270  alpha = malpha;
1271  }
1272  if(alpha)
1273  empty = false;
1274 
1275  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
1276  }
1277 
1278  ++beg;
1279  ++mbeg;
1280  }
1281  }
1282  if(empty_result)
1283  *empty_result = empty;
1284 
1285  return nsurf;
1286 }
1287 
1288 bool in_mask_surface(const surface &surf, const surface &mask)
1289 {
1290  if(surf == nullptr) {
1291  return false;
1292  }
1293  if(mask == nullptr){
1294  return true;
1295  }
1296 
1297  if (surf->w != mask->w || surf->h != mask->h ) {
1298  // not same size, consider it doesn't fit
1299  return false;
1300  }
1301 
1302  surface nsurf = make_neutral_surface(surf);
1303  surface nmask(make_neutral_surface(mask));
1304 
1305  if(nsurf == nullptr || nmask == nullptr) {
1306  std::cerr << "could not make neutral surface...\n";
1307  return false;
1308  }
1309 
1310  {
1311  surface_lock lock(nsurf);
1312  const_surface_lock mlock(nmask);
1313 
1314  const uint32_t* mbeg = mlock.pixels();
1315  const uint32_t* mend = mbeg + nmask->w*nmask->h;
1316  uint32_t* beg = lock.pixels();
1317  // no need for 'end', because both surfaces have same size
1318 
1319  while(mbeg != mend) {
1320  uint8_t malpha = (*mbeg) >> 24;
1321  if(malpha == 0) {
1322  uint8_t alpha = (*beg) >> 24;
1323  if (alpha)
1324  return false;
1325  }
1326  ++mbeg;
1327  ++beg;
1328  }
1329  }
1330 
1331  return true;
1332 }
1333 
1334 surface submerge_alpha(const surface &surf, int depth, float alpha_base, float alpha_delta)
1335 {
1336  if(surf== nullptr) {
1337  return nullptr;
1338  }
1339 
1340  surface nsurf(make_neutral_surface(surf));
1341 
1342  {
1343  surface_lock lock(nsurf);
1344 
1345  uint32_t* beg = lock.pixels();
1346  uint32_t* limit = beg + (nsurf->h-depth) * nsurf->w ;
1347  uint32_t* end = beg + nsurf->w * nsurf->h;
1348  beg = limit; // directlt jump to the bottom part
1349 
1350  while(beg != end){
1351  uint8_t alpha = (*beg) >> 24;
1352 
1353  if(alpha) {
1354  uint8_t r, g, b;
1355  r = (*beg) >> 16;
1356  g = (*beg) >> 8;
1357  b = (*beg);
1358  int d = (beg-limit)/nsurf->w; // current depth in pixels
1359  float a = alpha_base - d * alpha_delta;
1360  fixed_t amount = ftofxp(a<0?0:a);
1361  alpha = std::min<unsigned>(unsigned(fxpmult(alpha,amount)),255);
1362  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
1363  }
1364 
1365  ++beg;
1366  }
1367 
1368 /*
1369  for(int y = submerge_height; y < nsurf->h; ++y) {
1370  uint32_t* cur = beg + y * nsurf->w;
1371  uint32_t* row_end = beg + (y+1) * nsurf->w;
1372  float d = y * 1.0 / depth;
1373  double a = 0.2;//std::max<double>(0, (1-d)*0.3);
1374  fixed_t amount = ftofxp(a);
1375  while(cur != row_end) {
1376  uint8_t alpha = (*cur) >> 24;
1377 
1378  if(alpha) {
1379  uint8_t r, g, b;
1380  r = (*cur) >> 16;
1381  g = (*cur) >> 8;
1382  b = (*cur);
1383  alpha = std::min<unsigned>(unsigned(fxpmult(alpha,amount)),255);
1384  *cur = (alpha << 24) + (r << 16) + (g << 8) + b;
1385  }
1386 
1387  ++cur;
1388  }
1389  }*/
1390 
1391  }
1392 
1393  return nsurf;
1394 }
1395 
1396 surface light_surface(const surface &surf, const surface &lightmap)
1397 {
1398  if(surf == nullptr) {
1399  return nullptr;
1400  }
1401  if(lightmap == nullptr) {
1402  return surf;
1403  }
1404 
1405  surface nsurf = make_neutral_surface(surf);
1406 
1407  if(nsurf == nullptr) {
1408  std::cerr << "could not make neutral surface...\n";
1409  return nullptr;
1410  }
1411  if (nsurf->w != lightmap->w) {
1412  // we don't support efficiently different width.
1413  // (different height is not a real problem)
1414  // This function is used on all hexes and usually only for that
1415  // so better keep it simple and efficient for the normal case
1416  std::cerr << "Detected an image with bad dimensions: " << nsurf->w << "x" << nsurf->h << "\n";
1417  std::cerr << "It will not be lighted, please use: "<< lightmap->w << "x" << lightmap->h << "\n";
1418  return nsurf;
1419  }
1420  {
1421  surface_lock lock(nsurf);
1422  const_surface_lock llock(lightmap);
1423 
1424  uint32_t* beg = lock.pixels();
1425  uint32_t* end = beg + nsurf->w * nsurf->h;
1426  const uint32_t* lbeg = llock.pixels();
1427  const uint32_t* lend = lbeg + lightmap->w * lightmap->h;
1428 
1429  while(beg != end && lbeg != lend) {
1430  uint8_t alpha = (*beg) >> 24;
1431  if(alpha) {
1432  uint8_t lr, lg, lb;
1433 
1434  lr = (*lbeg) >> 16;
1435  lg = (*lbeg) >> 8;
1436  lb = (*lbeg);
1437 
1438  uint8_t r, g, b;
1439  r = (*beg) >> 16;
1440  g = (*beg) >> 8;
1441  b = (*beg);
1442 
1443  int dr = (static_cast<int>(lr) - 128) * 2;
1444  int dg = (static_cast<int>(lg) - 128) * 2;
1445  int db = (static_cast<int>(lb) - 128) * 2;
1446  //note that r + dr will promote r to int (needed to avoid uint8_t math)
1447  r = std::max<int>(0,std::min<int>(255, r + dr));
1448  g = std::max<int>(0,std::min<int>(255, g + dg));
1449  b = std::max<int>(0,std::min<int>(255, b + db));
1450 
1451  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
1452  }
1453  ++beg;
1454  ++lbeg;
1455  }
1456  }
1457 
1458  return nsurf;
1459 }
1460 
1461 
1462 surface blur_surface(const surface &surf, int depth)
1463 {
1464  if(surf == nullptr) {
1465  return nullptr;
1466  }
1467 
1468  surface res = make_neutral_surface(surf);
1469 
1470  if(res == nullptr) {
1471  std::cerr << "could not make neutral surface...\n";
1472  return nullptr;
1473  }
1474 
1475  SDL_Rect rect {0, 0, surf->w, surf->h};
1476  blur_surface(res, rect, depth);
1477 
1478  return res;
1479 }
1480 
1481 void blur_surface(surface& surf, SDL_Rect rect, int depth)
1482 {
1483  if(surf == nullptr) {
1484  return;
1485  }
1486 
1487  const int max_blur = 256;
1488  if(depth > max_blur) {
1489  depth = max_blur;
1490  }
1491 
1492  uint32_t queue[max_blur];
1493  const uint32_t* end_queue = queue + max_blur;
1494 
1495  const uint32_t ff = 0xff;
1496 
1497  const unsigned pixel_offset = rect.y * surf->w + rect.x;
1498 
1499  surface_lock lock(surf);
1500  for(int y = 0; y < rect.h; ++y) {
1501  const uint32_t* front = &queue[0];
1502  uint32_t* back = &queue[0];
1503  uint32_t red = 0, green = 0, blue = 0, avg = 0;
1504  uint32_t* p = lock.pixels() + pixel_offset + y * surf->w;
1505  for(int x = 0; x <= depth && x < rect.w; ++x, ++p) {
1506  red += ((*p) >> 16)&0xFF;
1507  green += ((*p) >> 8)&0xFF;
1508  blue += (*p)&0xFF;
1509  ++avg;
1510  *back++ = *p;
1511  if(back == end_queue) {
1512  back = &queue[0];
1513  }
1514  }
1515 
1516  p = lock.pixels() + pixel_offset + y * surf->w;
1517  for(int x = 0; x < rect.w; ++x, ++p) {
1518  *p = 0xFF000000
1519  | (std::min(red/avg,ff) << 16)
1520  | (std::min(green/avg,ff) << 8)
1521  | std::min(blue/avg,ff);
1522 
1523  if(x >= depth) {
1524  red -= ((*front) >> 16)&0xFF;
1525  green -= ((*front) >> 8)&0xFF;
1526  blue -= *front&0xFF;
1527  --avg;
1528  ++front;
1529  if(front == end_queue) {
1530  front = &queue[0];
1531  }
1532  }
1533 
1534  if(x + depth+1 < rect.w) {
1535  uint32_t* q = p + depth+1;
1536  red += ((*q) >> 16)&0xFF;
1537  green += ((*q) >> 8)&0xFF;
1538  blue += (*q)&0xFF;
1539  ++avg;
1540  *back++ = *q;
1541  if(back == end_queue) {
1542  back = &queue[0];
1543  }
1544  }
1545  }
1546  }
1547 
1548  for(int x = 0; x < rect.w; ++x) {
1549  const uint32_t* front = &queue[0];
1550  uint32_t* back = &queue[0];
1551  uint32_t red = 0, green = 0, blue = 0, avg = 0;
1552  uint32_t* p = lock.pixels() + pixel_offset + x;
1553  for(int y = 0; y <= depth && y < rect.h; ++y, p += surf->w) {
1554  red += ((*p) >> 16)&0xFF;
1555  green += ((*p) >> 8)&0xFF;
1556  blue += *p&0xFF;
1557  ++avg;
1558  *back++ = *p;
1559  if(back == end_queue) {
1560  back = &queue[0];
1561  }
1562  }
1563 
1564  p = lock.pixels() + pixel_offset + x;
1565  for(int y = 0; y < rect.h; ++y, p += surf->w) {
1566  *p = 0xFF000000
1567  | (std::min(red/avg,ff) << 16)
1568  | (std::min(green/avg,ff) << 8)
1569  | std::min(blue/avg,ff);
1570 
1571  if(y >= depth) {
1572  red -= ((*front) >> 16)&0xFF;
1573  green -= ((*front) >> 8)&0xFF;
1574  blue -= *front&0xFF;
1575  --avg;
1576  ++front;
1577  if(front == end_queue) {
1578  front = &queue[0];
1579  }
1580  }
1581 
1582  if(y + depth+1 < rect.h) {
1583  uint32_t* q = p + (depth+1)*surf->w;
1584  red += ((*q) >> 16)&0xFF;
1585  green += ((*q) >> 8)&0xFF;
1586  blue += (*q)&0xFF;
1587  ++avg;
1588  *back++ = *q;
1589  if(back == end_queue) {
1590  back = &queue[0];
1591  }
1592  }
1593  }
1594  }
1595 }
1596 
1597 surface blur_alpha_surface(const surface &surf, int depth)
1598 {
1599  if(surf == nullptr) {
1600  return nullptr;
1601  }
1602 
1603  surface res = make_neutral_surface(surf);
1604 
1605  if(res == nullptr) {
1606  std::cerr << "could not make neutral surface...\n";
1607  return nullptr;
1608  }
1609 
1610  const int max_blur = 256;
1611  if(depth > max_blur) {
1612  depth = max_blur;
1613  }
1614 
1615  struct Pixel{
1616  uint8_t alpha;
1617  uint8_t red;
1618  uint8_t green;
1619  uint8_t blue;
1620  Pixel(uint32_t* p)
1621  : alpha(((*p) >> 24)&0xFF)
1622  , red(((*p) >> 16)&0xFF)
1623  , green(((*p) >> 8)&0xFF)
1624  , blue((*p)&0xFF) {}
1625  };
1626  struct Average{
1627  uint32_t alpha;
1628  uint32_t red;
1629  uint32_t green;
1630  uint32_t blue;
1631  Average() : alpha(), red(), green(), blue()
1632  {}
1633  Average& operator+=(const Pixel& pix){
1634  red += pix.alpha * pix.red;
1635  green += pix.alpha * pix.green;
1636  blue += pix.alpha * pix.blue;
1637  alpha += pix.alpha;
1638  return *this;
1639  }
1640  Average& operator-=(const Pixel& pix){
1641  red -= pix.alpha * pix.red;
1642  green -= pix.alpha * pix.green;
1643  blue -= pix.alpha * pix.blue;
1644  alpha -= pix.alpha;
1645  return *this;
1646  }
1647  uint32_t operator()(unsigned num){
1648  const uint32_t ff = 0xff;
1649  if(!alpha){
1650  return 0;
1651  }
1652  return (std::min(alpha/num,ff) << 24)
1653  | (std::min(red/alpha,ff) << 16)
1654  | (std::min(green/alpha,ff) << 8)
1655  | std::min(blue/alpha,ff);
1656  }
1657  };
1658 
1659  boost::circular_buffer<Pixel> queue(depth*2+1);
1660 
1661  surface_lock lock(res);
1662  int x, y;
1663  // Iterate over rows, blurring each row horizontally
1664  for(y = 0; y < res->h; ++y) {
1665  // Sum of pixel values stored here
1666  Average avg;
1667 
1668  // Preload the first depth+1 pixels
1669  uint32_t* p = lock.pixels() + y*res->w;
1670  for(x = 0; x <= depth && x < res->w; ++x, ++p) {
1671  assert(!queue.full());
1672  queue.push_back(Pixel{p});
1673  avg += queue.back();
1674  }
1675 
1676  // This is the actual inner loop
1677  p = lock.pixels() + y*res->w;
1678  for(x = 0; x < res->w; ++x, ++p) {
1679  // Write the current average
1680  const uint32_t num = queue.size();
1681  *p = avg(num);
1682 
1683  // Unload earlier pixels that are now too far away
1684  if(x >= depth) {
1685  avg -= queue.front();
1686  assert(!queue.empty());
1687  queue.pop_front();
1688  }
1689 
1690  // Add new pixels
1691  if(x + depth+1 < res->w) {
1692  uint32_t* q = p + depth+1;
1693  assert(!queue.full());
1694  queue.push_back(Pixel{q});
1695  avg += queue.back();
1696  }
1697  }
1698  assert(static_cast<int>(queue.size()) == std::min(depth, res->w));
1699  queue.clear();
1700  }
1701 
1702  // Iterate over columns, blurring each column vertically
1703  for(x = 0; x < res->w; ++x) {
1704  // Sum of pixel values stored here
1705  Average avg;
1706 
1707  // Preload the first depth+1 pixels
1708  uint32_t* p = lock.pixels() + x;
1709  for(y = 0; y <= depth && y < res->h; ++y, p += res->w) {
1710  assert(!queue.full());
1711  queue.push_back(Pixel{p});
1712  avg += queue.back();
1713  }
1714 
1715  // This is the actual inner loop
1716  p = lock.pixels() + x;
1717  for(y = 0; y < res->h; ++y, p += res->w) {
1718  // Write the current average
1719  const uint32_t num = queue.size();
1720  *p = avg(num);
1721 
1722  // Unload earlier pixels that are now too far away
1723  if(y >= depth) {
1724  avg -= queue.front();
1725  assert(!queue.empty());
1726  queue.pop_front();
1727  }
1728 
1729  // Add new pixels
1730  if(y + depth+1 < res->h) {
1731  uint32_t* q = p + (depth+1)*res->w;
1732  assert(!queue.full());
1733  queue.push_back(Pixel{q});
1734  avg += queue.back();
1735  }
1736  }
1737  assert(static_cast<int>(queue.size()) == std::min(depth, res->h));
1738  queue.clear();
1739  }
1740 
1741  return res;
1742 }
1743 
1744 surface cut_surface(const surface &surf, const SDL_Rect& r)
1745 {
1746  if(surf == nullptr)
1747  return nullptr;
1748 
1749  surface res = create_compatible_surface(surf, r.w, r.h);
1750 
1751  if(res == nullptr) {
1752  std::cerr << "Could not create a new surface in cut_surface()\n";
1753  return nullptr;
1754  }
1755 
1756  size_t sbpp = surf->format->BytesPerPixel;
1757  size_t spitch = surf->pitch;
1758  size_t rbpp = res->format->BytesPerPixel;
1759  size_t rpitch = res->pitch;
1760 
1761  // compute the areas to copy
1762  SDL_Rect src_rect = r;
1763  SDL_Rect dst_rect { 0, 0, r.w, r.h };
1764 
1765  if (src_rect.x < 0) {
1766  if (src_rect.x + src_rect.w <= 0)
1767  return res;
1768  dst_rect.x -= src_rect.x;
1769  dst_rect.w += src_rect.x;
1770  src_rect.w += src_rect.x;
1771  src_rect.x = 0;
1772  }
1773  if (src_rect.y < 0) {
1774  if (src_rect.y + src_rect.h <= 0)
1775  return res;
1776  dst_rect.y -= src_rect.y;
1777  dst_rect.h += src_rect.y;
1778  src_rect.h += src_rect.y;
1779  src_rect.y = 0;
1780  }
1781 
1782  if(src_rect.x >= surf->w || src_rect.y >= surf->h)
1783  return res;
1784 
1785  const_surface_lock slock(surf);
1786  surface_lock rlock(res);
1787 
1788  const uint8_t* src = reinterpret_cast<const uint8_t *>(slock.pixels());
1789  uint8_t* dest = reinterpret_cast<uint8_t *>(rlock.pixels());
1790 
1791  for(int y = 0; y < src_rect.h && (src_rect.y + y) < surf->h; ++y) {
1792  const uint8_t* line_src = src + (src_rect.y + y) * spitch + src_rect.x * sbpp;
1793  uint8_t* line_dest = dest + (dst_rect.y + y) * rpitch + dst_rect.x * rbpp;
1794  size_t size = src_rect.w + src_rect.x <= surf->w ? src_rect.w : surf->w - src_rect.x;
1795 
1796  assert(rpitch >= src_rect.w * rbpp);
1797  memcpy(line_dest, line_src, size * rbpp);
1798  }
1799 
1800  return res;
1801 }
1803  const surface &surf
1804  , const double amount
1805  , const color_t color)
1806 {
1807  if(surf== nullptr) {
1808  return nullptr;
1809  }
1810 
1811  surface nsurf(make_neutral_surface(surf));
1812 
1813  if(nsurf == nullptr) {
1814  std::cerr << "could not make neutral surface...\n";
1815  return nullptr;
1816  }
1817 
1818  {
1819  surface_lock lock(nsurf);
1820  uint32_t* beg = lock.pixels();
1821  uint32_t* end = beg + nsurf->w*surf->h;
1822 
1823  uint16_t ratio = amount * 256;
1824  const uint16_t red = ratio * color.r;
1825  const uint16_t green = ratio * color.g;
1826  const uint16_t blue = ratio * color.b;
1827  ratio = 256 - ratio;
1828 
1829  while(beg != end) {
1830  uint8_t a = static_cast<uint8_t>(*beg >> 24);
1831  uint8_t r = (ratio * static_cast<uint8_t>(*beg >> 16) + red) >> 8;
1832  uint8_t g = (ratio * static_cast<uint8_t>(*beg >> 8) + green) >> 8;
1833  uint8_t b = (ratio * static_cast<uint8_t>(*beg) + blue) >> 8;
1834 
1835  *beg = (a << 24) | (r << 16) | (g << 8) | b;
1836 
1837  ++beg;
1838  }
1839  }
1840 
1841  return nsurf;
1842 }
1843 
1844 /* Simplified RotSprite algorithm.
1845  * http://en.wikipedia.org/wiki/Image_scaling#RotSprite
1846  * Lifted from: http://github.com/salmonmoose/SpriteRotator
1847  * 1) Zoom the source image by a certain factor.
1848  * 2) Scan the zoomed source image at every step=offset and put it in the result. */
1849 surface rotate_any_surface(const surface& surf, float angle, int zoom, int offset)
1850 {
1851  int src_w, src_h, dst_w, dst_h;
1852  float min_x, min_y, sine, cosine;
1853  {
1854  float max_x, max_y;
1855  // convert angle to radiant (angle * 2 * PI) / 360
1856  const float radians = angle * boost::math::constants::pi<float>() / 180;
1857  cosine = cos(radians);
1858  sine = sin(radians);
1859  // calculate the size of the dst image
1860  src_w = surf->w * zoom;
1861  src_h = surf->h * zoom;
1862  /* See http://en.wikipedia.org/wiki/Rotation_(mathematics) */
1863  const float point_1x = src_h * -sine;
1864  const float point_1y = src_h * cosine;
1865  const float point_2x = src_w * cosine - src_h * sine;
1866  const float point_2y = src_h * cosine + src_w * sine;
1867  const float point_3x = src_w * cosine;
1868  const float point_3y = src_w * sine;
1869  /* After the rotation, the new image has different dimensions.
1870  * E.g.: The maximum height equals the former diagonal in case the angle is 45, 135, 225 or 315 degree.
1871  * See http://en.wikipedia.org/wiki/File:Rotation_illustration2.svg to get the idea. */
1872  min_x = std::min(0.0F, std::min(point_1x, std::min(point_2x, point_3x)));
1873  min_y = std::min(0.0F, std::min(point_1y, std::min(point_2y, point_3y)));
1874  max_x = (angle > 90 && angle < 180) ? 0 : std::max(point_1x, std::max(point_2x, point_3x));
1875  max_y = (angle > 180 && angle < 270) ? 0 : std::max(point_1y, std::max(point_2y, point_3y));
1876  dst_w = static_cast<int>(ceil(std::abs(max_x) - min_x)) / zoom;
1877  dst_h = static_cast<int>(ceil(std::abs(max_y) - min_y)) / zoom;
1878  }
1879  surface dst(create_neutral_surface(dst_w, dst_h));
1880  {
1881  surface_lock dst_lock(dst);
1882  const surface src = scale_surface(surf, src_w, src_h);
1883  const_surface_lock src_lock(src);
1884  const float scale = 1.f / zoom;
1885  const int max_x = dst_w * zoom;
1886  const int max_y = dst_h * zoom;
1887  /* Loop through the zoomed src image,
1888  * take every pixel in steps with offset distance and place it in the dst image. */
1889  for (int x = 0; x < max_x; x += offset)
1890  for (int y = 0; y < max_y; y += offset) {
1891  // calculate the src pixel that fits in the dst
1892  const float source_x = (x + min_x)*cosine + (y + min_y)*sine;
1893  const float source_y = (y + min_y)*cosine - (x + min_x)*sine;
1894  // if the pixel exists on the src surface
1895  if (source_x >= 0 && source_x < src_w
1896  && source_y >= 0 && source_y < src_h)
1897  // get it from the src surface and place it on the dst surface
1898  put_pixel(dst, dst_lock, x*scale , y*scale, // multiply with scale
1899  get_pixel(src, src_lock, source_x, source_y));
1900  }
1901  }
1902 
1903  return dst;
1904 }
1905 
1906 void put_pixel(const surface& surf, surface_lock& surf_lock, int x, int y, uint32_t pixel)
1907 {
1908  const int bpp = surf->format->BytesPerPixel;
1909  /* dst is the address to the pixel we want to set */
1910  uint8_t* const dst = reinterpret_cast<uint8_t*>(surf_lock.pixels()) + y * surf->pitch + x * bpp;
1911  switch (bpp) {
1912  case 1:
1913  *dst = pixel;
1914  break;
1915  case 2:
1916  *reinterpret_cast<uint16_t*>(dst) = pixel;
1917  break;
1918  case 3:
1919  if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
1920  dst[0] = (pixel >> 16) & 0xff;
1921  dst[1] = (pixel >> 8) & 0xff;
1922  dst[2] = pixel & 0xff;
1923  } else {
1924  dst[0] = pixel & 0xff;
1925  dst[1] = (pixel >> 8) & 0xff;
1926  dst[2] = (pixel >> 16) & 0xff;
1927  }
1928  break;
1929  case 4:
1930  *reinterpret_cast<uint32_t*>(dst) = pixel;
1931  break;
1932  default:
1933  break;
1934  }
1935 }
1936 
1937 uint32_t get_pixel(const surface& surf, const const_surface_lock& surf_lock, int x, int y)
1938 {
1939  const int bpp = surf->format->BytesPerPixel;
1940  /* p is the address to the pixel we want to retrieve */
1941  const uint8_t* const src = reinterpret_cast<const uint8_t*>(surf_lock.pixels()) + y * surf->pitch + x * bpp;
1942  switch (bpp) {
1943  case 1:
1944  return *src;
1945  case 2:
1946  return *reinterpret_cast<const uint16_t*>(src);
1947  case 3:
1948  if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
1949  return src[0] << 16 | src[1] << 8 | src[2];
1950  else
1951  return src[0] | src[1] << 8 | src[2] << 16;
1952  break;
1953  case 4:
1954  return *reinterpret_cast<const uint32_t*>(src);
1955  }
1956  return 0;
1957 }
1958 
1959 // Rotates a surface 180 degrees.
1961 {
1962  if ( surf == nullptr )
1963  return nullptr;
1964 
1965  // Work with a "neutral" surface.
1966  surface nsurf(make_neutral_surface(surf));
1967 
1968  if ( nsurf == nullptr ) {
1969  std::cerr << "could not make neutral surface...\n";
1970  return nullptr;
1971  }
1972 
1973  {// Code block to limit the scope of the surface lock.
1974  surface_lock lock(nsurf);
1975  uint32_t* const pixels = lock.pixels();
1976 
1977  // Swap pixels in the upper half of the image with
1978  // those in the lower half.
1979  for (int y=0; y != nsurf->h/2; ++y) {
1980  for(int x=0; x != nsurf->w; ++x) {
1981  const int index1 = y*nsurf->w + x;
1982  const int index2 = (nsurf->h-y)*nsurf->w - x - 1;
1983  std::swap(pixels[index1],pixels[index2]);
1984  }
1985  }
1986 
1987  if ( is_odd(nsurf->h) ) {
1988  // The middle row still needs to be processed.
1989  for (int x=0; x != nsurf->w/2; ++x) {
1990  const int index1 = (nsurf->h/2)*nsurf->w + x;
1991  const int index2 = (nsurf->h/2)*nsurf->w + (nsurf->w - x - 1);
1992  std::swap(pixels[index1],pixels[index2]);
1993  }
1994  }
1995  }
1996 
1997  return nsurf;
1998 }
1999 
2000 
2001 // Rotates a surface 90 degrees, either clockwise or counter-clockwise.
2002 surface rotate_90_surface(const surface &surf, bool clockwise)
2003 {
2004  if ( surf == nullptr )
2005  return nullptr;
2006 
2007  // Work with "neutral" surfaces.
2008  surface dst(create_neutral_surface(surf->h, surf->w)); // Flipped dimensions.
2009  surface src(make_neutral_surface(surf));
2010 
2011  if ( src == nullptr || dst == nullptr ) {
2012  std::cerr << "could not make neutral surface...\n";
2013  return nullptr;
2014  }
2015 
2016  {// Code block to limit the scope of the surface locks.
2017  const_surface_lock src_lock(src);
2018  surface_lock dst_lock(dst);
2019 
2020  const uint32_t* const src_pixels = src_lock.pixels();
2021  uint32_t* const dst_pixels = dst_lock.pixels();
2022 
2023  // Copy the pixels.
2024  for ( int y = 0; y != src->h; ++y ) {
2025  for ( int x = 0; x != src->w; ++x ) {
2026  const int src_index = y*src->w + x;
2027  const int dst_index = clockwise ?
2028  x*dst->w + (dst->w-1-y) :
2029  (dst->h-1-x)*dst->w + y;
2030  dst_pixels[dst_index] = src_pixels[src_index];
2031  }
2032  }
2033  }
2034 
2035  return dst;
2036 }
2037 
2038 
2040 {
2041  if(surf == nullptr) {
2042  return nullptr;
2043  }
2044 
2045  surface nsurf(make_neutral_surface(surf));
2046 
2047  if(nsurf == nullptr) {
2048  std::cerr << "could not make neutral surface...\n";
2049  return nullptr;
2050  }
2051 
2052  {
2053  surface_lock lock(nsurf);
2054  uint32_t* const pixels = lock.pixels();
2055 
2056  for(int y = 0; y != nsurf->h; ++y) {
2057  for(int x = 0; x != nsurf->w/2; ++x) {
2058  const int index1 = y*nsurf->w + x;
2059  const int index2 = (y+1)*nsurf->w - x - 1;
2060  std::swap(pixels[index1],pixels[index2]);
2061  }
2062  }
2063  }
2064 
2065  return nsurf;
2066 }
2067 
2069 {
2070  if(surf == nullptr) {
2071  return nullptr;
2072  }
2073 
2074  surface nsurf(make_neutral_surface(surf));
2075 
2076  if(nsurf == nullptr) {
2077  std::cerr << "could not make neutral surface...\n";
2078  return nullptr;
2079  }
2080 
2081  {
2082  surface_lock lock(nsurf);
2083  uint32_t* const pixels = lock.pixels();
2084 
2085  for(int x = 0; x != nsurf->w; ++x) {
2086  for(int y = 0; y != nsurf->h/2; ++y) {
2087  const int index1 = y*nsurf->w + x;
2088  const int index2 = (nsurf->h-y-1)*surf->w + x;
2089  std::swap(pixels[index1],pixels[index2]);
2090  }
2091  }
2092  }
2093 
2094  return nsurf;
2095 }
2096 
2097 surface create_compatible_surface(const surface &surf, int width, int height)
2098 {
2099  if(surf == nullptr)
2100  return nullptr;
2101 
2102  if(width == -1)
2103  width = surf->w;
2104 
2105  if(height == -1)
2106  height = surf->h;
2107 
2108 #if SDL_VERSION_ATLEAST(2, 0, 6)
2109  surface s = SDL_CreateRGBSurfaceWithFormat(0, width, height, surf->format->BitsPerPixel, surf->format->format);
2110 #else
2111  surface s = SDL_CreateRGBSurface(0, width, height, surf->format->BitsPerPixel,
2112  surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);
2113 #endif
2114 
2115  if (surf->format->palette) {
2116  SDL_SetPaletteColors(s->format->palette, surf->format->palette->colors, 0, surf->format->palette->ncolors);
2117  }
2118  return s;
2119 }
2120 
2121 void blit_surface(const surface& surf,
2122  const SDL_Rect* srcrect, surface& dst, const SDL_Rect* dstrect)
2123 {
2124  assert(surf);
2125  assert(dst);
2126  assert(is_neutral(dst));
2127 
2128  const surface& src = is_neutral(surf) ? surf : make_neutral_surface(surf);
2129 
2130  // Get the areas to blit
2131  SDL_Rect dst_rect {0, 0, dst->w, dst->h};
2132  if(dstrect) {
2133  dst_rect.x = dstrect->x;
2134  dst_rect.w -= dstrect->x;
2135 
2136  dst_rect.y = dstrect->y;
2137  dst_rect.h -= dstrect->y;
2138 
2139  }
2140 
2141  SDL_Rect src_rect {0, 0, src->w, src->h};
2142  if(srcrect && srcrect->w && srcrect->h) {
2143  src_rect.x = srcrect->x;
2144  src_rect.y = srcrect->y;
2145 
2146  src_rect.w = srcrect->w;
2147  src_rect.h = srcrect->h;
2148 
2149  if (src_rect.x < 0) {
2150  if (src_rect.x + src_rect.w <= 0 || src_rect.x + dst_rect.w <= 0 )
2151  return;
2152  dst_rect.x -= src_rect.x;
2153  dst_rect.w += src_rect.x;
2154  src_rect.w += src_rect.x;
2155  src_rect.x = 0;
2156  }
2157  if (src_rect.y < 0) {
2158  if (src_rect.y + src_rect.h <= 0 || src_rect.y + dst_rect.h <= 0 )
2159  return;
2160  dst_rect.y -= src_rect.y;
2161  dst_rect.h += src_rect.y;
2162  src_rect.h += src_rect.y;
2163  src_rect.y = 0;
2164  }
2165  if (src_rect.x + src_rect.w > src->w) {
2166  if (src_rect.x >= src->w)
2167  return;
2168  src_rect.w = src->w - src_rect.x;
2169  }
2170  if (src_rect.y + src_rect.h > src->h) {
2171  if (src_rect.y >= src->h)
2172  return;
2173  src_rect.h = src->h - src_rect.y;
2174  }
2175  }
2176 
2177  assert(dst_rect.x >= 0);
2178  assert(dst_rect.y >= 0);
2179 
2180  // Get the blit size limits.
2181  const unsigned width = std::min(src_rect.w, dst_rect.w);
2182  const unsigned height = std::min(src_rect.h, dst_rect.h);
2183 
2184  {
2185  // Extra scoping used for the surface_lock.
2186  const_surface_lock src_lock(src);
2187  surface_lock dst_lock(dst);
2188 
2189  const uint32_t* const src_pixels = src_lock.pixels();
2190  uint32_t* dst_pixels = dst_lock.pixels();
2191 
2192  for(unsigned y = 0; y < height; ++y) {
2193  for(unsigned x = 0; x < width; ++x) {
2194 
2195  // We need to do the blitting using some optimizations
2196  // if the src is fully transparent we can ignore this pixel
2197  // if the src is fully opaque we can overwrite the destination with this pixel
2198  // if the destination is fully transparent we replace us with the source
2199  //
2200  // We do these optimizations between the extraction of the variables
2201  // to avoid creating variables not used (it might save us some cycles).
2202 
2203  const int src_offset = (y + src_rect.y) * src->w + (x + src_rect.x);
2204  assert(src_offset < src->w * src->h);
2205  const uint32_t src_pixel = src_pixels[src_offset];
2206  const uint8_t src_a = (src_pixel & 0xFF000000) >> 24;
2207 
2208  if(!src_a) {
2209  // Fully transparent source, ignore
2210  continue;
2211  }
2212 
2213  const ptrdiff_t dst_offset = (y + dst_rect.y) * dst->w + (x + dst_rect.x);
2214  assert(dst_offset < dst->w * dst->h);
2215  if(src_a == 255) {
2216  // Fully opaque source, copy
2217  dst_pixels[dst_offset] = src_pixel;
2218  continue;
2219  }
2220 
2221  const uint32_t dst_pixel = dst_pixels[dst_offset];
2222  uint8_t dst_a = (dst_pixel & 0xFF000000) >> 24;
2223 
2224  if(!dst_a) {
2225  // Fully transparent destination, copy
2226  dst_pixels[dst_offset] = src_pixel;
2227  continue;
2228  }
2229 
2230  const uint8_t src_r = (src_pixel & 0x00FF0000) >> 16;
2231  const uint8_t src_g = (src_pixel & 0x0000FF00) >> 8;
2232  const uint8_t src_b = src_pixel & 0x000000FF;
2233 
2234  uint8_t dst_r = (dst_pixel & 0x00FF0000) >> 16;
2235  uint8_t dst_g = (dst_pixel & 0x0000FF00) >> 8;
2236  uint8_t dst_b = dst_pixel & 0x000000FF;
2237 
2238  if(dst_a == 255) {
2239 
2240  // Destination fully opaque blend the source.
2241  dst_r = (((src_r - dst_r) * src_a) >> 8 ) + dst_r;
2242  dst_g = (((src_g - dst_g) * src_a) >> 8 ) + dst_g;
2243  dst_b = (((src_b - dst_b) * src_a) >> 8 ) + dst_b;
2244 
2245  } else {
2246 
2247  // Destination and source party transparent.
2248 
2249  // acquired the data now do the blitting
2250  const unsigned tmp_a = 255 - src_a;
2251 
2252  const unsigned tmp_r = 1 + (src_r * src_a) + (dst_r * tmp_a);
2253  dst_r = (tmp_r + (tmp_r >> 8)) >> 8;
2254 
2255  const unsigned tmp_g = 1 + (src_g * src_a) + (dst_g * tmp_a);
2256  dst_g = (tmp_g + (tmp_g >> 8)) >> 8;
2257 
2258  const unsigned tmp_b = 1 + (src_b * src_a) + (dst_b * tmp_a);
2259  dst_b = (tmp_b + (tmp_b >> 8)) >> 8;
2260 
2261  dst_a += (((255 - dst_a) * src_a) >> 8);
2262  }
2263 
2264  dst_pixels[dst_offset] = (dst_a << 24) | (dst_r << 16) | (dst_g << 8) | (dst_b);
2265 
2266  }
2267  }
2268  }
2269 }
2270 
2271 surface get_surface_portion(const surface &src, SDL_Rect &area)
2272 {
2273  if (src == nullptr) {
2274  return nullptr;
2275  }
2276 
2277  // Check if there is something in the portion
2278  if(area.x >= src->w || area.y >= src->h || area.x + area.w < 0 || area.y + area.h < 0) {
2279  return nullptr;
2280  }
2281 
2282  if(area.x + area.w > src->w) {
2283  area.w = src->w - area.x;
2284  }
2285  if(area.y + area.h > src->h) {
2286  area.h = src->h - area.y;
2287  }
2288 
2289  // use same format as the source (almost always the screen)
2290  surface dst = create_compatible_surface(src, area.w, area.h);
2291 
2292  if(dst == nullptr) {
2293  std::cerr << "Could not create a new surface in get_surface_portion()\n";
2294  return nullptr;
2295  }
2296 
2297  sdl_copy_portion(src, &area, dst, nullptr);
2298 
2299  return dst;
2300 }
2301 
2302 namespace {
2303 
2304 struct not_alpha
2305 {
2306  not_alpha() {}
2307 
2308  // we assume neutral format
2309  bool operator()(uint32_t pixel) const {
2310  uint8_t alpha = pixel >> 24;
2311  return alpha != 0x00;
2312  }
2313 };
2314 
2315 }
2316 
2318 {
2319  SDL_Rect res {0,0,0,0};
2320  surface nsurf(make_neutral_surface(surf));
2321  if(nsurf == nullptr) {
2322  std::cerr << "failed to make neutral surface\n";
2323  return res;
2324  }
2325 
2326  const not_alpha calc;
2327 
2328  surface_lock lock(nsurf);
2329  const uint32_t* const pixels = lock.pixels();
2330 
2331  int n;
2332  for(n = 0; n != nsurf->h; ++n) {
2333  const uint32_t* const start_row = pixels + n*nsurf->w;
2334  const uint32_t* const end_row = start_row + nsurf->w;
2335 
2336  if(std::find_if(start_row,end_row,calc) != end_row)
2337  break;
2338  }
2339 
2340  res.y = n;
2341 
2342  for(n = 0; n != nsurf->h-res.y; ++n) {
2343  const uint32_t* const start_row = pixels + (nsurf->h-n-1)*surf->w;
2344  const uint32_t* const end_row = start_row + nsurf->w;
2345 
2346  if(std::find_if(start_row,end_row,calc) != end_row)
2347  break;
2348  }
2349 
2350  // The height is the height of the surface,
2351  // minus the distance from the top and
2352  // the distance from the bottom.
2353  res.h = nsurf->h - res.y - n;
2354 
2355  for(n = 0; n != nsurf->w; ++n) {
2356  int y;
2357  for(y = 0; y != nsurf->h; ++y) {
2358  const uint32_t pixel = pixels[y*nsurf->w + n];
2359  if(calc(pixel))
2360  break;
2361  }
2362 
2363  if(y != nsurf->h)
2364  break;
2365  }
2366 
2367  res.x = n;
2368 
2369  for(n = 0; n != nsurf->w-res.x; ++n) {
2370  int y;
2371  for(y = 0; y != nsurf->h; ++y) {
2372  const uint32_t pixel = pixels[y*nsurf->w + surf->w - n - 1];
2373  if(calc(pixel))
2374  break;
2375  }
2376 
2377  if(y != nsurf->h)
2378  break;
2379  }
2380 
2381  res.w = nsurf->w - res.x - n;
2382 
2383  return res;
2384 }
2385 
2386 void draw_centered_on_background(surface surf, const SDL_Rect& rect, const color_t& color, surface target)
2387 {
2388  clip_rect_setter clip_setter(target, &rect);
2389 
2390  uint32_t col = SDL_MapRGBA(target->format, color.r, color.g, color.b, color.a);
2391  //TODO: only draw background outside the image
2392  SDL_Rect r = rect;
2393  sdl::fill_surface_rect(target, &r, col);
2394 
2395  if (surf != nullptr) {
2396  r.x = rect.x + (rect.w-surf->w)/2;
2397  r.y = rect.y + (rect.h-surf->h)/2;
2398  sdl_blit(surf, nullptr, target, &r);
2399  }
2400 }
2401 
2402 SDL_Color color_t::to_sdl() const {
2403  return {r, g, b, a};
2404 }
2405 
2406 color_t::color_t(const SDL_Color& c)
2407  : r(c.r)
2408  , g(c.g)
2409  , b(c.b)
2410  , a(c.a)
2411 {}
Definition: utils.hpp:161
surface recolor_image(surface surf, const color_range_map &map_rgb)
Recolors a surface using a map with source and converted palette values.
Definition: utils.cpp:1091
surface negative_image(const surface &surf, const int thresholdR, const int thresholdG, const int thresholdB)
Definition: utils.cpp:853
surface scale_surface_legacy(const surface &surf, int w, int h)
Scale a surface using simple bilinear filtering (discarding rgb from source pixels with 0 alpha) ...
Definition: utils.cpp:404
const uint32_t SDL_BLUE_MASK
Definition: color.hpp:31
surface create_neutral_surface(int w, int h)
Definition: utils.cpp:82
bool is_odd(T num)
Definition: math.hpp:35
std::vector< char_t > string
uint32_t get_pixel(const surface &surf, const const_surface_lock &surf_lock, int x, int y)
Definition: utils.cpp:1937
const uint32_t SDL_ALPHA_MASK
Definition: color.hpp:28
surface adjust_surface_color(const surface &surf, int red, int green, int blue)
Definition: utils.cpp:681
#define fxptoi(x)
IN: fixed_t - OUT: int.
Definition: math.hpp:321
pixel_t * pixels() const
Definition: surface.hpp:148
#define a
surface rotate_180_surface(const surface &surf)
Rotates a surface 180 degrees.
Definition: utils.cpp:1960
void fill_surface_rect(surface &dst, SDL_Rect *dst_rect, const uint32_t color)
Fill a rectangle on a given surface.
Definition: rect.hpp:115
bool in_mask_surface(const surface &surf, const surface &mask)
Check if a surface fit into a mask.
Definition: utils.cpp:1288
#define h
surface scale_surface(const surface &surf, int w, int h)
Scale a surface using alpha-weighted modified bilinear filtering Note: causes artifacts with alpha gr...
Definition: utils.cpp:272
#define d
surface blend_surface(const surface &surf, const double amount, const color_t color)
Blends a surface with a color.
Definition: utils.cpp:1802
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2271
surface flip_surface(const surface &surf)
Definition: utils.cpp:2039
void draw_centered_on_background(surface surf, const SDL_Rect &rect, const color_t &color, surface target)
Definition: utils.cpp:2386
SDL_Rect get_non_transparent_portion(const surface &surf)
Definition: utils.cpp:2317
surface submerge_alpha(const surface &surf, int depth, float alpha_base, float alpha_delta)
Progressively reduce alpha of bottom part of the surface.
Definition: utils.cpp:1334
#define b
void blit_surface(const surface &surf, const SDL_Rect *srcrect, surface &dst, const SDL_Rect *dstrect)
Replacement for sdl_blit.
Definition: utils.cpp:2121
const uint32_t SDL_RED_MASK
Definition: color.hpp:29
surface mask_surface(const surface &surf, const surface &mask, bool *empty_result, const std::string &filename)
Applies a mask on a surface.
Definition: utils.cpp:1219
surface flop_surface(const surface &surf)
Definition: utils.cpp:2068
void adjust_surface_alpha(surface &surf, fixed_t amount)
Definition: utils.cpp:1172
void scale(size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, const ScalerCfg &cfg=ScalerCfg(), int yFirst=0, int yLast=std::numeric_limits< int >::max())
Definition: xbrz.cpp:1190
#define fxpmult(x, y)
IN: unsigned and fixed_t - OUT: unsigned.
Definition: math.hpp:315
surface shadow_image(const surface &surf)
create an heavy shadow of the image, by blurring, increasing alpha and darkening
Definition: utils.cpp:952
void put_pixel(const surface &surf, surface_lock &surf_lock, int x, int y, uint32_t pixel)
Helper methods for setting/getting a single pixel in an image.
Definition: utils.cpp:1906
surface blur_alpha_surface(const surface &surf, int depth)
Cross-fades a surface with alpha channel.
Definition: utils.cpp:1597
Definition: utils.hpp:161
#define fxpdiv(x, y)
IN: unsigned and int - OUT: fixed_t.
Definition: math.hpp:318
surface adjust_surface_alpha_add(const surface &surf, int amount)
Definition: utils.cpp:1181
uint8_t r
Red value.
Definition: color.hpp:177
uint8_t a
Alpha value.
Definition: color.hpp:186
surface blur_surface(const surface &surf, int depth)
Cross-fades a surface.
Definition: utils.cpp:1462
surface cut_surface(const surface &surf, const SDL_Rect &r)
Cuts a rectangle from a surface.
Definition: utils.cpp:1744
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1320
color_t()
Definition: color.hpp:52
int32_t fixed_t
Definition: math.hpp:307
surface rotate_90_surface(const surface &surf, bool clockwise)
Rotates a surface 90 degrees.
Definition: utils.cpp:2002
surface scale_surface_nn(const surface &surf, int w, int h)
Scale a surface using the nearest neighbor algorithm (provided by xBRZ lib)
Definition: utils.cpp:232
static color_t from_argb_bytes(uint32_t c)
Creates a new color_t object from a uint32_t variable.
Definition: color.cpp:87
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: math.hpp:312
Definition: pump.hpp:39
std::unordered_map< color_t, color_t > color_range_map
Definition: color_range.hpp:32
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
Definition: utils.hpp:161
surface alpha_to_greyscale(const surface &surf)
Definition: utils.cpp:896
surface monochrome_image(const surface &surf, const int threshold)
Definition: utils.cpp:771
surface stretch_surface_horizontal(const surface &surf, const unsigned w)
Stretches a surface in the horizontal direction.
Definition: utils.cpp:105
mock_party p
SDL_Color to_sdl() const
Returns the stored color as an color_t object.
Definition: utils.cpp:2402
surface scale_surface_xbrz(const surface &surf, size_t z)
Scale a surface using xBRZ algorithm.
Definition: utils.cpp:193
static map_location::DIRECTION s
double g
Definition: astarsearch.cpp:64
Helper class for pinning SDL surfaces into memory.
Definition: surface.hpp:128
size_t i
Definition: function.cpp:933
int w
Definition: utils.hpp:161
static SDL_PixelFormat & get_neutral_pixel_format()
Definition: utils.cpp:47
surface greyscale_image(const surface &surf)
Definition: utils.cpp:726
version_info sdl_get_version()
Definition: utils.cpp:33
bool is_neutral(const surface &surf)
Check that the surface is neutral bpp 32.
Definition: utils.cpp:40
static map_location::DIRECTION sw
Represents version numbers.
Definition: version.hpp:43
surface light_surface(const surface &surf, const surface &lightmap)
Light surf using lightmap.
Definition: utils.cpp:1396
surface wipe_alpha(const surface &surf)
Definition: utils.cpp:924
Contains the SDL_Rect helper code.
surface brighten_image(const surface &surf, fixed_t amount)
Definition: utils.cpp:1130
surface tile_surface(const surface &surf, int w, int h, bool centered)
Tile a surface.
Definition: utils.cpp:637
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:70
surface scale_surface_sharp(const surface &surf, int w, int h)
Scale a surface using modified nearest neighbour algorithm.
Definition: utils.cpp:544
const uint32_t SDL_GREEN_MASK
Definition: color.hpp:30
surface create_compatible_surface(const surface &surf, int width, int height)
Definition: utils.cpp:2097
surface stretch_surface_vertical(const surface &surf, const unsigned h)
Stretches a surface in the vertical direction.
Definition: utils.cpp:150
#define e
void sdl_copy_portion(const surface &screen, SDL_Rect *screen_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:37
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:33
surface sepia_image(const surface &surf)
Definition: utils.cpp:811
uint8_t g
Green value.
Definition: color.hpp:180
uint8_t b
Blue value.
Definition: color.hpp:183
mock_char c
static map_location::DIRECTION n
void nearestNeighborScale(const uint32_t *src, int srcWidth, int srcHeight, uint32_t *trg, int trgWidth, int trgHeight)
Definition: xbrz.hpp:100
surface rotate_any_surface(const surface &surf, float angle, int zoom, int offset)
Rotates a surface by any degrees.
Definition: utils.cpp:1849
surface swap_channels_image(const surface &surf, channel r, channel g, channel b, channel a)
Definition: utils.cpp:989
channel
Definition: utils.hpp:161