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