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