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