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