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