35 #include <SDL2/SDL_image.h>
39 #include <boost/algorithm/string.hpp>
40 #include <boost/functional/hash_fwd.hpp>
46 #define ERR_IMG LOG_STREAM(err, log_image)
47 #define WRN_IMG LOG_STREAM(warn, log_image)
48 #define LOG_IMG LOG_STREAM(info, log_image)
49 #define DBG_IMG LOG_STREAM(debug, log_image)
52 #define ERR_CFG LOG_STREAM(err, log_config)
57 struct std::hash<
image::locator::value>
61 std::size_t hash = std::hash<unsigned>{}(val.
type);
64 boost::hash_combine(hash, val.
filename);
68 boost::hash_combine(hash, val.
loc.
x);
69 boost::hash_combine(hash, val.
loc.
y);
70 boost::hash_combine(hash, val.
center_x);
71 boost::hash_combine(hash, val.
center_y);
117 return std::hash<value>{}(
val_);
142 return elem.
loaded ? std::make_optional(elem.item) : std::nullopt;
154 std::array<surface_cache, NUM_TYPES> surfaces_;
160 using texture_cache_map = std::map<image::scale_quality, image::texture_cache>;
162 texture_cache_map textures_;
163 texture_cache_map textures_hexed_;
164 texture_cache_map texture_tod_colored_;
180 std::array<bool_cache, NUM_TYPES> skipped_cache_;
181 int duplicate_loads_ = 0;
182 int total_loads_ = 0;
186 std::map<std::string, bool> image_existence_map;
189 std::set<std::string> precached_dirs;
191 int red_adjust = 0, green_adjust = 0, blue_adjust = 0;
193 const std::string data_uri_prefix =
"data:";
194 struct parsed_data_URI{
195 explicit parsed_data_URI(std::string_view data_URI);
202 parsed_data_URI::parsed_data_URI(std::string_view data_URI)
204 const std::size_t colon = data_URI.find(
':');
205 const std::string_view after_scheme = data_URI.substr(colon + 1);
207 const std::size_t
comma = after_scheme.find(
',');
208 const std::string_view type_info = after_scheme.substr(0,
comma);
210 const std::size_t
semicolon = type_info.find(
';');
212 scheme = data_URI.substr(0, colon);
230 lit_surfaces_.flush();
231 lit_textures_.flush();
232 surface_lightmaps_.clear();
233 texture_lightmaps_.clear();
234 in_hex_info_.flush();
235 is_empty_hex_.flush();
237 textures_hexed_.clear();
238 texture_tod_colored_.clear();
242 image_existence_map.clear();
243 precached_dirs.clear();
279 if(boost::algorithm::starts_with(
filename, data_uri_prefix)) {
280 if(parsed_data_URI parsed{
filename }; !parsed.good) {
282 std::string_view stripped = view.substr(0, view.find(
","));
283 ERR_IMG <<
"Invalid data URI: " << stripped;
289 if(
const std::size_t markup_field =
filename.find(
'~'); markup_field != std::string::npos) {
304 const std::string& filename,
323 return filename ==
a.filename;
325 return std::tie(filename, loc,
modifications, center_x, center_y) ==
326 std::tie(
a.filename,
a.loc,
a.modifications,
a.center_x,
a.center_y);
335 return type <
a.type;
337 return filename <
a.filename;
339 return std::tie(filename, loc,
modifications, center_x, center_y) <
340 std::tie(
a.filename,
a.loc,
a.modifications,
a.center_x,
a.center_y);
350 surface ovr_surf = IMG_Load_RW(rwops.release(),
true);
355 SDL_Rect area {0, 0, ovr_surf->w, ovr_surf->h};
357 sdl_blit(ovr_surf, 0, orig_surf, &area);
371 std::string webp_name = name.substr(0, name.size() - 4) +
".webp";
373 if(!location.empty()) {
374 WRN_IMG <<
"Replaced missing '" << name <<
"' with found '"
375 << webp_name <<
"'.";
380 if(!location.empty()) {
383 if(!loc_location.empty()) {
384 location = loc_location;
388 res = IMG_Load_RW(rwops.release(),
true);
391 if(res && loc_location.empty()) {
393 if(!ovr_location.empty()) {
400 if(!res && !name.empty()) {
401 ERR_IMG <<
"could not open image '" << name <<
"'";
414 if(surf ==
nullptr) {
420 while(!mods.
empty()) {
426 std::ostringstream ss;
430 ss <<
"\t" << mod_name <<
"\n";
433 ERR_CFG <<
"Failed to apply a modification to an image:\n"
435 <<
"Modifications: " << ss.str() <<
"\n"
436 <<
"Error: " <<
e.message;
458 bool is_empty =
false;
483 std::string_view stripped = fn.substr(0, fn.find(
","));
484 ERR_IMG <<
"Invalid data URI: " << stripped;
485 }
else if(parsed.mime.substr(0, 5) !=
"image") {
486 ERR_IMG <<
"Data URI not of image MIME type: " << parsed.mime;
488 const std::vector<uint8_t> image_data =
base64::decode(parsed.data);
491 if(image_data.empty()) {
492 ERR_IMG <<
"Invalid encoding in data URI";
493 }
else if(parsed.mime ==
"image/png") {
494 surf = IMG_LoadTyped_RW(rwops.release(),
true,
"PNG");
495 }
else if(parsed.mime ==
"image/jpeg") {
496 surf = IMG_LoadTyped_RW(rwops.release(),
true,
"JPG");
498 ERR_IMG <<
"Invalid image MIME type: " << parsed.mime;
508 return static_cast<signed char>(std::min<int>(127, std::max<int>(-128,
i / 2)));
529 int m = ls[0] == -1 ? 2 : 1;
535 auto i = surface_lightmaps_.find(ls);
536 if(
i != surface_lightmaps_.end()) {
537 lightmap =
i->second;
540 static const std::string
p =
"terrain/light/light";
541 static const std::string lm_img[19] {
543 p +
"-concave-2-tr.png",
p +
"-concave-2-r.png",
p +
"-concave-2-br.png",
544 p +
"-concave-2-bl.png",
p +
"-concave-2-l.png",
p +
"-concave-2-tl.png",
545 p +
"-convex-br-bl.png",
p +
"-convex-bl-l.png",
p +
"-convex-l-tl.png",
546 p +
"-convex-tl-tr.png",
p +
"-convex-tr-r.png",
p +
"-convex-r-br.png",
547 p +
"-convex-l-bl.png",
p +
"-convex-tl-l.png",
p +
"-convex-tr-tl.png",
548 p +
"-convex-r-tr.png",
p +
"-convex-br-r.png",
p +
"-convex-bl-br.png"
552 for(std::size_t
c = 0;
c + 3 < ls.size();
c += 4) {
561 if(lightmap ==
nullptr) {
563 lightmap = lts.
clone();
565 sdl_blit(lts,
nullptr, lightmap,
nullptr);
570 surface_lightmaps_[ls] = lightmap;
611 if(r != red_adjust ||
g != green_adjust ||
b != blue_adjust) {
616 lit_surfaces_.flush();
617 lit_textures_.flush();
618 texture_tod_colored_.clear();
630 <<
" image to hex mask: " << i_locator;
634 if(
image->w > mask->w ||
image->h >= mask->h) {
637 SDL_MapRGBA(fit->format, 0, 0, 0, 0)
640 int cutx = std::max(0,
image->w - mask->w) / 2;
641 int cuty = std::max(0,
image->h - mask->h) / 2;
642 int cutw = std::min(
image->w, mask->w);
643 int cuth = std::min(
image->h, mask->h);
648 int placex = (mask->w -
image->w) / 2;
649 int placey = (mask->h -
image->h) / 2;
655 bool is_empty =
false;
671 if(red_adjust == 0 && green_adjust == 0 && blue_adjust == 0) {
701 WRN_IMG <<
"get_surface called with unknown image type";
711 DBG_IMG <<
"surface cache [" <<
type <<
"] miss: " << i_locator;
726 throw game::error(
"get_surface somehow lost image type?");
732 DBG_IMG <<
"duplicate load: " << i_locator
733 <<
" [" <<
type <<
"]"
734 <<
" (" << duplicate_loads_ <<
"/" << total_loads_ <<
" total)";
740 DBG_IMG <<
"surface cache [" <<
type <<
"] skip: " << i_locator;
767 auto lvi = lvar.find(ls);
768 if(lvi != lvar.end()) {
773 DBG_IMG <<
"lit surface cache miss: " << i_locator;
804 auto lvi = lvar.find(ls);
805 if(lvi != lvar.end()) {
810 DBG_IMG <<
"lit texture cache miss: " << i_locator;
841 if(i_locator.
in_cache(in_hex_info_)) {
862 if(!i_locator.
in_cache(is_empty_hex_)) {
866 if(!i_locator.
in_cache(is_empty_hex_)) {
870 bool is_empty =
false;
883 if(
type != loc::FILE &&
type != loc::SUB_FILE) {
891 bool& cache = iter->second;
905 const std::string checked_dir = dir +
"/" + subdir;
906 if(precached_dirs.find(checked_dir) != precached_dirs.end()) {
910 precached_dirs.insert(checked_dir);
916 std::vector<std::string> files_found;
917 std::vector<std::string> dirs_found;
921 for(
const auto&
f : files_found) {
922 image_existence_map[subdir +
f] =
true;
925 for(
const auto&
d : dirs_found) {
934 for(
const auto&
p : paths) {
941 const auto b = image_existence_map.find(file);
942 if(
b != image_existence_map.end()) {
961 LOG_IMG <<
"Writing a JPG image to " << filename;
968 LOG_IMG <<
"Writing a PNG image to " << filename;
1009 cache = &textures_hexed_[quality];
1012 cache = &texture_tod_colored_[quality];
1015 cache = &textures_[quality];
1022 return *cached_item;
1025 DBG_IMG <<
"texture cache [" <<
type <<
"] miss: " << i_locator;
1044 DBG_IMG <<
"texture cache [" <<
type <<
"] skip: " << i_locator;
std::unordered_map< Key, cache_item > content_
cache_item & get_element(const Key &item)
Generic locator abstracting the location of an image.
bool is_void() const
Returns true if the locator does not correspond to an actual image.
T & access_in_cache(cache_type< T > &cache) const
bool file_exists() const
Tests whether the file the locator points at exists.
const std::string & get_filename() const
const T & locate_in_cache(cache_type< T > &cache) const
const std::string & get_modifications() const
std::optional< T > copy_from_cache(cache_type< T > &cache) const
bool in_cache(cache_type< T > &cache) const
const map_location & get_loc() const
locator clone(const std::string &mods) const
Returns a copy of this locator with the given IPF.
void add_to_cache(cache_type< T > &cache, T data) const
A modified priority queue used to order image modifications.
modification * top() const
Returns the top element in the queue .
void pop()
Removes the top element from the queue.
Base abstract class for an image-path modification.
static modification_queue decode(const std::string &)
Decodes modifications from a modification string.
surface clone() const
Makes a copy of this surface.
Wrapper class to encapsulate creation and management of an SDL_Texture.
Declarations for File-IO.
Standard logging facilities (interface).
std::vector< uint8_t > decode(std::string_view in)
std::string get_localized_path(const std::string &file, const std::string &suff)
Returns the localized version of the given filename, if it exists.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
std::unique_ptr< SDL_RWops, sdl_rwops_deleter > rwops_ptr
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
rwops_ptr make_read_RWops(const std::string &path)
bool ends_with(const std::string &str, const std::string &suffix)
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't prese...
rwops_ptr make_write_RWops(const std::string &path)
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.
std::pair< std::string, unsigned > item
Functions to load and save images from/to disk.
std::map< light_string, surface > lit_surface_variants
Type used to pair light possibilities with the corresponding lit surface.
mini_terrain_cache_map mini_highlighted_terrain_cache
static surface load_image_sub_file(const image::locator &loc)
bool is_empty_hex(const locator &i_locator)
Checks if an image is empty after hex masking.
static void add_localized_overlay(const std::string &ovr_file, surface &orig_surf)
static surface apply_light(surface surf, const light_string &ls)
bool precached_file_exists(const std::string &file)
static TYPE simplify_type(const image::locator &i_locator, TYPE type)
translate type to a simpler one when possible
static surface load_image_data_uri(const image::locator &loc)
static surface get_tod_colored(const locator &i_locator, bool skip_cache=false)
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
void flush_cache()
Purges all image caches.
static void precache_file_existence_internal(const std::string &dir, const std::string &subdir)
mini_terrain_cache_map mini_terrain_cache
static surface load_from_disk(const locator &loc)
std::map< light_string, texture > lit_texture_variants
static signed char col_to_uchar(int i)
mini_terrain_cache_map mini_fogged_terrain_cache
surface get_surface(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image surface suitable for software manipulation.
surface get_hexmask()
Retrieves the standard hexagonal tile mask.
std::ostream & operator<<(std::ostream &s, const locator &l)
texture get_lighted_texture(const image::locator &i_locator, const light_string &ls)
save_result save_image(const locator &i_locator, const std::string &filename)
std::map< t_translation::terrain_code, surface > mini_terrain_cache_map
std::basic_string< signed char > light_string
Type used to store color information of central and adjacent hexes.
void precache_file_existence(const std::string &subdir)
Precache the existence of files in a binary path subdirectory (e.g.
TYPE
Used to specify the rendering format of images.
@ HEXED
Standard hexagonal tile mask applied, removing portions that don't fit.
@ TOD_COLORED
Same as HEXED, but with Time of Day color tint applied.
@ UNSCALED
Unmodified original-size image.
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
static surface get_hexed(const locator &i_locator, bool skip_cache=false)
surface get_lighted_image(const image::locator &i_locator, const light_string &ls)
Caches and returns an image with a lightmap applied to it.
void set_color_adjustment(int r, int g, int b)
Changes Time of Day color tint for all applicable image types.
light_string get_light_string(int op, int r, int g, int b)
Returns the light_string for one light operation.
bool is_in_hex(const locator &i_locator)
Checks if an image fits into a single hex.
static surface load_image_file(const image::locator &loc)
const std::vector< std::string > & modifications(bool mp)
void fill_surface_rect(surface &dst, SDL_Rect *dst_rect, const uint32_t color)
Fill a rectangle on a given 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...
static lg::log_domain log_image("image")
static lg::log_domain log_config("config")
Contains the SDL_Rect helper code.
Base class for all the errors encountered by the engine.
std::string modifications
bool operator<(const value &a) const
bool operator==(const value &a) const
Exception thrown by the operator() when an error occurs.
Encapsulates the map of the game.
An abstract description of a rectangle with integer coordinates.
std::size_t operator()(const image::locator::value &val) const
static map_location::DIRECTION s
surface adjust_surface_color(const surface &surf, int red, int green, int blue)
surface light_surface(const surface &surf, const surface &lightmap)
Light surf using lightmap.
surface cut_surface(const surface &surf, const SDL_Rect &r)
Cuts a rectangle from a surface.
surface mask_surface(const surface &surf, const surface &mask, bool *empty_result, const std::string &filename)
Applies a mask on a surface.
bool in_mask_surface(const surface &surf, const surface &mask)
Check if a surface fit into a mask.
void sdl_blit(const surface &src, const SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)