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