The Battle for Wesnoth  1.15.1+dev
image_modifications.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Iris Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "image_modifications.hpp"
16 
17 #include "color.hpp"
18 #include "config.hpp"
19 #include "game_config.hpp"
20 #include "picture.hpp"
21 #include "lexical_cast.hpp"
22 #include "log.hpp"
24 
25 #include "formula/formula.hpp"
26 #include "formula/callable.hpp"
27 
28 #define GETTEXT_DOMAIN "wesnoth-lib"
29 
30 static lg::log_domain log_display("display");
31 #define ERR_DP LOG_STREAM(err, log_display)
32 
33 namespace image {
34 
35 /** Adds @a mod to the queue (unless mod is nullptr). */
37 {
38  // Null pointers do not get stored. (Shouldn't happen, but just in case.)
39  if(mod != nullptr) {
40  priorities_[mod->priority()].emplace_back(mod);
41  }
42 }
43 
44 /** Removes the top element from the queue */
46 {
47  map_type::iterator top_pair = priorities_.begin();
48  auto& top_vector = top_pair->second;
49 
50  // Erase the top element.
51  top_vector.erase(top_vector.begin());
52  if(top_vector.empty()) {
53  // We need to keep the map clean.
54  priorities_.erase(top_pair);
55  }
56 }
57 
58 /** Returns the number of elements in the queue. */
59 std::size_t modification_queue::size() const
60 {
61  std::size_t count = 0;
62  for(const map_type::value_type& pair : priorities_) {
63  count += pair.second.size();
64  }
65 
66  return count;
67 }
68 
69 /** Returns the top element in the queue . */
71 {
72  return priorities_.begin()->second.front().get();
73 }
74 
75 
76 namespace {
77 
78 /// A function used to parse modification arguments
79 using mod_parser = std::function<modification*(const std::string&)>;
80 
81 /** A map of all registered mod parsers
82  *
83  * The mapping is between the modification name and the parser function pointer
84  * An example of an entry would be "TC" -> &parse_TC_mod
85  */
86 std::map<std::string, mod_parser> mod_parsers;
87 
88 /** Decodes a single modification using an appropriate mod_parser
89  *
90  * @param encoded_mod A string representing a single modification
91  *
92  * @return A pointer to the decoded modification object
93  * @retval nullptr if the string is invalid or a parser isn't found
94  */
95 modification* decode_modification(const std::string& encoded_mod)
96 {
97  std::vector<std::string> split = utils::parenthetical_split(encoded_mod);
98 
99  if(split.size() != 2) {
100  ERR_DP << "error parsing image modifications: " << encoded_mod << "\n";
101  return nullptr;
102  }
103 
104  std::string mod_type = split[0];
105  std::string args = split[1];
106 
107  if(mod_parsers.find(mod_type) == mod_parsers.end()) {
108  ERR_DP << "unknown image function in path: " << mod_type << '\n';
109  return nullptr;
110  }
111 
112  return mod_parsers[mod_type](args);
113 }
114 
115 } // end anon namespace
116 
117 
118 modification::imod_exception::imod_exception(const std::stringstream& message_stream)
119  : message(message_stream.str())
120 {
121 }
122 
124  : message(message)
125 {
126 }
127 
128 /** Decodes the modification string
129  *
130  * Important:
131  * It creates new objects which need to be deleted after use
132  *
133  * @param encoded_mods A string representing any number of modifications
134  *
135  * @return A modification_queue filled with decoded modification pointers
136  */
137 modification_queue modification::decode(const std::string& encoded_mods)
138 {
139  modification_queue mods;
140 
141  for(const std::string& encoded_mod : utils::parenthetical_split(encoded_mods, '~')) {
142  modification* mod = decode_modification(encoded_mod);
143 
144  if(mod) {
145  mods.push(mod);
146  }
147  }
148 
149  return mods;
150 }
151 
153 {
154  // unchecked
155  return recolor_image(src, rc_map_);
156 }
157 
159 {
160  surface ret = src;
161 
162  if(horiz_ && vert_ ) {
163  // Slightly faster than doing both a flip and a flop.
164  ret = rotate_180_surface(ret);
165  } else if(horiz_) {
166  ret = flip_surface(ret);
167  } else if(vert_) {
168  ret = flop_surface(ret);
169  }
170 
171  return ret;
172 }
173 
175 {
176  // Convert the number of degrees to the interval [0,360].
177  const int normalized = degrees_ >= 0 ?
178  degrees_ - 360 * (degrees_ / 360) :
179  degrees_ + 360 * (1 + (-degrees_) / 360); // In case compilers disagree as to what -90/360 is.
180 
181  switch ( normalized )
182  {
183  case 0: return src;
184  case 90: return rotate_90_surface(src, true);
185  case 180: return rotate_180_surface(src);
186  case 270: return rotate_90_surface(src, false);
187  case 360: return src;
188  }
189 
190  return rotate_any_surface(src, normalized, zoom_, offset_);
191 }
192 
194 {
195  return greyscale_image(src);
196 }
197 
199 {
200  return monochrome_image(src, threshold_);
201 }
202 
204 {
205  return sepia_image(src);
206 }
207 
209 {
210  return negative_image(src, red_, green_, blue_);
211 }
212 
214 {
215  return alpha_to_greyscale(src);
216 }
217 
219 {
220  return wipe_alpha(src);
221 }
222 
223 // TODO: Is this useful enough to move into formula/callable_objects?
225 {
226 public:
227  pixel_callable(SDL_Point p, color_t clr, uint32_t w, uint32_t h)
228  : p(p), clr(clr), w(w), h(h)
229  {}
230 
231  void get_inputs(wfl::formula_input_vector& inputs) const override
232  {
233  add_input(inputs, "x");
234  add_input(inputs, "y");
235  add_input(inputs, "u");
236  add_input(inputs, "v");
237  add_input(inputs, "red");
238  add_input(inputs, "green");
239  add_input(inputs, "blue");
240  add_input(inputs, "alpha");
241  add_input(inputs, "height");
242  add_input(inputs, "width");
243  }
244 
245  wfl::variant get_value(const std::string& key) const override
246  {
247  using wfl::variant;
248  if(key == "x") {
249  return variant(p.x);
250  } else if(key == "y") {
251  return variant(p.y);
252  } else if(key == "red") {
253  return variant(clr.r);
254  } else if(key == "green") {
255  return variant(clr.g);
256  } else if(key == "blue") {
257  return variant(clr.b);
258  } else if(key == "alpha") {
259  return variant(clr.a);
260  } else if(key == "width") {
261  return variant(w);
262  } else if(key == "height") {
263  return variant(h);
264  } else if(key == "u") {
265  return variant(p.x / static_cast<float>(w));
266  } else if(key == "v") {
267  return variant(p.y / static_cast<float>(h));
268  }
269 
270  return variant();
271  }
272 
273 private:
274  SDL_Point p;
276  uint32_t w, h;
277 };
278 
280 {
281  if(src == nullptr) {
282  return nullptr;
283  }
284 
285  wfl::formula new_alpha(formula_);
286 
287  surface nsurf = src.clone();
288 
289  if(nsurf == nullptr) {
290  std::cerr << "could not make neutral surface...\n";
291  return nullptr;
292  }
293 
294  {
295  surface_lock lock(nsurf);
296  uint32_t* cur = lock.pixels();
297  uint32_t* const end = cur + nsurf->w * src->h;
298  uint32_t* const beg = cur;
299 
300  while(cur != end) {
301  color_t pixel;
302  pixel.a = (*cur) >> 24;
303  pixel.r = (*cur) >> 16;
304  pixel.g = (*cur) >> 8;
305  pixel.b = (*cur);
306 
307  int i = cur - beg;
308  SDL_Point p;
309  p.y = i / nsurf->w;
310  p.x = i % nsurf->w;
311 
312  pixel_callable px(p, pixel, nsurf->w, nsurf->h);
313  pixel.a = std::min<unsigned>(new_alpha.evaluate(px).as_int(), 255);
314  *cur = (pixel.a << 24) + (pixel.r << 16) + (pixel.g << 8) + pixel.b;
315 
316  ++cur;
317  }
318  }
319 
320  return nsurf;
321 }
322 
324 {
325  if(src == nullptr) {
326  return nullptr;
327  }
328 
329  wfl::formula new_red(formulas_[0]);
330  wfl::formula new_green(formulas_[1]);
331  wfl::formula new_blue(formulas_[2]);
332  wfl::formula new_alpha(formulas_[3]);
333 
334  surface nsurf = src.clone();
335 
336  if(nsurf == nullptr) {
337  std::cerr << "could not make neutral surface...\n";
338  return nullptr;
339  }
340 
341  {
342  surface_lock lock(nsurf);
343  uint32_t* cur = lock.pixels();
344  uint32_t* const end = cur + nsurf->w * src->h;
345  uint32_t* const beg = cur;
346 
347  while(cur != end) {
348  color_t pixel;
349  pixel.a = (*cur) >> 24;
350  pixel.r = (*cur) >> 16;
351  pixel.g = (*cur) >> 8;
352  pixel.b = (*cur);
353 
354  int i = cur - beg;
355  SDL_Point p;
356  p.y = i / nsurf->w;
357  p.x = i % nsurf->w;
358 
359  pixel_callable px(p, pixel, nsurf->w, nsurf->h);
360  pixel.r = std::min<unsigned>(new_red.evaluate(px).as_int(), 255);
361  pixel.g = std::min<unsigned>(new_green.evaluate(px).as_int(), 255);
362  pixel.b = std::min<unsigned>(new_blue.evaluate(px).as_int(), 255);
363  pixel.a = std::min<unsigned>(new_alpha.evaluate(px).as_int(), 255);
364  *cur = (pixel.a << 24) + (pixel.r << 16) + (pixel.g << 8) + pixel.b;
365 
366  ++cur;
367  }
368  }
369 
370  return nsurf;
371 }
372 
374 {
375  SDL_Rect area = slice_;
376  if(area.w == 0) {
377  area.w = src->w;
378  }
379 
380  if(area.h == 0) {
381  area.h = src->h;
382  }
383 
384  /*
385  * Unlike other image functions cut_surface does not convert the input
386  * surface to a neutral surface, nor does it convert its return surface
387  * to an optimised surface.
388  *
389  * Since it seems to work for most cases, rather change this caller instead
390  * of the function signature. (The issue was discovered in bug #20876).
391  */
392  return cut_surface(src, area);
393 }
394 
396 {
397  if(x_ >= src->w) {
398  std::stringstream sstr;
399  sstr << "~BLIT(): x-coordinate '"
400  << x_ << "' larger than destination image's width '"
401  << src->w << "' no blitting performed.\n";
402 
403  throw imod_exception(sstr);
404  }
405 
406  if(y_ >= src->h) {
407  std::stringstream sstr;
408  sstr << "~BLIT(): y-coordinate '"
409  << y_ << "' larger than destination image's height '"
410  << src->h << "' no blitting performed.\n";
411 
412  throw imod_exception(sstr);
413  }
414 
415  if(surf_->w + x_ < 0) {
416  std::stringstream sstr;
417  sstr << "~BLIT(): offset and width '"
418  << x_ + surf_->w << "' less than zero no blitting performed.\n";
419 
420  throw imod_exception(sstr);
421  }
422 
423  if(surf_->h + y_ < 0) {
424  std::stringstream sstr;
425  sstr << "~BLIT(): offset and height '"
426  << y_ + surf_->h << "' less than zero no blitting performed.\n";
427 
428  throw imod_exception(sstr);
429  }
430 
431  surface nsrc = src.clone();
432  SDL_Rect r {x_, y_, 0, 0};
433  sdl_blit(surf_, nullptr, nsrc, &r);
434  return nsrc;
435 }
436 
438 {
439  if(src->w == mask_->w && src->h == mask_->h && x_ == 0 && y_ == 0) {
440  return mask_surface(src, mask_);
441  }
442 
443  SDL_Rect r {x_, y_, 0, 0};
444  surface new_mask(src->w, src->h);
445  sdl_blit(mask_, nullptr, new_mask, &r);
446  return mask_surface(src, new_mask);
447 }
448 
450  if(src == nullptr) { return nullptr; }
451 
452  // light_surface wants a neutral surface having same dimensions
453  surface nsurf;
454  if(surf_->w != src->w || surf_->h != src->h) {
455  nsurf = scale_surface(surf_, src->w, src->h);
456  } else {
457  nsurf = surf_;
458  }
459 
460  return light_surface(src, nsurf);
461 }
462 
464 {
465  std::pair<int,int> sz = calculate_size(src);
466 
467  if(nn_) {
468  return scale_surface_sharp(src, sz.first, sz.second);
469  } else {
470  return scale_surface_legacy(src, sz.first, sz.second);
471  }
472 }
473 
474 std::pair<int,int> scale_exact_modification::calculate_size(const surface& src) const
475 {
476  const int old_w = src->w;
477  const int old_h = src->h;
478  int w = get_w();
479  int h = get_h();
480 
481  if(w <= 0) {
482  if(w < 0) {
483  ERR_DP << "width of " << fn_ << " is negative - resetting to original width" << std::endl;
484  }
485  w = old_w;
486  }
487 
488  if(h <= 0) {
489  if(h < 0) {
490  ERR_DP << "height of " << fn_ << " is negative - resetting to original height" << std::endl;
491  }
492  h = old_h;
493  }
494 
495  return {w, h};
496 }
497 
498 std::pair<int,int> scale_into_modification::calculate_size(const surface& src) const
499 {
500  const int old_w = src->w;
501  const int old_h = src->h;
502  long double w = get_w();
503  long double h = get_h();
504 
505  if(w <= 0) {
506  if(w < 0) {
507  ERR_DP << "width of SCALE_INTO is negative - resetting to original width" << std::endl;
508  }
509  w = old_w;
510  }
511 
512  if(h <= 0) {
513  if(h < 0) {
514  ERR_DP << "height of SCALE_INTO is negative - resetting to original height" << std::endl;
515  }
516  h = old_h;
517  }
518 
519  long double ratio = std::min(w / old_w, h / old_h);
520 
521  return {static_cast<int>(old_w * ratio), static_cast<int>(old_h * ratio)};
522 }
523 
525 {
526  if(z_ == 1) {
527  return src;
528  }
529 
530  return scale_surface_xbrz(src, z_);
531 }
532 
533 /*
534  * The Opacity IPF doesn't seem to work with surface-wide alpha and instead needs per-pixel alpha.
535  * If this is needed anywhere else it can be moved back to sdl/utils.*pp.
536  */
538 {
539  surface nsurf = src.clone();
540 
541  if(nsurf == nullptr) {
542  std::cerr << "could not make neutral surface...\n";
543  return nullptr;
544  }
545 
546  uint16_t amount = ftofxp(opacity_);
547 
548  {
549  surface_lock lock(nsurf);
550  uint32_t* beg = lock.pixels();
551  uint32_t* end = beg + nsurf->w * src->h;
552 
553  while(beg != end) {
554  uint8_t alpha = (*beg) >> 24;
555 
556  if(alpha) {
557  uint8_t r, g, b;
558  r = (*beg) >> 16;
559  g = (*beg) >> 8;
560  b = (*beg);
561 
562  alpha = std::min<unsigned>(static_cast<unsigned>(fxpmult(alpha,amount)), 255);
563  *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
564  }
565 
566  ++beg;
567  }
568  }
569 
570  return nsurf;
571 }
572 
574 {
575  return((r_ != 0 || g_ != 0 || b_ != 0)
576  ? adjust_surface_color(src, r_, g_, b_)
577  : src
578  );
579 }
580 
582 {
583  return blend_surface(src, static_cast<double>(a_), color_t(r_, g_, b_));
584 }
585 
587 {
588  return blur_alpha_surface(src, depth_);
589 }
590 
592 {
593  surface ret = src.clone();
594  SDL_FillRect(ret, nullptr, SDL_MapRGBA(ret->format, color_.r, color_.g,
595  color_.b, color_.a));
596  sdl_blit(src, nullptr, ret, nullptr);
597  return ret;
598 }
599 
601 {
602  return swap_channels_image(src, red_, green_, blue_, alpha_);
603 }
604 
605 namespace {
606 
607 struct parse_mod_registration
608 {
609  parse_mod_registration(const char* name, mod_parser parser)
610  {
611  mod_parsers[name] = parser;
612  }
613 };
614 
615 /** A macro for automatic modification parser registration
616  *
617  * It automatically registers the created parser in the mod_parsers map
618  * It should be used just like a function header (look at the uses below)
619  * It should only be used within an anonymous namespace
620  *
621  * @param type The modification type to be registered (unquoted)
622  * @param args_var The name for the string argument provided
623  */
624 #define REGISTER_MOD_PARSER(type, args_var) \
625  static modification* parse_##type##_mod(const std::string&); \
626  static parse_mod_registration parse_##type##_mod_registration_aux(#type, &parse_##type##_mod); \
627  static modification* parse_##type##_mod(const std::string& args_var) \
628 
629 // Color-range-based recoloring
630 REGISTER_MOD_PARSER(TC, args)
631 {
632  std::vector<std::string> params = utils::split(args,',');
633 
634  if(params.size() < 2) {
635  ERR_DP << "too few arguments passed to the ~TC() function" << std::endl;
636 
637  return nullptr;
638  }
639 
640  int side_n = lexical_cast_default<int>(params[0], -1);
641  std::string team_color;
642  if(side_n < 1) {
643  ERR_DP << "invalid team (" << side_n
644  << ") passed to the ~TC() function\n";
645  return nullptr;
646  } else if(side_n <= static_cast<int>(image::get_team_colors().size())) {
647  team_color = image::get_team_colors()[side_n - 1];
648  } else {
649  // This side is not initialized; use default "n"
650  try {
651  team_color = std::to_string(side_n);
652  } catch(const bad_lexical_cast&) {
653  ERR_DP << "bad things happen" << std::endl;
654 
655  return nullptr;
656  }
657  }
658 
659  //
660  // Pass argseters for RC functor
661  //
662  if(!game_config::tc_info(params[1]).size()){
663  ERR_DP << "could not load TC info for '" << params[1]
664  << "' palette\n"
665  << "bailing out from TC\n";
666 
667  return nullptr;
668  }
669 
670  color_range_map rc_map;
671  try {
672  const color_range& new_color = game_config::color_info(team_color);
673  const std::vector<color_t>& old_color = game_config::tc_info(params[1]);
674 
675  rc_map = recolor_range(new_color,old_color);
676  } catch(const config::error& e) {
677  ERR_DP << "caught config::error while processing TC: "
678  << e.message
679  << '\n'
680  << "bailing out from TC\n";
681 
682  return nullptr;
683  }
684 
685  return new rc_modification(rc_map);
686 }
687 
688 // Team-color-based color range selection and recoloring
689 REGISTER_MOD_PARSER(RC, args)
690 {
691  const std::vector<std::string> recolor_params = utils::split(args,'>');
692 
693  if(recolor_params.size() <= 1) {
694  return nullptr;
695  }
696 
697  //
698  // recolor source palette to color range
699  //
700  color_range_map rc_map;
701  try {
702  const color_range& new_color = game_config::color_info(recolor_params[1]);
703  const std::vector<color_t>& old_color = game_config::tc_info(recolor_params[0]);
704 
705  rc_map = recolor_range(new_color,old_color);
706  } catch (const config::error& e) {
707  ERR_DP
708  << "caught config::error while processing color-range RC: "
709  << e.message
710  << '\n';
711  ERR_DP
712  << "bailing out from RC\n";
713  rc_map.clear();
714  }
715 
716  return new rc_modification(rc_map);
717 }
718 
719 // Palette switch
720 REGISTER_MOD_PARSER(PAL, args)
721 {
722  const std::vector<std::string> remap_params = utils::split(args,'>');
723 
724  if(remap_params.size() < 2) {
725  ERR_DP << "not enough arguments passed to the ~PAL() function: " << args << "\n";
726 
727  return nullptr;
728  }
729 
730  try {
731  color_range_map rc_map;
732  const std::vector<color_t>& old_palette = game_config::tc_info(remap_params[0]);
733  const std::vector<color_t>& new_palette =game_config::tc_info(remap_params[1]);
734 
735  for(std::size_t i = 0; i < old_palette.size() && i < new_palette.size(); ++i) {
736  rc_map[old_palette[i]] = new_palette[i];
737  }
738 
739  return new rc_modification(rc_map);
740  } catch(const config::error& e) {
741  ERR_DP
742  << "caught config::error while processing PAL function: "
743  << e.message
744  << '\n';
745  ERR_DP
746  << "bailing out from PAL\n";
747 
748  return nullptr;
749  }
750 }
751 
752 // Flip/flop
753 REGISTER_MOD_PARSER(FL, args)
754 {
755  bool horiz = (args.empty() || args.find("horiz") != std::string::npos);
756  bool vert = (args.find("vert") != std::string::npos);
757 
758  return new fl_modification(horiz, vert);
759 }
760 
761 // Rotations
762 REGISTER_MOD_PARSER(ROTATE, args)
763 {
764  const std::vector<std::string>& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
765  const std::size_t s = slice_params.size();
766 
767  switch(s) {
768  case 0:
769  return new rotate_modification();
770  break;
771  case 1:
772  return new rotate_modification(
773  lexical_cast_default<int>(slice_params[0]));
774  break;
775  case 2:
776  return new rotate_modification(
777  lexical_cast_default<int>(slice_params[0]),
778  lexical_cast_default<int>(slice_params[1]));
779  break;
780  case 3:
781  return new rotate_modification(
782  lexical_cast_default<int>(slice_params[0]),
783  lexical_cast_default<int>(slice_params[1]),
784  lexical_cast_default<int>(slice_params[2]));
785  break;
786  }
787  return nullptr;
788 }
789 
790 // Grayscale
792 {
793  return new gs_modification;
794 }
795 
796 // Black and white
797 REGISTER_MOD_PARSER(BW, args)
798 {
799  const std::vector<std::string>& params = utils::split(args, ',');
800 
801  if(params.size() != 1) {
802  ERR_DP << "~BW() requires exactly one argument" << std::endl;
803  return nullptr;
804  }
805 
806  try {
807  int threshold = std::stoi(params[0]);
808  if(threshold < 0 || threshold > 255) {
809  ERR_DP << "~BW() argument out of range 0 - 255" << std::endl;
810  return nullptr;
811  } else {
812  return new bw_modification(threshold);
813  }
814  } catch (const std::invalid_argument&) {
815  ERR_DP << "unsupported argument in ~BW() function" << std::endl;
816  return nullptr;
817  }
818 }
819 
820 // Sepia
821 REGISTER_MOD_PARSER(SEPIA, )
822 {
823  return new sepia_modification;
824 }
825 
826 // Negative
827 REGISTER_MOD_PARSER(NEG, args)
828 {
829  const std::vector<std::string>& params = utils::split(args, ',');
830 
831  switch (params.size()) {
832  case 0:
833  // apparently -1 may be a magic number
834  // but this is the threshold value required
835  // to fully invert a channel
836  return new negative_modification(-1,-1,-1);
837  break;
838  case 1:
839  try {
840  int threshold = std::stoi(params[0]);
841  if(threshold < -1 || threshold > 255) {
842  ERR_DP << "unsupported argument value in ~NEG() function" << std::endl;
843  return nullptr;
844  } else {
845  return new negative_modification(threshold, threshold, threshold);
846  }
847  } catch (const std::invalid_argument&) {
848  ERR_DP << "unsupported argument value in ~NEG() function" << std::endl;
849  return nullptr;
850  }
851  break;
852  case 3:
853  try {
854  int thresholdRed = std::stoi(params[0]);
855  int thresholdGreen = std::stoi(params[1]);
856  int thresholdBlue = std::stoi(params[2]);
857  if(thresholdRed < -1 || thresholdRed > 255 || thresholdGreen < -1 || thresholdGreen > 255 || thresholdBlue < -1 || thresholdBlue > 255) {
858  ERR_DP << "unsupported argument value in ~NEG() function" << std::endl;
859  return nullptr;
860  } else {
861  return new negative_modification(thresholdRed, thresholdGreen, thresholdBlue);
862  }
863  } catch (const std::invalid_argument&) {
864  ERR_DP << "unsupported argument value in ~NEG() function" << std::endl;
865  return nullptr;
866  }
867  break;
868  default:
869  ERR_DP << "~NEG() requires 0, 1 or 3 arguments" << std::endl;
870  return nullptr;
871  }
872 
873  return nullptr;
874 }
875 
876 // Plot Alpha
877 REGISTER_MOD_PARSER(PLOT_ALPHA, )
878 {
879  return new plot_alpha_modification;
880 }
881 
882 // Wipe Alpha
883 REGISTER_MOD_PARSER(WIPE_ALPHA, )
884 {
885  return new wipe_alpha_modification;
886 }
887 
888 // Adjust Alpha
889 REGISTER_MOD_PARSER(ADJUST_ALPHA, args)
890 {
891  // Formulas may contain commas, so use parenthetical split to ensure that they're properly considered a single argument.
892  // (A comma in a formula is only valid in function parameters or list/map literals, so this should always work.)
893  const std::vector<std::string>& params = utils::parenthetical_split(args, ',', "([", ")]");
894 
895  if(params.size() != 1) {
896  ERR_DP << "~ADJUST_ALPHA() requires exactly 1 arguments" << std::endl;
897  return nullptr;
898  }
899 
900  return new adjust_alpha_modification(params.at(0));
901 }
902 
903 // Adjust Channels
904 REGISTER_MOD_PARSER(CHAN, args)
905 {
906  // Formulas may contain commas, so use parenthetical split to ensure that they're properly considered a single argument.
907  // (A comma in a formula is only valid in function parameters or list/map literals, so this should always work.)
908  const std::vector<std::string>& params = utils::parenthetical_split(args, ',', "([", ")]");
909 
910  if(params.size() < 1 || params.size() > 4) {
911  ERR_DP << "~CHAN() requires 1 to 4 arguments" << std::endl;
912  return nullptr;
913  }
914 
915  return new adjust_channels_modification(params);
916 }
917 
918 // Color-shift
919 REGISTER_MOD_PARSER(CS, args)
920 {
921  std::vector<std::string> const factors = utils::split(args, ',');
922  const std::size_t s = factors.size();
923 
924  if(s == 0) {
925  ERR_DP << "no arguments passed to the ~CS() function" << std::endl;
926  return nullptr;
927  }
928 
929  int r = 0, g = 0, b = 0;
930 
931  r = lexical_cast_default<int>(factors[0]);
932 
933  if(s > 1 ) {
934  g = lexical_cast_default<int>(factors[1]);
935  }
936  if(s > 2 ) {
937  b = lexical_cast_default<int>(factors[2]);
938  }
939 
940  return new cs_modification(r, g, b);
941 }
942 
943 // Color blending
944 REGISTER_MOD_PARSER(BLEND, args)
945 {
946  const std::vector<std::string>& params = utils::split(args, ',');
947 
948  if(params.size() != 4) {
949  ERR_DP << "~BLEND() requires exactly 4 arguments" << std::endl;
950  return nullptr;
951  }
952 
953  float opacity = 0.0f;
954  const std::string& opacity_str = params[3];
955  const std::string::size_type p100_pos = opacity_str.find('%');
956 
957  if(p100_pos == std::string::npos)
958  opacity = lexical_cast_default<float>(opacity_str);
959  else {
960  // make multiplier
961  const std::string& parsed_field = opacity_str.substr(0, p100_pos);
962  opacity = lexical_cast_default<float>(parsed_field);
963  opacity /= 100.0f;
964  }
965 
966  return new blend_modification(
967  lexical_cast_default<int>(params[0]),
968  lexical_cast_default<int>(params[1]),
969  lexical_cast_default<int>(params[2]),
970  opacity);
971 }
972 
973 // Crop/slice
974 REGISTER_MOD_PARSER(CROP, args)
975 {
976  const std::vector<std::string>& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
977  const std::size_t s = slice_params.size();
978 
979  if(s == 0 || (s == 1 && slice_params[0].empty())) {
980  ERR_DP << "no arguments passed to the ~CROP() function" << std::endl;
981  return nullptr;
982  }
983 
984  SDL_Rect slice_rect { 0, 0, 0, 0 };
985 
986  slice_rect.x = lexical_cast_default<int16_t, const std::string&>(slice_params[0]);
987 
988  if(s > 1) {
989  slice_rect.y = lexical_cast_default<int16_t, const std::string&>(slice_params[1]);
990  }
991  if(s > 2) {
992  slice_rect.w = lexical_cast_default<uint16_t, const std::string&>(slice_params[2]);
993  }
994  if(s > 3) {
995  slice_rect.h = lexical_cast_default<uint16_t, const std::string&>(slice_params[3]);
996  }
997 
998  return new crop_modification(slice_rect);
999 }
1000 
1001 static bool check_image(const image::locator& img, std::stringstream & message)
1002 {
1003  if(img.file_exists()) return true;
1004  message << " image not found: '" << img.get_filename() << "'\n";
1005  ERR_DP << message.str();
1006  return false;
1007 }
1008 
1009 // Blit
1010 REGISTER_MOD_PARSER(BLIT, args)
1011 {
1012  std::vector<std::string> param = utils::parenthetical_split(args, ',');
1013  const std::size_t s = param.size();
1014 
1015  if(s == 0 || (s == 1 && param[0].empty())){
1016  ERR_DP << "no arguments passed to the ~BLIT() function" << std::endl;
1017  return nullptr;
1018  }
1019 
1020  if(s > 3){
1021  ERR_DP << "too many arguments passed to the ~BLIT() function" << std::endl;
1022  return nullptr;
1023  }
1024 
1025  int x = 0, y = 0;
1026 
1027  if(s == 3) {
1028  x = lexical_cast_default<int>(param[1]);
1029  y = lexical_cast_default<int>(param[2]);
1030  }
1031 
1032  const image::locator img(param[0]);
1033  std::stringstream message;
1034  message << "~BLIT():";
1035  if(!check_image(img, message))
1036  return nullptr;
1037  surface surf = get_image(img);
1038 
1039  return new blit_modification(surf, x, y);
1040 }
1041 
1042 // Mask
1043 REGISTER_MOD_PARSER(MASK, args)
1044 {
1045  std::vector<std::string> param = utils::parenthetical_split(args, ',');
1046  const std::size_t s = param.size();
1047 
1048  if(s == 0 || (s == 1 && param[0].empty())){
1049  ERR_DP << "no arguments passed to the ~MASK() function" << std::endl;
1050  return nullptr;
1051  }
1052 
1053  int x = 0, y = 0;
1054 
1055  if(s == 3) {
1056  x = lexical_cast_default<int>(param[1]);
1057  y = lexical_cast_default<int>(param[2]);
1058  }
1059 
1060  if(x < 0 || y < 0) {
1061  ERR_DP << "negative position arguments in ~MASK() function" << std::endl;
1062  return nullptr;
1063  }
1064 
1065  const image::locator img(param[0]);
1066  std::stringstream message;
1067  message << "~MASK():";
1068  if(!check_image(img, message))
1069  return nullptr;
1070  surface surf = get_image(img);
1071 
1072  return new mask_modification(surf, x, y);
1073 }
1074 
1075 // Light
1076 REGISTER_MOD_PARSER(L, args)
1077 {
1078  if(args.empty()){
1079  ERR_DP << "no arguments passed to the ~L() function" << std::endl;
1080  return nullptr;
1081  }
1082 
1083  surface surf = get_image(args);
1084 
1085  return new light_modification(surf);
1086 }
1087 
1088 // Scale
1089 REGISTER_MOD_PARSER(SCALE, args)
1090 {
1091  const std::vector<std::string>& scale_params = utils::split(args, ',', utils::STRIP_SPACES);
1092  const std::size_t s = scale_params.size();
1093 
1094  if(s == 0 || (s == 1 && scale_params[0].empty())) {
1095  ERR_DP << "no arguments passed to the ~SCALE() function" << std::endl;
1096  return nullptr;
1097  }
1098 
1099  int w = 0, h = 0;
1100 
1101  w = lexical_cast_default<int, const std::string&>(scale_params[0]);
1102 
1103  if(s > 1) {
1104  h = lexical_cast_default<int, const std::string&>(scale_params[1]);
1105  }
1106 
1107  return new scale_exact_modification(w, h, "SCALE", false);
1108 }
1109 
1110 REGISTER_MOD_PARSER(SCALE_SHARP, args)
1111 {
1112  const std::vector<std::string>& scale_params = utils::split(args, ',', utils::STRIP_SPACES);
1113  const std::size_t s = scale_params.size();
1114 
1115  if(s == 0 || (s == 1 && scale_params[0].empty())) {
1116  ERR_DP << "no arguments passed to the ~SCALE_SHARP() function" << std::endl;
1117  return nullptr;
1118  }
1119 
1120  int w = 0, h = 0;
1121 
1122  w = lexical_cast_default<int, const std::string&>(scale_params[0]);
1123 
1124  if(s > 1) {
1125  h = lexical_cast_default<int, const std::string&>(scale_params[1]);
1126  }
1127 
1128  return new scale_exact_modification(w, h, "SCALE_SHARP", true);
1129 }
1130 
1131 REGISTER_MOD_PARSER(SCALE_INTO, args)
1132 {
1133  const std::vector<std::string>& scale_params = utils::split(args, ',', utils::STRIP_SPACES);
1134  const std::size_t s = scale_params.size();
1135 
1136  if(s == 0 || (s == 1 && scale_params[0].empty())) {
1137  ERR_DP << "no arguments passed to the ~SCALE_INTO() function" << std::endl;
1138  return nullptr;
1139  }
1140 
1141  int w = 0, h = 0;
1142 
1143  w = lexical_cast_default<int, const std::string&>(scale_params[0]);
1144 
1145  if(s > 1) {
1146  h = lexical_cast_default<int, const std::string&>(scale_params[1]);
1147  }
1148 
1149  return new scale_into_modification(w, h, "SCALE_INTO", false);
1150 }
1151 
1152 REGISTER_MOD_PARSER(SCALE_INTO_SHARP, args)
1153 {
1154  const std::vector<std::string>& scale_params = utils::split(args, ',', utils::STRIP_SPACES);
1155  const std::size_t s = scale_params.size();
1156 
1157  if(s == 0 || (s == 1 && scale_params[0].empty())) {
1158  ERR_DP << "no arguments passed to the ~SCALE_INTO_SHARP() function" << std::endl;
1159  return nullptr;
1160  }
1161 
1162  int w = 0, h = 0;
1163 
1164  w = lexical_cast_default<int, const std::string&>(scale_params[0]);
1165 
1166  if(s > 1) {
1167  h = lexical_cast_default<int, const std::string&>(scale_params[1]);
1168  }
1169 
1170  return new scale_into_modification(w, h, "SCALE_INTO_SHARP", true);
1171 }
1172 
1173 // xBRZ
1174 REGISTER_MOD_PARSER(XBRZ, args)
1175 {
1176  int z = lexical_cast_default<int, const std::string &>(args);
1177  if(z < 1 || z > 5) {
1178  z = 5; //only values 2 - 5 are permitted for xbrz scaling factors.
1179  }
1180 
1181  return new xbrz_modification(z);
1182 }
1183 
1184 // scale
1185 
1186 // Gaussian-like blur
1187 REGISTER_MOD_PARSER(BL, args)
1188 {
1189  const int depth = std::max<int>(0, lexical_cast_default<int>(args));
1190 
1191  return new bl_modification(depth);
1192 }
1193 
1194 // Opacity-shift
1195 REGISTER_MOD_PARSER(O, args)
1196 {
1197  const std::string::size_type p100_pos = args.find('%');
1198  float num = 0.0f;
1199  if(p100_pos == std::string::npos) {
1200  num = lexical_cast_default<float,const std::string&>(args);
1201  } else {
1202  // make multiplier
1203  const std::string parsed_field = args.substr(0, p100_pos);
1204  num = lexical_cast_default<float,const std::string&>(parsed_field);
1205  num /= 100.0f;
1206  }
1207 
1208  return new o_modification(num);
1209 }
1210 
1211 //
1212 // ~R(), ~G() and ~B() are the children of ~CS(). Merely syntactic sugar.
1213 // Hence they are at the end of the evaluation.
1214 //
1215 // Red component color-shift
1216 REGISTER_MOD_PARSER(R, args)
1217 {
1218  const int r = lexical_cast_default<int>(args);
1219 
1220  return new cs_modification(r,0,0);
1221 }
1222 
1223 // Green component color-shift
1224 REGISTER_MOD_PARSER(G, args)
1225 {
1226  const int g = lexical_cast_default<int>(args);
1227 
1228  return new cs_modification(0,g,0);
1229 }
1230 
1231 // Blue component color-shift
1232 REGISTER_MOD_PARSER(B, args)
1233 {
1234  const int b = lexical_cast_default<int>(args);
1235 
1236  return new cs_modification(0,0,b);
1237 }
1238 
1239 REGISTER_MOD_PARSER(NOP, )
1240 {
1241  return nullptr;
1242 }
1243 
1244 // Only used to tag terrain images which should not be color-shifted by ToD
1245 REGISTER_MOD_PARSER(NO_TOD_SHIFT, )
1246 {
1247  return nullptr;
1248 }
1249 
1250 // Fake image function used by GUI2 portraits until
1251 // Mordante gets rid of it. *tsk* *tsk*
1252 REGISTER_MOD_PARSER(RIGHT, )
1253 {
1254  return nullptr;
1255 }
1256 
1257 // Add a background color.
1258 REGISTER_MOD_PARSER(BG, args)
1259 {
1260  int c[4] { 0, 0, 0, SDL_ALPHA_OPAQUE };
1261  std::vector<std::string> factors = utils::split(args, ',');
1262 
1263  for(int i = 0; i < std::min<int>(factors.size(), 4); ++i) {
1264  c[i] = lexical_cast_default<int>(factors[i]);
1265  }
1266 
1267  return new background_modification(color_t(c[0], c[1], c[2], c[3]));
1268 }
1269 
1270 // Channel swap
1271 REGISTER_MOD_PARSER(SWAP, args)
1272 {
1273  std::vector<std::string> params = utils::split(args, ',', utils::STRIP_SPACES);
1274 
1275  // accept 3 arguments (rgb) or 4 (rgba)
1276  if(params.size() != 3 && params.size() != 4) {
1277  ERR_DP << "incorrect number of arguments in ~SWAP() function, they must be 3 or 4" << std::endl;
1278  return nullptr;
1279  }
1280 
1281  channel redValue, greenValue, blueValue, alphaValue;
1282  // compare the parameter's value with the constants defined in the channels enum
1283  if(params[0] == "red") {
1284  redValue = RED;
1285  } else if(params[0] == "green") {
1286  redValue = GREEN;
1287  } else if(params[0] == "blue") {
1288  redValue = BLUE;
1289  } else if(params[0] == "alpha") {
1290  redValue = ALPHA;
1291  } else {
1292  ERR_DP << "unsupported argument value in ~SWAP() function: " << params[0] << std::endl;
1293  return nullptr;
1294  }
1295 
1296  // wash, rinse and repeat for the other three channels
1297  if(params[1] == "red") {
1298  greenValue = RED;
1299  } else if(params[1] == "green") {
1300  greenValue = GREEN;
1301  } else if(params[1] == "blue") {
1302  greenValue = BLUE;
1303  } else if(params[1] == "alpha") {
1304  greenValue = ALPHA;
1305  } else {
1306  ERR_DP << "unsupported argument value in ~SWAP() function: " << params[0] << std::endl;
1307  return nullptr;
1308  }
1309 
1310  if(params[2] == "red") {
1311  blueValue = RED;
1312  } else if(params[2] == "green") {
1313  blueValue = GREEN;
1314  } else if(params[2] == "blue") {
1315  blueValue = BLUE;
1316  } else if(params[2] == "alpha") {
1317  blueValue = ALPHA;
1318  } else {
1319  ERR_DP << "unsupported argument value in ~SWAP() function: " << params[0] << std::endl;
1320  return nullptr;
1321  }
1322 
1323  // additional check: the params vector may not have a fourth elementh
1324  // if so, default to the same channel
1325  if(params.size() == 3) {
1326  alphaValue = ALPHA;
1327  } else {
1328  if(params[3] == "red") {
1329  alphaValue = RED;
1330  } else if(params[3] == "green") {
1331  alphaValue = GREEN;
1332  } else if(params[3] == "blue") {
1333  alphaValue = BLUE;
1334  } else if(params[3] == "alpha") {
1335  alphaValue = ALPHA;
1336  } else {
1337  ERR_DP << "unsupported argument value in ~SWAP() function: " << params[3] << std::endl;
1338  return nullptr;
1339  }
1340  }
1341 
1342  return new swap_modification(redValue, greenValue, blueValue, alphaValue);
1343 }
1344 
1345 } // end anon namespace
1346 
1347 } /* end namespace image */
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
Definition: utils.hpp:147
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: picture.cpp:875
pixel_t * pixels() const
Definition: surface.hpp:167
surface recolor_image(surface surf, const color_range_map &map_rgb)
Recolors a surface using a map with source and converted palette values.
Definition: utils.cpp:1005
surface negative_image(const surface &surf, const int thresholdR, const int thresholdG, const int thresholdB)
Definition: utils.cpp:767
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:325
void get_inputs(wfl::formula_input_vector &inputs) const override
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:39
surface adjust_surface_color(const surface &surf, int red, int green, int blue)
Definition: utils.cpp:595
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
variant b_
Definition: function.cpp:722
New lexcical_cast header.
int as_int() const
Definition: variant.cpp:292
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
#define RC(i)
Definition: lvm.cpp:730
A modified priority queue used to order image modifications.
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
virtual std::pair< int, int > calculate_size(const surface &src) const
surface rotate_180_surface(const surface &surf)
Rotates a surface 180 degrees.
Definition: utils.cpp:1874
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
std::vector< formula_input > formula_input_vector
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
#define REGISTER_MOD_PARSER(type, args_var)
A macro for automatic modification parser registration.
#define G(L)
Definition: lstate.h:205
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
#define h
surface scale_surface(const surface &surf, int w, int h)
Scale a surface using alpha-weighted modified bilinear filtering Note: causes artifacts with alpha gr...
Definition: utils.cpp:196
surface blend_surface(const surface &surf, const double amount, const color_t color)
Blends a surface with a color.
Definition: utils.cpp:1716
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
surface flip_surface(const surface &surf)
Definition: utils.cpp:1951
Definitions for the interface to Wesnoth Markup Language (WML).
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
virtual int priority() const
Specifies the priority of the modification.
surface clone() const
Makes a copy of this surface.
Definition: surface.cpp:75
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
void push(modification *mod)
Adds mod to the queue (unless mod is nullptr).
#define b
static const char * name(const std::vector< SDL_Joystick *> &joysticks, const std::size_t index)
Definition: joystick.cpp:48
std::size_t size() const
Returns the number of elements in the queue.
surface mask_surface(const surface &surf, const surface &mask, bool *empty_result, const std::string &filename)
Applies a mask on a surface.
Definition: utils.cpp:1133
surface flop_surface(const surface &surf)
Definition: utils.cpp:1980
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
imod_exception(const std::stringstream &message_stream)
Constructor.
static lg::log_domain log_display("display")
#define fxpmult(x, y)
IN: unsigned and fixed_t - OUT: unsigned.
Definition: math.hpp:300
surface blur_alpha_surface(const surface &surf, int depth)
Cross-fades a surface with alpha channel.
Definition: utils.cpp:1511
Definition: utils.hpp:147
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
REMOVE_EMPTY: remove empty elements.
uint8_t r
Red value.
Definition: color.hpp:177
uint8_t a
Alpha value.
Definition: color.hpp:186
#define ERR_DP
surface cut_surface(const surface &surf, const SDL_Rect &r)
Cuts a rectangle from a surface.
Definition: utils.cpp:1658
Base abstract class for an image-path modification.
color_range_map recolor_range(const color_range &new_range, const std::vector< color_t > &old_rgb)
Converts a source palette using the specified color_range object.
Definition: color_range.cpp:28
surface rotate_90_surface(const surface &surf, bool clockwise)
Rotates a surface 90 degrees.
Definition: utils.cpp:1916
static modification_queue decode(const std::string &)
Decodes modifications from a modification string.
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: math.hpp:297
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
std::unordered_map< color_t, color_t > color_range_map
Definition: color_range.hpp:32
Definition: utils.hpp:147
surface alpha_to_greyscale(const surface &surf)
Definition: utils.cpp:810
std::size_t i
Definition: function.cpp:933
surface monochrome_image(const surface &surf, const int threshold)
Definition: utils.cpp:685
wfl::variant get_value(const std::string &key) const override
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
mock_party p
static map_location::DIRECTION s
double g
Definition: astarsearch.cpp:64
surface scale_surface_xbrz(const surface &surf, std::size_t z)
Scale a surface using xBRZ algorithm.
Definition: utils.cpp:122
Helper class for pinning SDL surfaces into memory.
Definition: surface.hpp:147
A color range definition is made of four reference RGB colors, used for calculating conversions from ...
Definition: color_range.hpp:50
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
int w
Definition: utils.hpp:147
const std::string & get_filename() const
Definition: picture.hpp:84
surface greyscale_image(const surface &surf)
Definition: utils.cpp:640
variant a_
Definition: function.cpp:722
surface light_surface(const surface &surf, const surface &lightmap)
Light surf using lightmap.
Definition: utils.cpp:1310
surface wipe_alpha(const surface &surf)
Definition: utils.cpp:838
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
virtual std::pair< int, int > calculate_size(const surface &src) const
pixel_callable(SDL_Point p, color_t clr, uint32_t w, uint32_t h)
const color_range & color_info(const std::string &name)
this module manages the cache of images.
surface scale_surface_sharp(const surface &surf, int w, int h)
Scale a surface using modified nearest neighbour algorithm.
Definition: utils.cpp:462
Standard logging facilities (interface).
modification * top() const
Returns the top element in the queue .
std::string message
Definition: exceptions.hpp:31
map_type priorities_
Map from a mod&#39;s priority() to the mods having that priority.
const std::vector< color_t > & tc_info(const std::string &name)
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
const std::vector< std::string > & get_team_colors()
Definition: picture.cpp:723
#define e
void pop()
Removes the top element from the queue.
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:33
surface sepia_image(const surface &surf)
Definition: utils.cpp:725
uint8_t g
Green value.
Definition: color.hpp:180
uint8_t b
Blue value.
Definition: color.hpp:183
mock_char c
Thrown when a lexical_cast fails.
surface rotate_any_surface(const surface &surf, float angle, int zoom, int offset)
Rotates a surface by any degrees.
Definition: utils.cpp:1763
bool file_exists() const
Tests whether the file the locater points at exists.
Definition: picture.cpp:668
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
surface swap_channels_image(const surface &surf, channel r, channel g, channel b, channel a)
Definition: utils.cpp:903
virtual surface operator()(const surface &src) const
Applies the image-path modification on the specified surface.
std::vector< std::string > parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
channel
Definition: utils.hpp:147
const std::string message
The error message regarding the failed operation.