The Battle for Wesnoth  1.19.10+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 void mask_surface(surface& nsurf, const surface& nmask, bool* empty_result, const std::string& filename)
707 {
708  if(nsurf == nullptr) {
709  *empty_result = true;
710  return;
711  }
712  if(nmask == nullptr) {
713  return;
714  }
715 
716  if (nsurf->w != nmask->w) {
717  // we don't support efficiently different width.
718  // (different height is not a real problem)
719  // This function is used on all hexes and usually only for that
720  // so better keep it simple and efficient for the normal case
721  std::stringstream ss;
722  ss << "Detected an image with bad dimensions: ";
723  if(!filename.empty()) ss << filename << ": ";
724  ss << nsurf->w << "x" << nsurf->h;
725  PLAIN_LOG << ss.str();
726  PLAIN_LOG << "It will not be masked, please use: "<< nmask->w << "x" << nmask->h;
727  return;
728  }
729 
730  bool empty = true;
731  {
732  surface_lock lock(nsurf);
733  const_surface_lock mlock(nmask);
734 
735  uint32_t* beg = lock.pixels();
736  uint32_t* end = beg + nsurf.area();
737  const uint32_t* mbeg = mlock.pixels();
738  const uint32_t* mend = mbeg + nmask->w*nmask->h;
739 
740  while(beg != end && mbeg != mend) {
741  auto [r, g, b, alpha] = color_t::from_argb_bytes(*beg);
742 
743  uint8_t malpha = (*mbeg) >> 24;
744  if (alpha > malpha) {
745  alpha = malpha;
746  }
747  if(alpha)
748  empty = false;
749 
750  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
751 
752  ++beg;
753  ++mbeg;
754  }
755  }
756  if(empty_result)
757  *empty_result = empty;
758 }
759 
760 bool in_mask_surface(const surface& nsurf, const surface& nmask)
761 {
762  if(nsurf == nullptr) {
763  return false;
764  }
765  if(nmask == nullptr){
766  return true;
767  }
768 
769  if (nsurf->w != nmask->w || nsurf->h != nmask->h ) {
770  // not same size, consider it doesn't fit
771  return false;
772  }
773 
774  {
775  const_surface_lock lock(nsurf);
776  const_surface_lock mlock(nmask);
777 
778  const uint32_t* mbeg = mlock.pixels();
779  const uint32_t* mend = mbeg + nmask->w*nmask->h;
780  const uint32_t* beg = lock.pixels();
781  // no need for 'end', because both surfaces have same size
782 
783  while(mbeg != mend) {
784  uint8_t malpha = (*mbeg) >> 24;
785  if(malpha == 0) {
786  uint8_t alpha = (*beg) >> 24;
787  if (alpha)
788  return false;
789  }
790  ++mbeg;
791  ++beg;
792  }
793  }
794 
795  return true;
796 }
797 
798 void light_surface(surface& nsurf, const surface &lightmap)
799 {
800  if(nsurf == nullptr) {
801  return;
802  }
803  if(lightmap == nullptr) {
804  return;
805  }
806 
807  if (nsurf->w != lightmap->w) {
808  // we don't support efficiently different width.
809  // (different height is not a real problem)
810  // This function is used on all hexes and usually only for that
811  // so better keep it simple and efficient for the normal case
812  PLAIN_LOG << "Detected an image with bad dimensions: " << nsurf->w << "x" << nsurf->h;
813  PLAIN_LOG << "It will not be lighted, please use: "<< lightmap->w << "x" << lightmap->h;
814  return;
815  }
816  {
817  surface_lock lock(nsurf);
818  const_surface_lock llock(lightmap);
819 
820  uint32_t* beg = lock.pixels();
821  uint32_t* end = beg + nsurf.area();
822  const uint32_t* lbeg = llock.pixels();
823  const uint32_t* lend = lbeg + lightmap.area();
824 
825  while(beg != end && lbeg != lend) {
826  auto [lr, lg, lb, la] = color_t::from_argb_bytes(*lbeg);
827  auto [r, g, b, alpha] = color_t::from_argb_bytes(*beg);
828 
829  int dr = (static_cast<int>(lr) - 128) * 2;
830  int dg = (static_cast<int>(lg) - 128) * 2;
831  int db = (static_cast<int>(lb) - 128) * 2;
832 
833  //note that r + dr will promote r to int (needed to avoid uint8_t math)
834  r = std::clamp(r + dr, 0, 255);
835  g = std::clamp(g + dg, 0, 255);
836  b = std::clamp(b + db, 0, 255);
837 
838  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
839 
840  ++beg;
841  ++lbeg;
842  }
843  }
844 }
845 
846 void blur_surface(surface& surf, SDL_Rect rect, int depth)
847 {
848  if(surf == nullptr) {
849  return;
850  }
851 
852  const int max_blur = 256;
853  if(depth > max_blur) {
854  depth = max_blur;
855  }
856 
857  uint32_t queue[max_blur];
858  const uint32_t* end_queue = queue + max_blur;
859 
860  const uint32_t ff = 0xff;
861 
862  const unsigned pixel_offset = rect.y * surf->w + rect.x;
863 
864  surface_lock lock(surf);
865  for(int y = 0; y < rect.h; ++y) {
866  const uint32_t* front = &queue[0];
867  uint32_t* back = &queue[0];
868  uint32_t red = 0, green = 0, blue = 0, avg = 0;
869  uint32_t* p = lock.pixels() + pixel_offset + y * surf->w;
870  for(int x = 0; x <= depth && x < rect.w; ++x, ++p) {
871  red += ((*p) >> 16)&0xFF;
872  green += ((*p) >> 8)&0xFF;
873  blue += (*p)&0xFF;
874  ++avg;
875  *back++ = *p;
876  if(back == end_queue) {
877  back = &queue[0];
878  }
879  }
880 
881  p = lock.pixels() + pixel_offset + y * surf->w;
882  for(int x = 0; x < rect.w; ++x, ++p) {
883  *p = 0xFF000000
884  | (std::min(red/avg,ff) << 16)
885  | (std::min(green/avg,ff) << 8)
886  | std::min(blue/avg,ff);
887 
888  if(x >= depth) {
889  red -= ((*front) >> 16)&0xFF;
890  green -= ((*front) >> 8)&0xFF;
891  blue -= *front&0xFF;
892  --avg;
893  ++front;
894  if(front == end_queue) {
895  front = &queue[0];
896  }
897  }
898 
899  if(x + depth+1 < rect.w) {
900  uint32_t* q = p + depth+1;
901  red += ((*q) >> 16)&0xFF;
902  green += ((*q) >> 8)&0xFF;
903  blue += (*q)&0xFF;
904  ++avg;
905  *back++ = *q;
906  if(back == end_queue) {
907  back = &queue[0];
908  }
909  }
910  }
911  }
912 
913  for(int x = 0; x < rect.w; ++x) {
914  const uint32_t* front = &queue[0];
915  uint32_t* back = &queue[0];
916  uint32_t red = 0, green = 0, blue = 0, avg = 0;
917  uint32_t* p = lock.pixels() + pixel_offset + x;
918  for(int y = 0; y <= depth && y < rect.h; ++y, p += surf->w) {
919  red += ((*p) >> 16)&0xFF;
920  green += ((*p) >> 8)&0xFF;
921  blue += *p&0xFF;
922  ++avg;
923  *back++ = *p;
924  if(back == end_queue) {
925  back = &queue[0];
926  }
927  }
928 
929  p = lock.pixels() + pixel_offset + x;
930  for(int y = 0; y < rect.h; ++y, p += surf->w) {
931  *p = 0xFF000000
932  | (std::min(red/avg,ff) << 16)
933  | (std::min(green/avg,ff) << 8)
934  | std::min(blue/avg,ff);
935 
936  if(y >= depth) {
937  red -= ((*front) >> 16)&0xFF;
938  green -= ((*front) >> 8)&0xFF;
939  blue -= *front&0xFF;
940  --avg;
941  ++front;
942  if(front == end_queue) {
943  front = &queue[0];
944  }
945  }
946 
947  if(y + depth+1 < rect.h) {
948  uint32_t* q = p + (depth+1)*surf->w;
949  red += ((*q) >> 16)&0xFF;
950  green += ((*q) >> 8)&0xFF;
951  blue += (*q)&0xFF;
952  ++avg;
953  *back++ = *q;
954  if(back == end_queue) {
955  back = &queue[0];
956  }
957  }
958  }
959  }
960 }
961 
962 void blur_alpha_surface(surface& res, int depth)
963 {
964  if(res == nullptr) {
965  return;
966  }
967 
968  const int max_blur = 256;
969  if(depth > max_blur) {
970  depth = max_blur;
971  }
972 
973  struct Pixel{
974  uint8_t alpha;
975  uint8_t red;
976  uint8_t green;
977  uint8_t blue;
978  Pixel(uint32_t* p)
979  : alpha(((*p) >> 24)&0xFF)
980  , red(((*p) >> 16)&0xFF)
981  , green(((*p) >> 8)&0xFF)
982  , blue((*p)&0xFF) {}
983  };
984  struct Average{
985  uint32_t alpha;
986  uint32_t red;
987  uint32_t green;
988  uint32_t blue;
989  Average() : alpha(), red(), green(), blue()
990  {}
991  Average& operator+=(const Pixel& pix){
992  red += pix.alpha * pix.red;
993  green += pix.alpha * pix.green;
994  blue += pix.alpha * pix.blue;
995  alpha += pix.alpha;
996  return *this;
997  }
998  Average& operator-=(const Pixel& pix){
999  red -= pix.alpha * pix.red;
1000  green -= pix.alpha * pix.green;
1001  blue -= pix.alpha * pix.blue;
1002  alpha -= pix.alpha;
1003  return *this;
1004  }
1005  uint32_t operator()(unsigned num){
1006  const uint32_t ff = 0xff;
1007  if(!alpha){
1008  return 0;
1009  }
1010  return (std::min(alpha/num,ff) << 24)
1011  | (std::min(red/alpha,ff) << 16)
1012  | (std::min(green/alpha,ff) << 8)
1013  | std::min(blue/alpha,ff);
1014  }
1015  };
1016 
1017  boost::circular_buffer<Pixel> queue(depth*2+1);
1018 
1019  surface_lock lock(res);
1020  int x, y;
1021  // Iterate over rows, blurring each row horizontally
1022  for(y = 0; y < res->h; ++y) {
1023  // Sum of pixel values stored here
1024  Average avg;
1025 
1026  // Preload the first depth+1 pixels
1027  uint32_t* p = lock.pixels() + y*res->w;
1028  for(x = 0; x <= depth && x < res->w; ++x, ++p) {
1029  assert(!queue.full());
1030  queue.push_back(Pixel{p});
1031  avg += queue.back();
1032  }
1033 
1034  // This is the actual inner loop
1035  p = lock.pixels() + y*res->w;
1036  for(x = 0; x < res->w; ++x, ++p) {
1037  // Write the current average
1038  const uint32_t num = queue.size();
1039  *p = avg(num);
1040 
1041  // Unload earlier pixels that are now too far away
1042  if(x >= depth) {
1043  avg -= queue.front();
1044  assert(!queue.empty());
1045  queue.pop_front();
1046  }
1047 
1048  // Add new pixels
1049  if(x + depth+1 < res->w) {
1050  uint32_t* q = p + depth+1;
1051  assert(!queue.full());
1052  queue.push_back(Pixel{q});
1053  avg += queue.back();
1054  }
1055  }
1056  assert(static_cast<int>(queue.size()) == std::min(depth, res->w));
1057  queue.clear();
1058  }
1059 
1060  // Iterate over columns, blurring each column vertically
1061  for(x = 0; x < res->w; ++x) {
1062  // Sum of pixel values stored here
1063  Average avg;
1064 
1065  // Preload the first depth+1 pixels
1066  uint32_t* p = lock.pixels() + x;
1067  for(y = 0; y <= depth && y < res->h; ++y, p += res->w) {
1068  assert(!queue.full());
1069  queue.push_back(Pixel{p});
1070  avg += queue.back();
1071  }
1072 
1073  // This is the actual inner loop
1074  p = lock.pixels() + x;
1075  for(y = 0; y < res->h; ++y, p += res->w) {
1076  // Write the current average
1077  const uint32_t num = queue.size();
1078  *p = avg(num);
1079 
1080  // Unload earlier pixels that are now too far away
1081  if(y >= depth) {
1082  avg -= queue.front();
1083  assert(!queue.empty());
1084  queue.pop_front();
1085  }
1086 
1087  // Add new pixels
1088  if(y + depth+1 < res->h) {
1089  uint32_t* q = p + (depth+1)*res->w;
1090  assert(!queue.full());
1091  queue.push_back(Pixel{q});
1092  avg += queue.back();
1093  }
1094  }
1095  assert(static_cast<int>(queue.size()) == std::min(depth, res->h));
1096  queue.clear();
1097  }
1098 }
1099 
1100 surface cut_surface(const surface &surf, const SDL_Rect& r)
1101 {
1102  if(surf == nullptr)
1103  return nullptr;
1104 
1105  surface res(r.w, r.h);
1106 
1107  if(res == nullptr) {
1108  PLAIN_LOG << "Could not create a new surface in cut_surface()";
1109  return nullptr;
1110  }
1111 
1112  std::size_t sbpp = surf->format->BytesPerPixel;
1113  std::size_t spitch = surf->pitch;
1114  std::size_t rbpp = res->format->BytesPerPixel;
1115  std::size_t rpitch = res->pitch;
1116 
1117  // compute the areas to copy
1118  SDL_Rect src_rect = r;
1119  SDL_Rect dst_rect { 0, 0, r.w, r.h };
1120 
1121  if (src_rect.x < 0) {
1122  if (src_rect.x + src_rect.w <= 0)
1123  return res;
1124  dst_rect.x -= src_rect.x;
1125  dst_rect.w += src_rect.x;
1126  src_rect.w += src_rect.x;
1127  src_rect.x = 0;
1128  }
1129  if (src_rect.y < 0) {
1130  if (src_rect.y + src_rect.h <= 0)
1131  return res;
1132  dst_rect.y -= src_rect.y;
1133  dst_rect.h += src_rect.y;
1134  src_rect.h += src_rect.y;
1135  src_rect.y = 0;
1136  }
1137 
1138  if(src_rect.x >= surf->w || src_rect.y >= surf->h)
1139  return res;
1140 
1141  const_surface_lock slock(surf);
1142  surface_lock rlock(res);
1143 
1144  const uint8_t* src = reinterpret_cast<const uint8_t *>(slock.pixels());
1145  uint8_t* dest = reinterpret_cast<uint8_t *>(rlock.pixels());
1146 
1147  for(int y = 0; y < src_rect.h && (src_rect.y + y) < surf->h; ++y) {
1148  const uint8_t* line_src = src + (src_rect.y + y) * spitch + src_rect.x * sbpp;
1149  uint8_t* line_dest = dest + (dst_rect.y + y) * rpitch + dst_rect.x * rbpp;
1150  std::size_t size = src_rect.w + src_rect.x <= surf->w ? src_rect.w : surf->w - src_rect.x;
1151 
1152  assert(rpitch >= src_rect.w * rbpp);
1153  memcpy(line_dest, line_src, size * rbpp);
1154  }
1155 
1156  return res;
1157 }
1158 
1159 void blend_surface(surface& nsurf, const double amount, const color_t color)
1160 {
1161  if(nsurf) {
1162  surface_lock lock(nsurf);
1163 
1164  uint16_t ratio = amount * 256;
1165  const uint16_t red = ratio * color.r;
1166  const uint16_t green = ratio * color.g;
1167  const uint16_t blue = ratio * color.b;
1168  ratio = 256 - ratio;
1169 
1170  for(auto& pixel : lock.pixel_span()) {
1171  auto [r, g, b, a] = color_t::from_argb_bytes(pixel);
1172 
1173  r = (ratio * r + red) >> 8;
1174  g = (ratio * g + green) >> 8;
1175  b = (ratio * b + blue) >> 8;
1176 
1177  pixel = (a << 24) | (r << 16) | (g << 8) | b;
1178  }
1179  }
1180 }
1181 
1182 /* Simplified RotSprite algorithm.
1183  * http://en.wikipedia.org/wiki/Image_scaling#RotSprite
1184  * Lifted from: http://github.com/salmonmoose/SpriteRotator
1185  * 1) Zoom the source image by a certain factor.
1186  * 2) Scan the zoomed source image at every step=offset and put it in the result. */
1187 surface rotate_any_surface(const surface& surf, float angle, int zoom, int offset)
1188 {
1189  int src_w, src_h, dst_w, dst_h;
1190  float min_x, min_y, sine, cosine;
1191  {
1192  float max_x, max_y;
1193  // convert angle to radiant (angle * 2 * PI) / 360
1194  const float radians = angle * boost::math::constants::pi<float>() / 180;
1195  cosine = std::cos(radians);
1196  sine = std::sin(radians);
1197  // calculate the size of the dst image
1198  src_w = surf->w * zoom;
1199  src_h = surf->h * zoom;
1200  /* See http://en.wikipedia.org/wiki/Rotation_(mathematics) */
1201  const float point_1x = src_h * -sine;
1202  const float point_1y = src_h * cosine;
1203  const float point_2x = src_w * cosine - src_h * sine;
1204  const float point_2y = src_h * cosine + src_w * sine;
1205  const float point_3x = src_w * cosine;
1206  const float point_3y = src_w * sine;
1207  /* After the rotation, the new image has different dimensions.
1208  * E.g.: The maximum height equals the former diagonal in case the angle is 45, 135, 225 or 315 degree.
1209  * See http://en.wikipedia.org/wiki/File:Rotation_illustration2.svg to get the idea. */
1210  min_x = std::min(0.0F, std::min(point_1x, std::min(point_2x, point_3x)));
1211  min_y = std::min(0.0F, std::min(point_1y, std::min(point_2y, point_3y)));
1212  max_x = (angle > 90 && angle < 180) ? 0 : std::max(point_1x, std::max(point_2x, point_3x));
1213  max_y = (angle > 180 && angle < 270) ? 0 : std::max(point_1y, std::max(point_2y, point_3y));
1214  dst_w = static_cast<int>(ceil(std::abs(max_x) - min_x)) / zoom;
1215  dst_h = static_cast<int>(ceil(std::abs(max_y) - min_y)) / zoom;
1216  }
1217  surface dst(dst_w, dst_h);
1218  {
1219  surface_lock dst_lock(dst);
1220  uint32_t* const dst_pixels = dst_lock.pixels();
1221 
1222  const surface src = scale_surface(surf, src_w, src_h);
1223  const_surface_lock src_lock(src);
1224  const uint32_t* const src_pixels = src_lock.pixels();
1225 
1226  const float scale = 1.f / zoom;
1227  const int max_x = dst_w * zoom;
1228  const int max_y = dst_h * zoom;
1229  /* Loop through the zoomed src image,
1230  * take every pixel in steps with offset distance and place it in the dst image. */
1231  for (int x = 0; x < max_x; x += offset)
1232  for (int y = 0; y < max_y; y += offset) {
1233  // calculate the src pixel that fits in the dst
1234  const float source_x = (x + min_x)*cosine + (y + min_y)*sine;
1235  const float source_y = (y + min_y)*cosine - (x + min_x)*sine;
1236  // if the pixel exists on the src surface
1237  if (source_x >= 0 && source_x < src_w && source_y >= 0 && source_y < src_h) {
1238  // get it from the src surface and place it on the dst surface
1239  dst_pixels[int((y * scale)) * dst->w + int((x * scale))] =
1240  src_pixels[int(source_y) * src->w + int(source_x)];
1241  }
1242  }
1243  }
1244 
1245  return dst;
1246 }
1247 
1248 // Rotates a surface 180 degrees.
1250 {
1251  if ( surf == nullptr )
1252  return nullptr;
1253 
1254  surface nsurf = surf.clone();
1255 
1256  if ( nsurf == nullptr ) {
1257  PLAIN_LOG << "could not make neutral surface...";
1258  return nullptr;
1259  }
1260 
1261  {// Code block to limit the scope of the surface lock.
1262  surface_lock lock(nsurf);
1263  uint32_t* const pixels = lock.pixels();
1264 
1265  // Swap pixels in the upper half of the image with
1266  // those in the lower half.
1267  for (int y=0; y != nsurf->h/2; ++y) {
1268  for(int x=0; x != nsurf->w; ++x) {
1269  const int index1 = y*nsurf->w + x;
1270  const int index2 = (nsurf->h-y)*nsurf->w - x - 1;
1271  std::swap(pixels[index1],pixels[index2]);
1272  }
1273  }
1274 
1275  if ( is_odd(nsurf->h) ) {
1276  // The middle row still needs to be processed.
1277  for (int x=0; x != nsurf->w/2; ++x) {
1278  const int index1 = (nsurf->h/2)*nsurf->w + x;
1279  const int index2 = (nsurf->h/2)*nsurf->w + (nsurf->w - x - 1);
1280  std::swap(pixels[index1],pixels[index2]);
1281  }
1282  }
1283  }
1284 
1285  return nsurf;
1286 }
1287 
1288 // Rotates a surface 90 degrees, either clockwise or counter-clockwise.
1289 surface rotate_90_surface(const surface& surf, bool clockwise)
1290 {
1291  if(surf == nullptr)
1292  return surf;
1293 
1294  surface dst(surf->h, surf->w); // Flipped dimensions.
1295 
1296  if ( surf == nullptr || dst == nullptr ) {
1297  PLAIN_LOG << "could not make neutral surface...";
1298  return nullptr;
1299  }
1300 
1301  {
1302  const_surface_lock src_lock(surf);
1303  surface_lock dst_lock(dst);
1304 
1305  const uint32_t* const src_pixels = src_lock.pixels();
1306  uint32_t* const dst_pixels = dst_lock.pixels();
1307 
1308  // Copy the pixels.
1309  for(int y = 0; y != surf->h; ++y) {
1310  for ( int x = 0; x != surf->w; ++x ) {
1311  const int src_index = y*surf->w + x;
1312  const int dst_index = clockwise ?
1313  x*dst->w + (dst->w-1-y) :
1314  (dst->h-1-x)*dst->w + y;
1315  dst_pixels[dst_index] = src_pixels[src_index];
1316  }
1317  }
1318  }
1319 
1320  return dst;
1321 }
1322 
1323 void flip_surface(surface& nsurf)
1324 {
1325  if(nsurf) {
1326  surface_lock lock(nsurf);
1327  uint32_t* const pixels = lock.pixels();
1328 
1329  for(int y = 0; y != nsurf->h; ++y) {
1330  for(int x = 0; x != nsurf->w/2; ++x) {
1331  const int index1 = y*nsurf->w + x;
1332  const int index2 = (y+1)*nsurf->w - x - 1;
1333  std::swap(pixels[index1],pixels[index2]);
1334  }
1335  }
1336  }
1337 }
1338 
1339 void flop_surface(surface& nsurf)
1340 {
1341  if(nsurf) {
1342  surface_lock lock(nsurf);
1343  uint32_t* const pixels = lock.pixels();
1344 
1345  for(int x = 0; x != nsurf->w; ++x) {
1346  for(int y = 0; y != nsurf->h/2; ++y) {
1347  const int index1 = y*nsurf->w + x;
1348  const int index2 = (nsurf->h-y-1)*nsurf->w + x;
1349  std::swap(pixels[index1],pixels[index2]);
1350  }
1351  }
1352  }
1353 }
1354 
1355 surface get_surface_portion(const surface &src, SDL_Rect &area)
1356 {
1357  if (src == nullptr) {
1358  return nullptr;
1359  }
1360 
1361  // Check if there is something in the portion
1362  if(area.x >= src->w || area.y >= src->h || area.x + area.w < 0 || area.y + area.h < 0) {
1363  return nullptr;
1364  }
1365 
1366  if(area.x + area.w > src->w) {
1367  area.w = src->w - area.x;
1368  }
1369  if(area.y + area.h > src->h) {
1370  area.h = src->h - area.y;
1371  }
1372 
1373  // use same format as the source (almost always the screen)
1374  surface dst(area.w, area.h);
1375 
1376  if(dst == nullptr) {
1377  PLAIN_LOG << "Could not create a new surface in get_surface_portion()";
1378  return nullptr;
1379  }
1380 
1381  // Blit to dst with BLENDMODE_NONE, then reset src blend mode.
1382  SDL_BlendMode src_blend;
1383  SDL_GetSurfaceBlendMode(src, &src_blend);
1384  SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE);
1385  SDL_BlitSurface(src, &area, dst, nullptr);
1386  SDL_SetSurfaceBlendMode(src, src_blend);
1387 
1388  return dst;
1389 }
1390 
1391 namespace
1392 {
1393 constexpr bool not_alpha(uint32_t pixel)
1394 {
1395  return (pixel >> 24) != 0x00;
1396 }
1397 }
1398 
1400 {
1401  rect res {0,0,0,0};
1402 
1403  const_surface_lock lock(nsurf);
1404  const uint32_t* const pixels = lock.pixels();
1405 
1406  int n;
1407  for(n = 0; n != nsurf->h; ++n) {
1408  const uint32_t* const start_row = pixels + n*nsurf->w;
1409  const uint32_t* const end_row = start_row + nsurf->w;
1410 
1411  if(std::find_if(start_row,end_row,not_alpha) != end_row)
1412  break;
1413  }
1414 
1415  res.y = n;
1416 
1417  for(n = 0; n != nsurf->h-res.y; ++n) {
1418  const uint32_t* const start_row = pixels + (nsurf->h-n-1)*nsurf->w;
1419  const uint32_t* const end_row = start_row + nsurf->w;
1420 
1421  if(std::find_if(start_row,end_row,not_alpha) != end_row)
1422  break;
1423  }
1424 
1425  // The height is the height of the surface,
1426  // minus the distance from the top and
1427  // the distance from the bottom.
1428  res.h = nsurf->h - res.y - n;
1429 
1430  for(n = 0; n != nsurf->w; ++n) {
1431  int y;
1432  for(y = 0; y != nsurf->h; ++y) {
1433  const uint32_t pixel = pixels[y*nsurf->w + n];
1434  if(not_alpha(pixel))
1435  break;
1436  }
1437 
1438  if(y != nsurf->h)
1439  break;
1440  }
1441 
1442  res.x = n;
1443 
1444  for(n = 0; n != nsurf->w-res.x; ++n) {
1445  int y;
1446  for(y = 0; y != nsurf->h; ++y) {
1447  const uint32_t pixel = pixels[y*nsurf->w + nsurf->w - n - 1];
1448  if(not_alpha(pixel))
1449  break;
1450  }
1451 
1452  if(y != nsurf->h)
1453  break;
1454  }
1455 
1456  res.w = nsurf->w - res.x - n;
1457  return res;
1458 }
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)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1339
int w
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: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: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: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
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
void mask_surface(surface &nsurf, const surface &nmask, bool *empty_result, const std::string &filename)
Applies a mask on a surface.
Definition: utils.cpp:706
rect get_non_transparent_portion(const surface &nsurf)
Definition: utils.cpp:1399
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 wipe_alpha(surface &nsurf)
Definition: utils.cpp:515
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:1159
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
surface cut_surface(const surface &surf, const SDL_Rect &r)
Cuts a rectangle from a surface.
Definition: utils.cpp:1100
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:798
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:1355
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:760
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:962
void greyscale_image(surface &nsurf)
Definition: utils.cpp:422
void flop_surface(surface &nsurf)
Definition: utils.cpp:1339
surface rotate_90_surface(const surface &surf, bool clockwise)
Rotates a surface 90 degrees.
Definition: utils.cpp:1289
surface rotate_180_surface(const surface &surf)
Rotates a surface 180 degrees.
Definition: utils.cpp:1249
void flip_surface(surface &nsurf)
Definition: utils.cpp:1323
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:1187
void monochrome_image(surface &nsurf, const int threshold)
Definition: utils.cpp:444
void blur_surface(surface &surf, SDL_Rect rect, int depth)
Cross-fades a surface in place.
Definition: utils.cpp:846
channel
Definition: utils.hpp:104
@ BLUE
Definition: utils.hpp:104
@ ALPHA
Definition: utils.hpp:104
@ GREEN
Definition: utils.hpp:104
@ RED
Definition: utils.hpp:104
#define e
#define h
#define b