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