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