The Battle for Wesnoth  1.15.3+dev
picture.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Routines for images: load, scale, re-color, etc.
18  */
19 
20 #include "picture.hpp"
21 
22 #include "config.hpp"
23 #include "display.hpp"
24 #include "filesystem.hpp"
25 #include "game_config.hpp"
26 #include "image_modifications.hpp"
27 #include "log.hpp"
28 #include "preferences/general.hpp"
29 #include "serialization/base64.hpp"
31 #include "sdl/rect.hpp"
32 #include "utils/general.hpp"
33 
34 #include <SDL2/SDL_image.h>
35 
36 #include "utils/functional.hpp"
37 
38 #include <boost/algorithm/string.hpp>
39 #include <boost/functional/hash_fwd.hpp>
40 
41 #include <set>
42 
43 static lg::log_domain log_display("display");
44 #define ERR_DP LOG_STREAM(err, log_display)
45 #define LOG_DP LOG_STREAM(info, log_display)
46 
47 static lg::log_domain log_config("config");
48 #define ERR_CFG LOG_STREAM(err, log_config)
49 
51 
52 template<typename T>
53 struct cache_item
54 {
56  : item()
57  , loaded(false)
58  {
59  }
60 
61  cache_item(const T& item)
62  : item(item)
63  , loaded(true)
64  {
65  }
66 
67  T item;
68  bool loaded;
69 };
70 
71 namespace std
72 {
73 template<>
74 struct hash<image::locator::value>
75 {
76  std::size_t operator()(const image::locator::value& val) const
77  {
78  std::size_t hash = std::hash<unsigned>{}(val.type_);
79 
81  boost::hash_combine(hash, val.filename_);
82  }
83 
84  if(val.type_ == image::locator::SUB_FILE) {
85  boost::hash_combine(hash, val.loc_.x);
86  boost::hash_combine(hash, val.loc_.y);
87  boost::hash_combine(hash, val.center_x_);
88  boost::hash_combine(hash, val.center_y_);
89  boost::hash_combine(hash, val.modifications_);
90  }
91 
92  return hash;
93  }
94 };
95 }
96 
97 namespace image
98 {
99 template<typename T>
101 {
102 public:
104  : content_()
105  {
106  }
107 
109  {
110  if(static_cast<unsigned>(index) >= content_.size())
111  content_.resize(index + 1);
112  return content_[index];
113  }
114 
115  void flush()
116  {
117  content_.clear();
118  }
119 
120 private:
121  std::vector<cache_item<T>> content_;
122 };
123 
124 template<typename T>
125 bool locator::in_cache(cache_type<T>& cache) const
126 {
127  return index_ < 0 ? false : cache.get_element(index_).loaded;
128 }
129 
130 template<typename T>
131 const T& locator::locate_in_cache(cache_type<T>& cache) const
132 {
133  static T dummy;
134  return index_ < 0 ? dummy : cache.get_element(index_).item;
135 }
136 
137 template<typename T>
138 T& locator::access_in_cache(cache_type<T>& cache) const
139 {
140  static T dummy;
141  return index_ < 0 ? dummy : cache.get_element(index_).item;
142 }
143 
144 template<typename T>
145 void locator::add_to_cache(cache_type<T>& cache, const T& data) const
146 {
147  if(index_ >= 0) {
148  cache.get_element(index_) = cache_item<T>(data);
149  }
150 }
151 }
152 
153 namespace
154 {
155 image::locator::locator_finder_t locator_finder;
156 
157 /** Definition of all image maps */
158 image::image_cache images_, scaled_to_zoom_, hexed_images_, scaled_to_hex_images_, tod_colored_images_,
159  brightened_images_;
160 
161 // cache storing if each image fit in a hex
162 image::bool_cache in_hex_info_;
163 
164 // cache storing if this is an empty hex
165 image::bool_cache is_empty_hex_;
166 
167 // caches storing the different lighted cases for each image
168 image::lit_cache lit_images_, lit_scaled_images_;
169 // caches storing each lightmap generated
170 image::lit_variants lightmaps_;
171 
172 // const int cache_version_ = 0;
173 
174 std::map<std::string, bool> image_existence_map;
175 
176 // directories where we already cached file existence
177 std::set<std::string> precached_dirs;
178 
179 std::map<surface, surface> reversed_images_;
180 
181 int red_adjust = 0, green_adjust = 0, blue_adjust = 0;
182 
183 /** List of colors used by the TC image modification */
184 std::vector<std::string> team_colors;
185 
186 unsigned int zoom = tile_size;
187 unsigned int cached_zoom = 0;
188 
189 const std::string data_uri_prefix = "data:";
190 struct parsed_data_URI{
191  explicit parsed_data_URI(utils::string_view data_URI);
192  utils::string_view scheme;
193  utils::string_view mime;
195  utils::string_view data;
196  bool good;
197 };
198 parsed_data_URI::parsed_data_URI(utils::string_view data_URI)
199 {
200  const std::size_t colon = data_URI.find(':');
201  const utils::string_view after_scheme = data_URI.substr(colon + 1);
202 
203  const std::size_t comma = after_scheme.find(',');
204  const utils::string_view type_info = after_scheme.substr(0, comma);
205 
206  const std::size_t semicolon = type_info.find(';');
207 
208  scheme = data_URI.substr(0, colon);
209  base64 = type_info.substr(semicolon + 1);
210  mime = type_info.substr(0, semicolon);
211  data = after_scheme.substr(comma + 1);
212  good = (scheme == "data" && base64 == "base64" && mime.length() > 0 && data.length() > 0);
213 }
214 
215 } // end anon namespace
216 
217 namespace image
218 {
222 
223 static int last_index_ = 0;
224 
226 {
227  {
228  images_.flush();
229  hexed_images_.flush();
230  tod_colored_images_.flush();
231  scaled_to_zoom_.flush();
232  scaled_to_hex_images_.flush();
233  brightened_images_.flush();
234  lit_images_.flush();
235  lit_scaled_images_.flush();
236  in_hex_info_.flush();
237  is_empty_hex_.flush();
238  mini_terrain_cache.clear();
239  mini_fogged_terrain_cache.clear();
240  mini_highlighted_terrain_cache.clear();
241  reversed_images_.clear();
242  image_existence_map.clear();
243  precached_dirs.clear();
244  }
245  /* We can't reset last_index_, since some locators are still alive
246  when using :refresh. That would cause them to point to the wrong
247  images. Not resetting the variable causes a memory leak, though. */
248  // last_index_ = 0;
249 }
250 
251 void locator::init_index()
252 {
253  auto i = locator_finder.find(val_);
254 
255  if(i == locator_finder.end()) {
256  index_ = last_index_++;
257  locator_finder.emplace(val_, index_);
258  } else {
259  index_ = i->second;
260  }
261 }
262 
263 void locator::parse_arguments()
264 {
265  std::string& fn = val_.filename_;
266  if(fn.empty()) {
267  return;
268  }
269 
270  if(boost::algorithm::starts_with(fn, data_uri_prefix)) {
271  parsed_data_URI parsed{fn};
272 
273  if(!parsed.good) {
274  utils::string_view view{ fn };
275  utils::string_view stripped = view.substr(0, view.find(","));
276  ERR_DP << "Invalid data URI: " << stripped << std::endl;
277  }
278 
279  val_.is_data_uri_ = true;
280  }
281 
282  std::size_t markup_field = fn.find('~');
283 
284  if(markup_field != std::string::npos) {
285  val_.type_ = SUB_FILE;
286  val_.modifications_ = fn.substr(markup_field, fn.size() - markup_field);
287  fn = fn.substr(0, markup_field);
288  }
289 }
290 
291 locator::locator()
292  : index_(-1)
293  , val_()
294 {
295 }
296 
297 locator::locator(const locator& a, const std::string& mods)
298  : index_(-1)
299  , val_(a.val_)
300 {
301  if(!mods.empty()) {
302  val_.modifications_ += mods;
303  val_.type_ = SUB_FILE;
304  init_index();
305  } else {
306  index_ = a.index_;
307  }
308 }
309 
310 locator::locator(const char* filename)
311  : index_(-1)
312  , val_(filename)
313 {
314  parse_arguments();
315  init_index();
316 }
317 
318 locator::locator(const std::string& filename)
319  : index_(-1)
320  , val_(filename)
321 {
322  parse_arguments();
323  init_index();
324 }
325 
326 locator::locator(const std::string& filename, const std::string& modifications)
327  : index_(-1)
328  , val_(filename, modifications)
329 {
330  init_index();
331 }
332 
333 locator::locator(const std::string& filename,
334  const map_location& loc,
335  int center_x,
336  int center_y,
337  const std::string& modifications)
338  : index_(-1)
339  , val_(filename, loc, center_x, center_y, modifications)
340 {
341  init_index();
342 }
343 
345 {
346  index_ = a.index_;
347  val_ = a.val_;
348 
349  return *this;
350 }
351 
353  : type_(NONE)
354  , is_data_uri_(false)
355  , filename_()
356  , loc_()
357  , modifications_()
358  , center_x_(0)
359  , center_y_(0)
360 {
361 }
362 
363 locator::value::value(const char* filename)
364  : type_(FILE)
365  , is_data_uri_(false)
366  , filename_(filename)
367  , loc_()
368  , modifications_()
369  , center_x_(0)
370  , center_y_(0)
371 {
372 }
373 
374 locator::value::value(const std::string& filename)
375  : type_(FILE)
376  , is_data_uri_(false)
377  , filename_(filename)
378  , loc_()
379  , modifications_()
380  , center_x_(0)
381  , center_y_(0)
382 {
383 }
384 
385 locator::value::value(const std::string& filename, const std::string& modifications)
386  : type_(SUB_FILE)
387  , is_data_uri_(false)
388  , filename_(filename)
389  , loc_()
390  , modifications_(modifications)
391  , center_x_(0)
392  , center_y_(0)
393 {
394 }
395 
396 locator::value::value(const std::string& filename,
397  const map_location& loc,
398  int center_x,
399  int center_y,
400  const std::string& modifications)
401  : type_(SUB_FILE)
402  , is_data_uri_(false)
403  , filename_(filename)
404  , loc_(loc)
405  , modifications_(modifications)
406  , center_x_(center_x)
407  , center_y_(center_y)
408 {
409 }
410 
412 {
413  if(a.type_ != type_) {
414  return false;
415  } else if(type_ == FILE) {
416  return filename_ == a.filename_;
417  } else if(type_ == SUB_FILE) {
418  return filename_ == a.filename_ && loc_ == a.loc_ && modifications_ == a.modifications_
419  && center_x_ == a.center_x_ && center_y_ == a.center_y_;
420  }
421 
422  return false;
423 }
424 
426 {
427  if(type_ != a.type_) {
428  return type_ < a.type_;
429  } else if(type_ == FILE) {
430  return filename_ < a.filename_;
431  } else if(type_ == SUB_FILE) {
432  if(filename_ != a.filename_)
433  return filename_ < a.filename_;
434  if(loc_ != a.loc_)
435  return loc_ < a.loc_;
436  if(center_x_ != a.center_x_)
437  return center_x_ < a.center_x_;
438  if(center_y_ != a.center_y_)
439  return center_y_ < a.center_y_;
440  return (modifications_ < a.modifications_);
441  }
442 
443  return false;
444 }
445 
446 // Load overlay image and compose it with the original surface.
447 static void add_localized_overlay(const std::string& ovr_file, surface& orig_surf)
448 {
450  surface ovr_surf = IMG_Load_RW(rwops.release(), true); // SDL takes ownership of rwops
451  if(!ovr_surf) {
452  return;
453  }
454 
455  SDL_Rect area {0, 0, ovr_surf->w, ovr_surf->h};
456 
457  sdl_blit(ovr_surf, 0, orig_surf, &area);
458 }
459 
461 {
462  surface res;
463 
464  std::string location = filesystem::get_binary_file_location("images", loc.get_filename());
465 
466  {
467  if(!location.empty()) {
468  // Check if there is a localized image.
469  const std::string loc_location = filesystem::get_localized_path(location);
470  if(!loc_location.empty()) {
471  location = loc_location;
472  }
473 
475  res = IMG_Load_RW(rwops.release(), true); // SDL takes ownership of rwops
476 
477  // If there was no standalone localized image, check if there is an overlay.
478  if(res && loc_location.empty()) {
479  const std::string ovr_location = filesystem::get_localized_path(location, "--overlay");
480  if(!ovr_location.empty()) {
481  add_localized_overlay(ovr_location, res);
482  }
483  }
484  }
485  }
486 
487  if(!res && !loc.get_filename().empty()) {
488  ERR_DP << "could not open image '" << loc.get_filename() << "'" << std::endl;
491  }
492 
493  return res;
494 }
495 
497 {
498  surface surf = get_image(loc.get_filename(), UNSCALED);
499  if(surf == nullptr) {
500  return nullptr;
501  }
502 
504 
505  while(!mods.empty()) {
506  modification* mod = mods.top();
507 
508  try {
509  surf = (*mod)(surf);
510  } catch(const image::modification::imod_exception& e) {
511  std::ostringstream ss;
512  ss << "\n";
513 
514  for(const std::string& mod_name : utils::parenthetical_split(loc.get_modifications(), '~')) {
515  ss << "\t" << mod_name << "\n";
516  }
517 
518  ERR_CFG << "Failed to apply a modification to an image:\n"
519  << "Image: " << loc.get_filename() << "\n"
520  << "Modifications: " << ss.str() << "\n"
521  << "Error: " << e.message << "\n";
522  }
523 
524  // NOTE: do this *after* applying the mod or you'll get crashes!
525  mods.pop();
526  }
527 
528  if(loc.get_loc().valid()) {
529  SDL_Rect srcrect = sdl::create_rect(
530  ((tile_size * 3) / 4) * loc.get_loc().x,
531  tile_size * loc.get_loc().y + (tile_size / 2) * (loc.get_loc().x % 2),
532  tile_size,
533  tile_size
534  );
535 
536  if(loc.get_center_x() >= 0 && loc.get_center_y() >= 0) {
537  srcrect.x += surf->w / 2 - loc.get_center_x();
538  srcrect.y += surf->h / 2 - loc.get_center_y();
539  }
540 
541  // cut and hex mask, but also check and cache if empty result
542  surface cut(cut_surface(surf, srcrect));
543  bool is_empty = false;
544  surf = mask_surface(cut, get_hexmask(), &is_empty);
545 
546  // discard empty images to free memory
547  if(is_empty) {
548  // Safe because those images are only used by terrain rendering
549  // and it filters them out.
550  // A safer and more general way would be to keep only one copy of it
551  surf = nullptr;
552  }
553 
554  loc.add_to_cache(is_empty_hex_, is_empty);
555  }
556 
557  return surf;
558 }
559 
561 {
562  surface surf;
563 
564  parsed_data_URI parsed{loc.get_filename()};
565 
566  if(!parsed.good) {
567  utils::string_view fn = loc.get_filename();
568  utils::string_view stripped = fn.substr(0, fn.find(","));
569  ERR_DP << "Invalid data URI: " << stripped << std::endl;
570  } else if(parsed.mime.substr(0, 5) != "image") {
571  ERR_DP << "Data URI not of image MIME type: " << parsed.mime << std::endl;
572  } else {
573  const std::vector<uint8_t> image_data = base64::decode(parsed.data);
574  filesystem::rwops_ptr rwops{SDL_RWFromConstMem(image_data.data(), image_data.size()), &SDL_FreeRW};
575 
576  if(image_data.empty()) {
577  ERR_DP << "Invalid encoding in data URI" << std::endl;
578  } else if(parsed.mime == "image/png") {
579  surf = IMG_LoadTyped_RW(rwops.release(), true, "PNG");
580  } else if(parsed.mime == "image/jpeg") {
581  surf = IMG_LoadTyped_RW(rwops.release(), true, "JPG");
582  } else {
583  ERR_DP << "Invalid image MIME type: " << parsed.mime << std::endl;
584  }
585  }
586 
587  return surf;
588 }
589 
590 // small utility function to store an int from (-256,254) to an signed char
591 static signed char col_to_uchar(int i)
592 {
593  return static_cast<signed char>(std::min<int>(127, std::max<int>(-128, i / 2)));
594 }
595 
596 light_string get_light_string(int op, int r, int g, int b)
597 {
598  light_string ls;
599  ls.reserve(4);
600  ls.push_back(op);
601  ls.push_back(col_to_uchar(r));
602  ls.push_back(col_to_uchar(g));
603  ls.push_back(col_to_uchar(b));
604 
605  return ls;
606 }
607 
608 static surface apply_light(surface surf, const light_string& ls)
609 {
610  // atomic lightmap operation are handled directly (important to end recursion)
611  if(ls.size() == 4) {
612  // if no lightmap (first char = -1) then we need the initial value
613  //(before the halving done for lightmap)
614  int m = ls[0] == -1 ? 2 : 1;
615  return adjust_surface_color(surf, ls[1] * m, ls[2] * m, ls[3] * m);
616  }
617 
618  // check if the lightmap is already cached or need to be generated
619  surface lightmap = nullptr;
620  auto i = lightmaps_.find(ls);
621  if(i != lightmaps_.end()) {
622  lightmap = i->second;
623  } else {
624  // build all the paths for lightmap sources
625  static const std::string p = "terrain/light/light";
626  static const std::string lm_img[19] {
627  p + ".png",
628  p + "-concave-2-tr.png", p + "-concave-2-r.png", p + "-concave-2-br.png",
629  p + "-concave-2-bl.png", p + "-concave-2-l.png", p + "-concave-2-tl.png",
630  p + "-convex-br-bl.png", p + "-convex-bl-l.png", p + "-convex-l-tl.png",
631  p + "-convex-tl-tr.png", p + "-convex-tr-r.png", p + "-convex-r-br.png",
632  p + "-convex-l-bl.png", p + "-convex-tl-l.png", p + "-convex-tr-tl.png",
633  p + "-convex-r-tr.png", p + "-convex-br-r.png", p + "-convex-bl-br.png"
634  };
635 
636  // decompose into atomic lightmap operations (4 chars)
637  for(std::size_t c = 0; c + 3 < ls.size(); c += 4) {
638  light_string sls = ls.substr(c, 4);
639 
640  // get the corresponding image and apply the lightmap operation to it
641  // This allows to also cache lightmap parts.
642  // note that we avoid infinite recursion by using only atomic operation
643  surface lts = image::get_lighted_image(lm_img[sls[0]], sls, HEXED);
644 
645  // first image will be the base where we blit the others
646  if(lightmap == nullptr) {
647  // copy the cached image to avoid modifying the cache
648  lightmap = lts.clone();
649  } else {
650  sdl_blit(lts, nullptr, lightmap, nullptr);
651  }
652  }
653 
654  // cache the result
655  lightmaps_[ls] = lightmap;
656  }
657 
658  // apply the final lightmap
659  return light_surface(surf, lightmap);
660 }
661 
663 {
664  return val_.is_data_uri_
665  ? parsed_data_URI{val_.filename_}.good
667 }
668 
670 {
671  switch(loc.get_type()) {
672  case locator::FILE:
673  if(loc.is_data_uri()){
674  return load_image_data_uri(loc);
675  } else {
676  return load_image_file(loc);
677  }
678  case locator::SUB_FILE:
679  return load_image_sub_file(loc);
680  default:
681  return surface(nullptr);
682  }
683 }
684 
686 {
687 }
688 
690 {
691  flush_cache();
692 }
693 
694 void set_color_adjustment(int r, int g, int b)
695 {
696  if(r != red_adjust || g != green_adjust || b != blue_adjust) {
697  red_adjust = r;
698  green_adjust = g;
699  blue_adjust = b;
700  tod_colored_images_.flush();
701  brightened_images_.flush();
702  lit_images_.flush();
703  lit_scaled_images_.flush();
704  reversed_images_.clear();
705  }
706 }
707 
708 void set_team_colors(const std::vector<std::string>* colors)
709 {
710  if(colors == nullptr) {
711  team_colors.clear();
712  } else {
713  team_colors = *colors;
714  }
715 }
716 
717 const std::vector<std::string>& get_team_colors()
718 {
719  return team_colors;
720 }
721 
722 void set_zoom(unsigned int amount)
723 {
724  if(amount != zoom) {
725  zoom = amount;
726  tod_colored_images_.flush();
727  brightened_images_.flush();
728  reversed_images_.clear();
729 
730  // We keep these caches if:
731  // we use default zoom (it doesn't need those)
732  // or if they are already at the wanted zoom.
733  if(zoom != tile_size && zoom != cached_zoom) {
734  scaled_to_zoom_.flush();
735  scaled_to_hex_images_.flush();
736  lit_scaled_images_.flush();
737  cached_zoom = zoom;
738  }
739  }
740 }
741 
742 static surface get_hexed(const locator& i_locator)
743 {
744  surface image(get_image(i_locator, UNSCALED));
745  // hex cut tiles, also check and cache if empty result
746  bool is_empty = false;
747  surface res = mask_surface(image, get_hexmask(), &is_empty, i_locator.get_filename());
748  i_locator.add_to_cache(is_empty_hex_, is_empty);
749  return res;
750 }
751 
752 static surface get_scaled_to_hex(const locator& i_locator)
753 {
754  surface img = get_image(i_locator, HEXED);
755  // return scale_surface(img, zoom, zoom);
756 
757  if(img) {
758  return scale_surface_nn(img, zoom, zoom);
759  }
760 
761  return surface(nullptr);
762 
763 }
764 
765 static surface get_tod_colored(const locator& i_locator)
766 {
767  surface img = get_image(i_locator, SCALED_TO_HEX);
768  return adjust_surface_color(img, red_adjust, green_adjust, blue_adjust);
769 }
770 
771 static surface get_scaled_to_zoom(const locator& i_locator)
772 {
773  assert(zoom != tile_size);
774  assert(tile_size != 0);
775 
776  surface res(get_image(i_locator, UNSCALED));
777  // For some reason haloes seems to have invalid images, protect against crashing
778  if(res) {
779  return scale_surface_nn(res, ((res->w * zoom) / tile_size), ((res->h * zoom) / tile_size));
780  }
781 
782  return surface(nullptr);
783 }
784 
785 static surface get_brightened(const locator& i_locator)
786 {
787  surface image(get_image(i_locator, TOD_COLORED));
789 }
790 
791 /// translate type to a simpler one when possible
792 static TYPE simplify_type(const image::locator& i_locator, TYPE type)
793 {
794  switch(type) {
795  case SCALED_TO_ZOOM:
796  if(zoom == tile_size) {
797  type = UNSCALED;
798  }
799 
800  break;
801  case BRIGHTENED:
803  type = TOD_COLORED;
804  }
805 
806  break;
807  default:
808  break;
809  }
810 
811  if(type == TOD_COLORED) {
812  if(red_adjust == 0 && green_adjust == 0 && blue_adjust == 0) {
813  type = SCALED_TO_HEX;
814  }
815  }
816 
817  if(type == SCALED_TO_HEX) {
818  if(zoom == tile_size) {
819  type = HEXED;
820  }
821  }
822 
823  if(type == HEXED) {
824  // check if the image is already hex-cut by the location system
825  if(i_locator.get_loc().valid()) {
826  type = UNSCALED;
827  }
828  }
829 
830  return type;
831 }
832 
834 {
835  surface res;
836 
837  if(i_locator.is_void()) {
838  return res;
839  }
840 
841  type = simplify_type(i_locator, type);
842 
843  image_cache* imap;
844  // select associated cache
845  switch(type) {
846  case UNSCALED:
847  imap = &images_;
848  break;
849  case TOD_COLORED:
850  imap = &tod_colored_images_;
851  break;
852  case SCALED_TO_ZOOM:
853  imap = &scaled_to_zoom_;
854  break;
855  case HEXED:
856  imap = &hexed_images_;
857  break;
858  case SCALED_TO_HEX:
859  imap = &scaled_to_hex_images_;
860  break;
861  case BRIGHTENED:
862  imap = &brightened_images_;
863  break;
864  default:
865  return res;
866  }
867 
868  // return the image if already cached
869  bool tmp = i_locator.in_cache(*imap);
870 
871  if(tmp) {
872  surface result = i_locator.locate_in_cache(*imap);
873  return result;
874  }
875 
876  // not cached, generate it
877  switch(type) {
878  case UNSCALED:
879  // If type is unscaled, directly load the image from the disk.
880  res = load_from_disk(i_locator);
881  break;
882  case TOD_COLORED:
883  res = get_tod_colored(i_locator);
884  break;
885  case SCALED_TO_ZOOM:
886  res = get_scaled_to_zoom(i_locator);
887  break;
888  case HEXED:
889  res = get_hexed(i_locator);
890  break;
891  case SCALED_TO_HEX:
892  res = get_scaled_to_hex(i_locator);
893  break;
894  case BRIGHTENED:
895  res = get_brightened(i_locator);
896  break;
897  default:
898  return res;
899  }
900 
901  i_locator.add_to_cache(*imap, res);
902 
903  return res;
904 }
905 
907 {
908  surface res;
909  if(i_locator.is_void()) {
910  return res;
911  }
912 
913  if(type == SCALED_TO_HEX && zoom == tile_size) {
914  type = HEXED;
915  }
916 
917  // select associated cache
918  lit_cache* imap = &lit_images_;
919  if(type == SCALED_TO_HEX) {
920  imap = &lit_scaled_images_;
921  }
922 
923  // if no light variants yet, need to add an empty map
924  if(!i_locator.in_cache(*imap)) {
925  i_locator.add_to_cache(*imap, lit_variants());
926  }
927 
928  // need access to add it if not found
929  { // enclose reference pointing to data stored in a changing vector
930  const lit_variants& lvar = i_locator.locate_in_cache(*imap);
931  auto lvi = lvar.find(ls);
932  if(lvi != lvar.end()) {
933  return lvi->second;
934  }
935  }
936 
937  // not cached yet, generate it
938  switch(type) {
939  case HEXED:
940  res = get_image(i_locator, HEXED);
941  res = apply_light(res, ls);
942  break;
943  case SCALED_TO_HEX:
944  // we light before scaling to reuse the unscaled cache
945  res = get_lighted_image(i_locator, ls, HEXED);
946  res = scale_surface_nn(res, zoom, zoom);
947  break;
948  default:
949  break;
950  }
951 
952  // record the lighted surface in the corresponding variants cache
953  i_locator.access_in_cache(*imap)[ls] = res;
954 
955  return res;
956 }
957 
959 {
961  return get_image(terrain_mask, UNSCALED);
962 }
963 
964 bool is_in_hex(const locator& i_locator)
965 {
966  bool result;
967  {
968  if(i_locator.in_cache(in_hex_info_)) {
969  result = i_locator.locate_in_cache(in_hex_info_);
970  } else {
971  const surface image(get_image(i_locator, UNSCALED));
972 
973  bool res = in_mask_surface(image, get_hexmask());
974 
975  i_locator.add_to_cache(in_hex_info_, res);
976 
977  // std::cout << "in_hex : " << i_locator.get_filename()
978  // << " " << (res ? "yes" : "no") << "\n";
979 
980  result = res;
981  }
982  }
983 
984  return result;
985 }
986 
987 bool is_empty_hex(const locator& i_locator)
988 {
989  if(!i_locator.in_cache(is_empty_hex_)) {
990  const surface surf = get_image(i_locator, HEXED);
991  // emptiness of terrain image is checked during hex cut
992  // so, maybe in cache now, let's recheck
993  if(!i_locator.in_cache(is_empty_hex_)) {
994  // should never reach here
995  // but do it manually if it happens
996  // assert(false);
997  bool is_empty = false;
998  mask_surface(surf, get_hexmask(), &is_empty);
999  i_locator.add_to_cache(is_empty_hex_, is_empty);
1000  }
1001  }
1002 
1003  return i_locator.locate_in_cache(is_empty_hex_);
1004 }
1005 
1007 {
1008  if(surf == nullptr) {
1009  return surface(nullptr);
1010  }
1011 
1012  const auto itor = reversed_images_.find(surf);
1013  if(itor != reversed_images_.end()) {
1014  // sdl_add_ref(itor->second);
1015  return itor->second;
1016  }
1017 
1018  const surface rev(flip_surface(surf));
1019  if(rev == nullptr) {
1020  return surface(nullptr);
1021  }
1022 
1023  reversed_images_.emplace(surf, rev);
1024  // sdl_add_ref(rev);
1025  return rev;
1026 }
1027 
1028 bool exists(const image::locator& i_locator)
1029 {
1030  typedef image::locator loc;
1031  loc::type type = i_locator.get_type();
1032  if(type != loc::FILE && type != loc::SUB_FILE) {
1033  return false;
1034  }
1035 
1036  // The insertion will fail if there is already an element in the cache
1037  // and this will point to the existing element.
1038  auto iter = image_existence_map.begin();
1039  bool success;
1040 
1041  std::tie(iter, success) = image_existence_map.emplace(i_locator.get_filename(), false);
1042 
1043  bool& cache = iter->second;
1044  if(success) {
1045  if(i_locator.is_data_uri()) {
1046  cache = parsed_data_URI{i_locator.get_filename()}.good;
1047  } else {
1048  cache = !filesystem::get_binary_file_location("images", i_locator.get_filename()).empty();
1049  }
1050  }
1051 
1052  return cache;
1053 }
1054 
1055 static void precache_file_existence_internal(const std::string& dir, const std::string& subdir)
1056 {
1057  const std::string checked_dir = dir + "/" + subdir;
1058  if(precached_dirs.find(checked_dir) != precached_dirs.end()) {
1059  return;
1060  }
1061 
1062  precached_dirs.insert(checked_dir);
1063 
1064  if(!filesystem::is_directory(checked_dir)) {
1065  return;
1066  }
1067 
1068  std::vector<std::string> files_found;
1069  std::vector<std::string> dirs_found;
1070  filesystem::get_files_in_dir(checked_dir, &files_found, &dirs_found, filesystem::FILE_NAME_ONLY,
1072 
1073  for(const auto& f : files_found) {
1074  image_existence_map[subdir + f] = true;
1075  }
1076 
1077  for(const auto& d : dirs_found) {
1078  precache_file_existence_internal(dir, subdir + d + "/");
1079  }
1080 }
1081 
1082 void precache_file_existence(const std::string& subdir)
1083 {
1084  const std::vector<std::string>& paths = filesystem::get_binary_paths("images");
1085 
1086  for(const auto& p : paths) {
1088  }
1089 }
1090 
1091 bool precached_file_exists(const std::string& file)
1092 {
1093  const auto b = image_existence_map.find(file);
1094  if(b != image_existence_map.end()) {
1095  return b->second;
1096  }
1097 
1098  return false;
1099 }
1100 
1101 save_result save_image(const locator& i_locator, const std::string& filename)
1102 {
1103  return save_image(get_image(i_locator), filename);
1104 }
1105 
1106 save_result save_image(const surface& surf, const std::string& filename)
1107 {
1108  if(!surf) {
1109  return save_result::no_image;
1110  }
1111 
1112 #ifdef SDL_IMAGE_VERSION_ATLEAST
1113 #if SDL_IMAGE_VERSION_ATLEAST(2, 0, 2)
1114  if(filesystem::ends_with(filename, ".jpeg") || filesystem::ends_with(filename, ".jpg") || filesystem::ends_with(filename, ".jpe")) {
1115  LOG_DP << "Writing a JPG image to " << filename << std::endl;
1116 
1117  const int err = IMG_SaveJPG_RW(surf, filesystem::make_write_RWops(filename).release(), true, 75); // SDL takes ownership of the RWops
1118  return err == 0 ? save_result::success : save_result::save_failed;
1119  }
1120 #endif
1121 #endif
1122 
1123  if(filesystem::ends_with(filename, ".png")) {
1124  LOG_DP << "Writing a PNG image to " << filename << std::endl;
1125 
1126  const int err = IMG_SavePNG_RW(surf, filesystem::make_write_RWops(filename).release(), true); // SDL takes ownership of the RWops
1127  return err == 0 ? save_result::success : save_result::save_failed;
1128  }
1129 
1130  if(filesystem::ends_with(filename, ".bmp")) {
1131  LOG_DP << "Writing a BMP image to " << filename << std::endl;
1132  const int err = SDL_SaveBMP(surf, filename.c_str()) == 0;
1133  return err == 0 ? save_result::success : save_result::save_failed;
1134  }
1135 
1137 }
1138 
1139 } // end namespace image
TYPE
UNSCALED : image will be drawn "as is" without changing size, even in case of redraw SCALED_TO_ZOOM :...
Definition: picture.hpp:185
static surface load_image_file(const image::locator &loc)
Definition: picture.cpp:460
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: picture.cpp:833
const std::string & get_modifications() const
Definition: picture.hpp:89
int get_center_y() const
Definition: picture.hpp:88
BOOST_CXX14_CONSTEXPR size_type find(basic_string_view s, size_type pos=0) const BOOST_NOEXCEPT
void precache_file_existence(const std::string &subdir)
precache the existence of files in the subdir (ex: "terrain/")
Definition: picture.cpp:1082
map_location loc_
Definition: picture.hpp:55
void add_to_cache(cache_type< T > &cache, const T &data) const
Definition: picture.cpp:145
std::string filename_
Definition: picture.hpp:54
void init_index()
Definition: picture.cpp:251
int dummy
Definition: lstrlib.cpp:1125
static lg::log_domain log_display("display")
surface adjust_surface_color(const surface &surf, int red, int green, int blue)
Definition: utils.cpp:595
void set_team_colors(const std::vector< std::string > *colors)
set the team colors used by the TC image modification use a vector with one string for each team usin...
Definition: picture.cpp:708
static surface get_scaled_to_hex(const locator &i_locator)
Definition: picture.cpp:752
static surface get_hexed(const locator &i_locator)
Definition: picture.cpp:742
bool precached_file_exists(const std::string &file)
Definition: picture.cpp:1091
save_result
Definition: picture.hpp:217
surface reverse_image(const surface &surf)
function to reverse an image.
Definition: picture.cpp:1006
#define a
A modified priority queue used to order image modifications.
bool ends_with(const std::string &str, const std::string &suffix)
mini_terrain_cache_map mini_fogged_terrain_cache
Definition: picture.cpp:220
static surface load_image_data_uri(const image::locator &loc)
Definition: picture.cpp:560
cache_item< T > & get_element(int index)
Definition: picture.cpp:108
rwops_ptr make_read_RWops(const std::string &path)
#define ERR_CFG
Definition: picture.cpp:48
#define val_(o)
Definition: lobject.h:123
static signed char col_to_uchar(int i)
Definition: picture.cpp:591
BOOST_CXX14_CONSTEXPR basic_string_view substr(size_type pos, size_type n=npos) const
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:1101
bool in_mask_surface(const surface &surf, const surface &mask)
Check if a surface fit into a mask.
Definition: utils.cpp:1202
T & access_in_cache(cache_type< T > &cache) const
Definition: picture.cpp:138
STL namespace.
void parse_arguments()
Definition: picture.cpp:263
static surface get_scaled_to_zoom(const locator &i_locator)
Definition: picture.cpp:771
std::map< t_translation::terrain_code, surface > mini_terrain_cache_map
Definition: picture.hpp:130
std::string get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type or an empty string if the file isn&#39;t prese...
type get_type() const
Definition: picture.hpp:90
#define d
surface flip_surface(const surface &surf)
Definition: utils.cpp:1951
Definitions for the interface to Wesnoth Markup Language (WML).
surface clone() const
Makes a copy of this surface.
Definition: surface.cpp:75
std::string terrain_mask
void flush_cache()
Definition: picture.cpp:225
std::string filename_
Definition: action_wml.cpp:555
map_location loc_
#define b
static TYPE simplify_type(const image::locator &i_locator, TYPE type)
translate type to a simpler one when possible
Definition: picture.cpp:792
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1028
surface mask_surface(const surface &surf, const surface &mask, bool *empty_result, const std::string &filename)
Applies a mask on a surface.
Definition: utils.cpp:1133
const T & locate_in_cache(cache_type< T > &cache) const
Definition: picture.cpp:131
static surface get_brightened(const locator &i_locator)
Definition: picture.cpp:785
#define LOG_DP
Definition: picture.cpp:45
static surface load_image_sub_file(const image::locator &loc)
Definition: picture.cpp:496
rwops_ptr make_write_RWops(const std::string &path)
static surface get_tod_colored(const locator &i_locator)
Definition: picture.cpp:765
light_string get_light_string(int op, int r, int g, int b)
return light_string of one light operation(see above)
Definition: picture.cpp:596
std::string modifications_
Definition: picture.hpp:56
static int last_index_
Definition: picture.cpp:223
bool valid() const
Definition: location.hpp:93
std::unordered_map< value, int > locator_finder_t
Definition: picture.hpp:65
#define ERR_DP
Definition: picture.cpp:44
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
void set_zoom(unsigned int amount)
sets the amount scaled images should be scaled.
Definition: picture.cpp:722
std::map< light_string, surface > lit_variants
Definition: picture.hpp:148
std::vector< cache_item< T > > content_
Definition: picture.cpp:121
surface cut_surface(const surface &surf, const SDL_Rect &r)
Cuts a rectangle from a surface.
Definition: utils.cpp:1658
map_display and display: classes which take care of displaying the map and game-data on the screen...
Base abstract class for an image-path modification.
static void precache_file_existence_internal(const std::string &dir, const std::string &subdir)
Definition: picture.cpp:1055
surface scale_surface_nn(const surface &surf, int w, int h)
Scale a surface using the nearest neighbor algorithm (provided by xBRZ lib)
Definition: utils.cpp:159
static modification_queue decode(const std::string &)
Decodes modifications from a modification string.
void set_color_adjustment(int r, int g, int b)
will make all scaled images have these rgb values added to all their pixels.
Definition: picture.cpp:694
std::size_t operator()(const image::locator::value &val) const
Definition: picture.cpp:76
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: math.hpp:317
Encapsulates the map of the game.
Definition: location.hpp:42
std::vector< uint8_t > decode(utils::string_view in)
Definition: base64.cpp:215
surface load_from_disk(const locator &loc)
Definition: picture.cpp:669
const std::vector< std::string > & get_binary_paths(const std::string &type)
Returns a vector with all possible paths to a given type of binary, e.g.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, file_name_option mode, file_filter_option filter, file_reorder_option reorder, file_tree_checksum *checksum)
Populates &#39;files&#39; with all the files and &#39;dirs&#39; with all the directories in dir.
Definition: filesystem.cpp:352
std::size_t i
Definition: function.cpp:933
logger & err()
Definition: log.cpp:78
cache_item()
Definition: picture.cpp:55
mock_party p
static lg::log_domain log_config("config")
locator & operator=(const locator &a)
Definition: picture.cpp:344
double g
Definition: astarsearch.cpp:64
static tcache cache
Definition: minimap.cpp:134
surface get_hexmask()
function to get the standard hex mask
Definition: picture.cpp:958
std::basic_string< signed char > light_string
light_string store colors info of central and adjacent hexes.
Definition: picture.hpp:143
mini_terrain_cache_map mini_highlighted_terrain_cache
Definition: picture.cpp:221
int get_center_x() const
Definition: picture.hpp:87
Declarations for File-IO.
std::unique_ptr< SDL_RWops, void(*)(SDL_RWops *)> rwops_ptr
Definition: filesystem.hpp:42
const bool & debug
std::string get_localized_path(const std::string &file, const std::string &suff)
Returns the localized version of the given filename, if it exists.
const std::string & get_filename() const
Definition: picture.hpp:84
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
bool is_in_hex(const locator &i_locator)
function to check if an image fit into an hex return false if the image has not the standard size...
Definition: picture.cpp:964
bool is_data_uri() const
Definition: picture.hpp:85
bool operator<(const value &a) const
Definition: picture.cpp:425
surface light_surface(const surface &surf, const surface &lightmap)
Light surf using lightmap.
Definition: utils.cpp:1310
mini_terrain_cache_map mini_terrain_cache
Definition: picture.cpp:219
cache_item(const T &item)
Definition: picture.cpp:61
surface get_lighted_image(const image::locator &i_locator, const light_string &ls, TYPE type)
function to get the surface corresponding to an image.
Definition: picture.cpp:906
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an SDL_Rect with the given dimensions.
Definition: rect.hpp:39
const std::vector< std::string > & modifications(bool mp)
Definition: game.cpp:753
Contains the SDL_Rect helper code.
#define f
const map_location & get_loc() const
Definition: picture.hpp:86
unsigned int tile_size
Definition: game_config.cpp:68
bool is_empty_hex(const locator &i_locator)
function to check if an image is empty after hex cut should be only used on terrain image (cache the ...
Definition: picture.cpp:987
surface brighten_image(const surface &surf, fixed_t amount)
Definition: utils.cpp:1044
static void add_localized_overlay(const std::string &ovr_file, surface &orig_surf)
Definition: picture.cpp:447
this module manages the cache of images.
Standard logging facilities (interface).
double hex_brightening
Definition: game_config.cpp:80
modification * top() const
Returns the top element in the queue .
bool operator==(const value &a) const
Definition: picture.cpp:411
const std::vector< std::string > & get_team_colors()
Definition: picture.cpp:717
#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
bool is_void() const
Definition: picture.hpp:95
mock_char c
bool in_cache(cache_type< T > &cache) const
Definition: picture.cpp:125
Exception thrown by the operator() when an error occurs.
bool file_exists() const
Tests whether the file the locater points at exists.
Definition: picture.cpp:662
static surface apply_light(surface surf, const light_string &ls)
Definition: picture.cpp:608
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...
const std::string message
The error message regarding the failed operation.
bool loaded
Definition: picture.cpp:68