image.cpp

Go to the documentation of this file.
00001 /* $Id: image.cpp 52936 2012-02-06 20:27:20Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Routines for images: load, scale, re-color, etc.
00019  */
00020 
00021 #define GETTEXT_DOMAIN "wesnoth-lib"
00022 
00023 #include "global.hpp"
00024 
00025 #include "color_range.hpp"
00026 #include "config.hpp"
00027 #include "filesystem.hpp"
00028 #include "foreach.hpp"
00029 #include "game_config.hpp"
00030 #include "image.hpp"
00031 #include "image_modifications.hpp"
00032 #include "log.hpp"
00033 #include "gettext.hpp"
00034 #include "serialization/string_utils.hpp"
00035 
00036 #include "SDL_image.h"
00037 
00038 #include <boost/functional/hash.hpp>
00039 
00040 #include <list>
00041 #include <set>
00042 
00043 static lg::log_domain log_display("display");
00044 #define ERR_DP LOG_STREAM(err, log_display)
00045 #define LOG_DP LOG_STREAM(info, log_display)
00046 
00047 static lg::log_domain log_config("config");
00048 #define ERR_CFG LOG_STREAM(err , log_config)
00049 
00050 template<typename T>
00051 struct cache_item
00052 {
00053     cache_item(): item(), loaded(false)
00054     {}
00055 
00056     cache_item(const T &item): item(item), loaded(true)
00057     {}
00058 
00059     T item;
00060     bool loaded;
00061 };
00062 
00063 namespace image {
00064 
00065 template<typename T>
00066 class cache_type
00067 {
00068 public:
00069     cache_type(): content_()
00070     {}
00071 
00072     cache_item<T> &get_element(int index) {
00073         if (static_cast<unsigned>(index) >= content_.size())
00074             content_.resize(index + 1);
00075         return content_[index];
00076     }
00077 
00078     void flush() { content_.clear(); }
00079 
00080 private:
00081     std::vector<cache_item<T> > content_;
00082 };
00083 
00084 template <typename T>
00085 bool locator::in_cache(cache_type<T> &cache) const
00086 {
00087     return index_ < 0 ? false : cache.get_element(index_).loaded;
00088 }
00089 
00090 template <typename T>
00091 const T &locator::locate_in_cache(cache_type<T> &cache) const
00092 {
00093     static T dummy;
00094     return index_ < 0 ? dummy : cache.get_element(index_).item;
00095 }
00096 
00097 template <typename T>
00098 T &locator::access_in_cache(cache_type<T> &cache) const
00099 {
00100     static T dummy;
00101     return index_ < 0 ? dummy : cache.get_element(index_).item;
00102 }
00103 
00104 template <typename T>
00105 void locator::add_to_cache(cache_type<T> &cache, const T &data) const
00106 {
00107     if (index_ >= 0)
00108         cache.get_element(index_) = cache_item<T>(data);
00109 }
00110 
00111 }
00112 
00113 namespace {
00114 
00115 image::locator::locator_finder_t locator_finder;
00116 
00117 /** Definition of all image maps */
00118 image::image_cache images_,
00119         scaled_to_zoom_,
00120         hexed_images_,
00121         scaled_to_hex_images_,
00122         tod_colored_images_,
00123         brightened_images_;
00124 
00125 // cache storing if each image fit in a hex
00126 image::bool_cache in_hex_info_;
00127 
00128 // cache storing if this is an empty hex
00129 image::bool_cache is_empty_hex_;
00130 
00131 // caches storing the diffrent lighted cases for each image
00132 image::lit_cache lit_images_,
00133         lit_scaled_images_;
00134 // caches storing each lightmap generated
00135 image::lit_variants lightmaps_;
00136 
00137 // const int cache_version_ = 0;
00138 
00139 std::map<std::string,bool> image_existence_map;
00140 
00141 // directories where we already cached file existence
00142 std::set<std::string> precached_dirs;
00143 
00144 std::map<surface, surface> reversed_images_;
00145 
00146 int red_adjust = 0, green_adjust = 0, blue_adjust = 0;
00147 
00148 /** List of colors used by the TC image modification */
00149 std::vector<std::string> team_colors;
00150 
00151 int zoom = image::tile_size;
00152 int cached_zoom = 0;
00153 
00154 } // end anon namespace
00155 
00156 namespace image {
00157 
00158 std::list<int> dummy_list;
00159 
00160 mini_terrain_cache_map mini_terrain_cache;
00161 mini_terrain_cache_map mini_fogged_terrain_cache;
00162 
00163 static int last_index_ = 0;
00164 
00165 void flush_cache()
00166 {
00167 #ifdef _OPENMP
00168 #pragma omp critical(image_cache)
00169 #endif //_OPENMP
00170     {
00171         images_.flush();
00172         hexed_images_.flush();
00173         tod_colored_images_.flush();
00174         scaled_to_zoom_.flush();
00175         scaled_to_hex_images_.flush();
00176         brightened_images_.flush();
00177         lit_images_.flush();
00178         lit_scaled_images_.flush();
00179         in_hex_info_.flush();
00180         is_empty_hex_.flush();
00181         mini_terrain_cache.clear();
00182         mini_fogged_terrain_cache.clear();
00183         reversed_images_.clear();
00184         image_existence_map.clear();
00185         precached_dirs.clear();
00186     }
00187     /* We can't reset last_index_, since some locators are still alive
00188        when using :refresh. That would cause them to point to the wrong
00189        images. Not resetting the variable causes a memory leak, though. */
00190     // last_index_ = 0;
00191 }
00192 
00193 void locator::init_index()
00194 {
00195     std::map<value, int>& finder = locator_finder[hash_value(val_)];
00196     std::map<value, int>::iterator i = finder.find(val_);
00197 
00198     if(i == finder.end()) {
00199         index_ = last_index_++;
00200         finder.insert(std::make_pair(val_, index_));
00201     } else {
00202         index_ = i->second;
00203     }
00204 }
00205 
00206 void locator::parse_arguments()
00207 {
00208     std::string& fn = val_.filename_;
00209     if(fn.empty()) {
00210         return;
00211     }
00212     size_t markup_field = fn.find('~');
00213 
00214     if(markup_field != std::string::npos) {
00215         val_.type_ = SUB_FILE;
00216         val_.modifications_ = fn.substr(markup_field, fn.size() - markup_field);
00217         fn = fn.substr(0,markup_field);
00218     }
00219 }
00220 
00221 locator::locator() :
00222     index_(-1),
00223     val_()
00224 {
00225 }
00226 
00227 locator::locator(const locator &a, const std::string& mods):
00228     index_(-1),
00229     val_(a.val_)
00230 {
00231     if(!mods.empty()){
00232             val_.modifications_ += mods;
00233             val_.type_=SUB_FILE;
00234             init_index();
00235     }
00236     else index_=a.index_;
00237 }
00238 
00239 locator::locator(const char *filename) :
00240     index_(-1),
00241     val_(filename)
00242 {
00243     parse_arguments();
00244     init_index();
00245 }
00246 
00247 locator::locator(const std::string &filename) :
00248     index_(-1),
00249     val_(filename)
00250 {
00251     parse_arguments();
00252     init_index();
00253 }
00254 
00255 locator::locator(const std::string &filename, const std::string& modifications) :
00256     index_(-1),
00257     val_(filename, modifications)
00258 {
00259     init_index();
00260 }
00261 
00262 locator::locator(const std::string &filename, const map_location &loc,
00263         int center_x, int center_y, const std::string& modifications) :
00264     index_(-1),
00265     val_(filename, loc, center_x, center_y, modifications)
00266 {
00267     init_index();
00268 }
00269 
00270 locator& locator::operator=(const locator &a)
00271 {
00272     index_ = a.index_;
00273     val_ = a.val_;
00274 
00275     return *this;
00276 }
00277 
00278 locator::value::value(const locator::value& a) :
00279   type_(a.type_), filename_(a.filename_), loc_(a.loc_),
00280   modifications_(a.modifications_),
00281   center_x_(a.center_x_), center_y_(a.center_y_)
00282 {}
00283 
00284 locator::value::value() :
00285     type_(NONE), filename_(), loc_(),
00286     modifications_(),
00287   center_x_(0), center_y_(0)
00288 {}
00289 
00290 locator::value::value(const char *filename) :
00291   type_(FILE), filename_(filename), loc_(),
00292   modifications_(),
00293   center_x_(0), center_y_(0)
00294 {}
00295 
00296 locator::value::value(const std::string& filename) :
00297   type_(FILE), filename_(filename),  loc_(),
00298   modifications_(),
00299   center_x_(0), center_y_(0)
00300 {}
00301 
00302 locator::value::value(const std::string& filename, const std::string& modifications) :
00303   type_(SUB_FILE), filename_(filename), loc_(), modifications_(modifications),
00304   center_x_(0), center_y_(0)
00305 {}
00306 
00307 locator::value::value(const std::string& filename, const map_location& loc, int center_x, int center_y, const std::string& modifications) :
00308   type_(SUB_FILE), filename_(filename), loc_(loc), modifications_(modifications),
00309   center_x_(center_x), center_y_(center_y)
00310 {}
00311 
00312 bool locator::value::operator==(const value& a) const
00313 {
00314     if(a.type_ != type_) {
00315         return false;
00316     } else if(type_ == FILE) {
00317         return filename_ == a.filename_;
00318     } else if(type_ == SUB_FILE) {
00319       return filename_ == a.filename_ && loc_ == a.loc_
00320             && modifications_ == a.modifications_
00321             && center_x_ == a.center_x_ && center_y_ == a.center_y_;
00322     } else {
00323         return false;
00324     }
00325 }
00326 
00327 bool locator::value::operator<(const value& a) const
00328 {
00329     if(type_ != a.type_) {
00330         return type_ < a.type_;
00331     } else if(type_ == FILE) {
00332         return filename_ < a.filename_;
00333     } else if(type_ == SUB_FILE) {
00334         if(filename_ != a.filename_)
00335             return filename_ < a.filename_;
00336         if(loc_ != a.loc_)
00337                 return loc_ < a.loc_;
00338         if(center_x_ != a.center_x_)
00339             return center_x_ < a.center_x_;
00340         if(center_y_ != a.center_y_)
00341             return center_y_ < a.center_y_;
00342         return (modifications_ < a.modifications_);
00343     } else {
00344         return false;
00345     }
00346 }
00347 
00348 size_t hash_value(const locator::value& val) {
00349     using boost::hash_value;
00350     using boost::hash_combine;
00351 
00352     size_t hash = hash_value(val.type_);
00353     if (val.type_ == locator::FILE || val.type_ == locator::SUB_FILE) {
00354         hash_combine(hash, val.filename_);
00355     }
00356     if (val.type_ == locator::SUB_FILE) {
00357         hash_combine(hash, val.loc_.x);
00358         hash_combine(hash, val.loc_.y);
00359         hash_combine(hash, val.center_x_);
00360         hash_combine(hash, val.center_y_);
00361         hash_combine(hash, val.modifications_);
00362     }
00363 
00364     return hash;
00365 }
00366 
00367 // Check if localized file is uptodate according to l10n track index.
00368 // Make sure only that the image is not explicitly recorded as fuzzy,
00369 // in order to be able to use non-tracked images (e.g. from UMC).
00370 static std::set<std::string> fuzzy_localized_files;
00371 static bool localized_file_uptodate (const std::string& loc_file)
00372 {
00373     if (fuzzy_localized_files.empty()) {
00374         // First call, parse track index to collect fuzzy files by path.
00375         std::string fsep = "\xC2\xA6"; // UTF-8 for "broken bar"
00376         std::string trackpath = get_binary_file_location("", "l10n-track");
00377         std::string contents = read_file(trackpath);
00378         std::vector<std::string> lines = utils::split(contents, '\n');
00379         foreach (const std::string &line, lines) {
00380             size_t p1 = line.find(fsep);
00381             if (p1 == std::string::npos)
00382                 continue;
00383             std::string state = line.substr(0, p1);
00384             utils::strip(state);
00385             if (state == "fuzzy") {
00386                 size_t p2 = line.find(fsep, p1 + fsep.length());
00387                 if (p2 == std::string::npos)
00388                     continue;
00389                 std::string relpath = line.substr(p1 + fsep.length(), p2 - p1 - fsep.length());
00390                 fuzzy_localized_files.insert(game_config::path + '/' + relpath);
00391             }
00392         }
00393         fuzzy_localized_files.insert(""); // make sure not empty any more
00394     }
00395     return fuzzy_localized_files.count(loc_file) == 0;
00396 }
00397 
00398 // Return path to localized counterpart of the given file, if any, or empty string.
00399 // Localized counterpart may also be requested to have a suffix to base name.
00400 static std::string get_localized_path (const std::string& file, const std::string& suff = "")
00401 {
00402     std::string dir = directory_name(file);
00403     std::string base = file_name(file);
00404     const size_t pos_ext = base.rfind(".");
00405     std::string loc_base;
00406     if (pos_ext != std::string::npos) {
00407         loc_base = base.substr(0, pos_ext) + suff + base.substr(pos_ext);
00408     } else {
00409         loc_base = base + suff;
00410     }
00411     // TRANSLATORS: This is the language code which will be used
00412     // to store and fetch localized non-textual resources, such as images,
00413     // when they exist. Normally it is just the code of the PO file itself,
00414     // e.g. "de" of de.po for German. But it can also be a comma-separated
00415     // list of language codes by priority, when the localized resource
00416     // found for first of those languages will be used. This is useful when
00417     // two languages share sufficient commonality, that they can use each
00418     // other's resources rather than duplicating them. For example,
00419     // Swedish (sv) and Danish (da) are such, so Swedish translator could
00420     // translate this message as "sv,da", while Danish as "da,sv".
00421     std::vector<std::string> langs = utils::split(_("language code for localized resources^en_US"));
00422     // In case even the original image is split into base and overlay,
00423     // add en_US with lowest priority, since the message above will
00424     // not have it when translated.
00425     langs.push_back("en_US");
00426     foreach (const std::string &lang, langs) {
00427         std::string loc_file = dir + "l10n" + "/" + lang + "/" + loc_base;
00428         if (file_exists(loc_file) && localized_file_uptodate(loc_file)) {
00429             return loc_file;
00430         }
00431     }
00432     return "";
00433 }
00434 
00435 // Load overlay image and compose it with the original surface.
00436 static void add_localized_overlay (const std::string& ovr_file, surface &orig_surf)
00437 {
00438     surface ovr_surf = IMG_Load(ovr_file.c_str());
00439     if (ovr_surf.null()) {
00440         return;
00441     }
00442     SDL_Rect area;
00443     area.x = 0;
00444     area.y = 0;
00445     area.w = ovr_surf->w;
00446     area.h = ovr_surf->h;
00447     sdl_blit(ovr_surf, 0, orig_surf, &area);
00448 }
00449 
00450 surface locator::load_image_file() const
00451 {
00452     surface res;
00453 
00454     std::string location = get_binary_file_location("images", val_.filename_);
00455 
00456 
00457     {
00458         if (!location.empty()) {
00459             // Check if there is a localized image.
00460             const std::string loc_location = get_localized_path(location);
00461             if (!loc_location.empty()) {
00462                 location = loc_location;
00463             }
00464             res = IMG_Load(location.c_str());
00465             // If there was no standalone localized image, check if there is an overlay.
00466             if (!res.null() && loc_location.empty()) {
00467                 const std::string ovr_location = get_localized_path(location, "--overlay");
00468                 if (!ovr_location.empty()) {
00469                     add_localized_overlay(ovr_location, res);
00470                 }
00471             }
00472         }
00473     }
00474 
00475     if (res.null() && !val_.filename_.empty()) {
00476         ERR_DP << "could not open image '" << val_.filename_ << "'\n";
00477         if (game_config::debug && val_.filename_ != game_config::images::missing)
00478             return get_image(game_config::images::missing, UNSCALED);
00479     }
00480 
00481     return res;
00482 }
00483 
00484 //small utility function to store an int from (-256,254) to an signed char
00485 static signed char col_to_uchar(int i) {
00486     return static_cast<signed char>(std::min<int>(127, std::max<int>(-128, i/2)));
00487 }
00488 
00489 light_string get_light_string(int op, int r, int g, int b){
00490     light_string ls;
00491     ls.reserve(4);
00492     ls.push_back(op);
00493     ls.push_back(col_to_uchar(r));
00494     ls.push_back(col_to_uchar(g));
00495     ls.push_back(col_to_uchar(b));
00496     return ls;
00497 }
00498 
00499 static surface apply_light(surface surf, const light_string& ls){
00500     // atomic lightmap operation are handled directly (important to end recursion)
00501     if(ls.size() == 4){
00502         //if no lightmap (first char = -1) then we need the inital value
00503         //(before the halving done for lightmap)
00504         int m = ls[0] == -1 ? 2 : 1;
00505         return adjust_surface_color(surf, ls[1]*m, ls[2]*m, ls[3]*m);
00506     }
00507 
00508     // check if the lightmap is already cached or need to be generated
00509     surface lightmap = NULL;
00510     lit_variants::iterator i = lightmaps_.find(ls);
00511     if(i != lightmaps_.end()) {
00512         lightmap = i->second;
00513     } else {
00514         //build all the 7 paths for lightmap sources
00515         static const std::string p = "terrain/light";
00516         static const std::string lm_img[7] = {
00517             p+"-n.png", p+"-ne.png", p+"-se.png",
00518             p+"-s.png", p+"-sw.png", p+"-nw.png",
00519             p+".png"
00520         };
00521 
00522         //decompose into atomic lightmap operations (4 chars)
00523         for(size_t c = 0; c+3 < ls.size(); c+=4){
00524             light_string sls = ls.substr(c,4);
00525             //get the corresponding image and apply the lightmap operation to it
00526             //This allows to also cache lightmap parts.
00527             //note that we avoid infinite recursion by using only atomic operation
00528             surface lts = image::get_lighted_image(lm_img[sls[0]], sls, HEXED);
00529             //first image will be the base where we blit the others
00530             if(lightmap == NULL) {
00531                 //copy the cached image to avoid modifying the cache
00532                 lightmap = make_neutral_surface(lts);
00533             } else{
00534                 blit_surface(lts, NULL, lightmap, NULL);
00535             }
00536         }
00537         //cache the result
00538         lightmaps_[ls] = lightmap;
00539     }
00540     // apply the final lightmap
00541     return light_surface(surf, lightmap);
00542 }
00543 
00544 surface locator::load_image_sub_file() const
00545 {
00546     surface surf = get_image(val_.filename_, UNSCALED);
00547     if(surf == NULL)
00548         return NULL;
00549 
00550     modification_queue mods = modification::decode(val_.modifications_);
00551 
00552     while(!mods.empty()) {
00553         modification* mod = mods.top();
00554         mods.pop();
00555 
00556         try {
00557             surf = (*mod)(surf);
00558         } catch(const image::modification::texception& e) {
00559             ERR_CFG << "Failed to apply a modification to an image:\n"
00560                 << "Image: " << val_.filename_ << ".\n"
00561                 << "Modifications: " << val_.modifications_ << ".\n"
00562                 << "Error: " << e.message;
00563         }
00564         delete mod;
00565     }
00566 
00567     if(val_.loc_.valid()) {
00568         SDL_Rect srcrect = create_rect(
00569                                        ((tile_size*3) / 4) * val_.loc_.x
00570                                        , tile_size * val_.loc_.y + (tile_size / 2) * (val_.loc_.x % 2)
00571                                        , tile_size
00572                                        , tile_size);
00573 
00574         if(val_.center_x_ >= 0 && val_.center_y_>= 0){
00575             srcrect.x += surf->w/2 - val_.center_x_;
00576             srcrect.y += surf->h/2 - val_.center_y_;
00577         }
00578 
00579         // cut and hex mask, but also check and cache if empty result
00580         surface cut(cut_surface(surf, srcrect));
00581         bool is_empty = false;
00582         surf = mask_surface(cut, get_hexmask(), &is_empty);
00583         // discard empty images to free memory
00584         if(is_empty) {
00585             // Safe because those images are only used by terrain rendering
00586             // and it filters them out.
00587             // A safer and more general way would be to keep only one copy of it
00588             surf = NULL;
00589         }
00590         add_to_cache(is_empty_hex_, is_empty);
00591     }
00592 
00593     return surf;
00594 }
00595 
00596 bool locator::file_exists() const
00597 {
00598     return !get_binary_file_location("images", val_.filename_).empty();
00599 }
00600 
00601 surface locator::load_from_disk() const
00602 {
00603     switch(val_.type_) {
00604         case FILE:
00605             return load_image_file();
00606         case SUB_FILE:
00607             return load_image_sub_file();
00608         default:
00609             return surface(NULL);
00610     }
00611 }
00612 
00613 
00614 manager::manager() {}
00615 
00616 manager::~manager()
00617 {
00618     flush_cache();
00619 }
00620 
00621 SDL_PixelFormat last_pixel_format;
00622 
00623 void set_pixel_format(SDL_PixelFormat* format)
00624 {
00625     assert(format != NULL);
00626 
00627     SDL_PixelFormat &f = *format;
00628     SDL_PixelFormat &l = last_pixel_format;
00629     // if the pixel format change, we clear the cache,
00630     // because some images are now optimized for the wrong display format
00631     // FIXME: 8 bpp use palette, need to compare them. For now assume a change
00632     if (format->BitsPerPixel == 8 ||
00633         f.BitsPerPixel != l.BitsPerPixel || f.BytesPerPixel != l.BytesPerPixel ||
00634         f.Rmask != l.Rmask || f.Gmask != l.Gmask || f.Bmask != l.Bmask ||
00635         f.Rloss != l.Rloss || f.Gloss != l.Gloss || f.Bloss != l.Bloss ||
00636         f.Rshift != l.Rshift || f.Gshift != l.Gshift || f.Bshift != l.Bshift ||
00637         f.colorkey != l.colorkey || f.alpha != l.alpha)
00638     {
00639         LOG_DP << "detected a new display format\n";
00640         flush_cache();
00641     }
00642     last_pixel_format = *format;
00643 }
00644 
00645 void set_color_adjustment(int r, int g, int b)
00646 {
00647     if(r != red_adjust || g != green_adjust || b != blue_adjust) {
00648         red_adjust = r;
00649         green_adjust = g;
00650         blue_adjust = b;
00651         tod_colored_images_.flush();
00652         brightened_images_.flush();
00653         lit_images_.flush();
00654         lit_scaled_images_.flush();
00655         reversed_images_.clear();
00656     }
00657 }
00658 
00659 color_adjustment_resetter::color_adjustment_resetter()
00660 : r_(red_adjust), g_(green_adjust), b_(blue_adjust)
00661 {
00662 }
00663 
00664 void color_adjustment_resetter::reset()
00665 {
00666     set_color_adjustment(r_, g_, b_);
00667 }
00668 
00669 void set_team_colors(const std::vector<std::string>* colors)
00670 {
00671     if (colors == NULL)
00672         team_colors.clear();
00673     else {
00674         team_colors = *colors;
00675     }
00676 }
00677 
00678 const std::vector<std::string>& get_team_colors()
00679 {
00680     return team_colors;
00681 }
00682 
00683 void set_zoom(int amount)
00684 {
00685     if(amount != zoom) {
00686         zoom = amount;
00687         tod_colored_images_.flush();
00688         brightened_images_.flush();
00689         reversed_images_.clear();
00690 
00691         // We keep these caches if:
00692         // we use default zoom (it doesn't need those)
00693         // or if they are already at the wanted zoom.
00694         if (zoom != tile_size && zoom != cached_zoom) {
00695             scaled_to_zoom_.flush();
00696             scaled_to_hex_images_.flush();
00697             lit_scaled_images_.flush();
00698             cached_zoom = zoom;
00699         }
00700     }
00701 }
00702 
00703 static surface get_hexed(const locator& i_locator)
00704 {
00705     surface image(get_image(i_locator, UNSCALED));
00706     // hex cut tiles, also check and cache if empty result
00707     bool is_empty = false;
00708     surface res = mask_surface(image, get_hexmask(), &is_empty, i_locator.get_filename());
00709     i_locator.add_to_cache(is_empty_hex_, is_empty);
00710     return res;
00711 }
00712 
00713 static surface get_scaled_to_hex(const locator& i_locator)
00714 {
00715     surface img = get_image(i_locator, HEXED);
00716     return scale_surface(img, zoom, zoom);
00717 }
00718 
00719 static surface get_tod_colored(const locator& i_locator)
00720 {
00721     surface img = get_image(i_locator, SCALED_TO_HEX);
00722     return adjust_surface_color(img, red_adjust, green_adjust, blue_adjust);
00723 }
00724 
00725 static surface get_scaled_to_zoom(const locator& i_locator)
00726 {
00727     assert(zoom != tile_size);
00728     assert(tile_size != 0);
00729 
00730     surface res(get_image(i_locator, UNSCALED));
00731     // For some reason haloes seems to have invalid images, protect against crashing
00732     if(!res.null()) {
00733         return scale_surface(res, ((res.get()->w * zoom) / tile_size), ((res.get()->h * zoom) / tile_size));
00734     } else {
00735         return surface(NULL);
00736     }
00737 }
00738 
00739 static surface get_brightened(const locator& i_locator)
00740 {
00741     surface image(get_image(i_locator, TOD_COLORED));
00742     return surface(brighten_image(image, ftofxp(game_config::hex_brightening)));
00743 }
00744 
00745 ///translate type to a simpler one when possible
00746 static TYPE simplify_type(const image::locator& i_locator, TYPE type){
00747     switch(type) {
00748     case SCALED_TO_ZOOM:
00749         if(zoom == tile_size)
00750             type = UNSCALED;
00751         break;
00752     case BRIGHTENED:
00753         if(ftofxp(game_config::hex_brightening) == ftofxp(1.0))
00754             type = TOD_COLORED;
00755         break;
00756     default:
00757         break;
00758     }
00759 
00760     if(type == TOD_COLORED) {
00761         if (red_adjust==0 && green_adjust==0 && blue_adjust==0)
00762             type = SCALED_TO_HEX;
00763     }
00764 
00765     if(type == SCALED_TO_HEX) {
00766         if(zoom == tile_size)
00767             type = HEXED;
00768     }
00769 
00770     if(type == HEXED) {
00771         // check if the image is already hex-cut by the location system
00772         if(i_locator.get_loc().valid())
00773             type = UNSCALED;
00774     }
00775 
00776     return type;
00777 }
00778 
00779 
00780 surface get_image(const image::locator& i_locator, TYPE type)
00781 {
00782     surface res;
00783 
00784     if(i_locator.is_void())
00785         return res;
00786 
00787     type = simplify_type(i_locator, type);
00788 
00789     image_cache *imap;
00790     // select associated cache
00791     switch(type) {
00792     case UNSCALED:
00793         imap = &images_;
00794         break;
00795     case TOD_COLORED:
00796         imap = &tod_colored_images_;
00797         break;
00798     case SCALED_TO_ZOOM:
00799         imap = &scaled_to_zoom_;
00800         break;
00801     case HEXED:
00802         imap = &hexed_images_;
00803         break;
00804     case SCALED_TO_HEX:
00805         imap = &scaled_to_hex_images_;
00806         break;
00807     case BRIGHTENED:
00808         imap = &brightened_images_;
00809         break;
00810     default:
00811         return res;
00812     }
00813 
00814     // return the image if already cached
00815     bool tmp;
00816 #ifdef _OPENMP
00817 #pragma omp critical(image_cache)
00818 #endif //_OPENMP
00819     tmp=i_locator.in_cache(*imap);
00820 
00821     if(tmp) {
00822         surface result;
00823 #ifdef _OPENMP
00824 #pragma omp critical(image_cache)
00825 #endif //_OPENMP
00826         result = i_locator.locate_in_cache(*imap);
00827         return result;
00828     }
00829 
00830     // not cached, generate it
00831     switch(type) {
00832     case UNSCALED:
00833         // If type is unscaled, directly load the image from the disk.
00834         res = i_locator.load_from_disk();
00835         break;
00836     case TOD_COLORED:
00837         res = get_tod_colored(i_locator);
00838         break;
00839     case SCALED_TO_ZOOM:
00840         res = get_scaled_to_zoom(i_locator);
00841         break;
00842     case HEXED:
00843         res = get_hexed(i_locator);
00844         break;
00845     case SCALED_TO_HEX:
00846         res = get_scaled_to_hex(i_locator);
00847         break;
00848     case BRIGHTENED:
00849         res = get_brightened(i_locator);
00850         break;
00851     default:
00852         return res;
00853     }
00854 
00855     // Optimizes surface before storing it
00856     if(res)
00857         res = create_optimized_surface(res);
00858 
00859 #ifdef _OPENMP
00860 #pragma omp critical(image_cache)
00861 #endif //_OPENMP
00862     i_locator.add_to_cache(*imap, res);
00863 
00864     return res;
00865 }
00866 
00867 surface get_lighted_image(const image::locator& i_locator, const light_string& ls, TYPE type)
00868 {
00869     surface res;
00870     if(i_locator.is_void())
00871         return res;
00872 
00873     if(type == SCALED_TO_HEX && zoom == tile_size){
00874         type = HEXED;
00875     }
00876 
00877     // select associated cache
00878     lit_cache* imap = &lit_images_;
00879     if(type == SCALED_TO_HEX)
00880         imap = &lit_scaled_images_;
00881 
00882     // if no light variants yet, need to add an empty map
00883     if(!i_locator.in_cache(*imap)){
00884         i_locator.add_to_cache(*imap, lit_variants());
00885     }
00886 
00887     //need access to add it if not found
00888     { // enclose reference pointing to data stored in a changing vector
00889         const lit_variants& lvar = i_locator.locate_in_cache(*imap);
00890         lit_variants::const_iterator lvi = lvar.find(ls);
00891         if(lvi != lvar.end()) {
00892             return lvi->second;
00893         }
00894     }
00895 
00896     // not cached yet, generate it
00897     switch(type) {
00898     case HEXED:
00899         res = get_image(i_locator, HEXED);
00900         res = apply_light(res, ls);
00901         break;
00902     case SCALED_TO_HEX:
00903         //we light before scaling to reuse the unscaled cache
00904         res = get_lighted_image(i_locator, ls, HEXED);
00905         res = scale_surface(res, zoom, zoom);;
00906         break;
00907     default:
00908         ;
00909     }
00910 
00911     // Optimizes surface before storing it
00912     res = create_optimized_surface(res);
00913     // record the lighted surface in the corresponding variants cache
00914     i_locator.access_in_cache(*imap)[ls] = res;
00915 
00916     return res;
00917 }
00918 
00919 
00920 surface get_hexmask()
00921 {
00922     static const image::locator terrain_mask(game_config::images::terrain_mask);
00923     return get_image(terrain_mask, UNSCALED);
00924 }
00925 
00926 bool is_in_hex(const locator& i_locator)
00927 {
00928     bool result;
00929 #ifdef _OPENMP
00930 #pragma omp critical(in_hex_info_)
00931 #endif //_OPENMP
00932     {
00933     if(i_locator.in_cache(in_hex_info_)) {
00934         result= i_locator.locate_in_cache(in_hex_info_);
00935     } else {
00936         const surface image(get_image(i_locator, UNSCALED));
00937 
00938         bool res = in_mask_surface(image, get_hexmask());
00939 
00940         i_locator.add_to_cache(in_hex_info_, res);
00941 
00942         //std::cout << "in_hex : " << i_locator.get_filename()
00943         //      << " " << (res ? "yes" : "no") << "\n";
00944 
00945         result= res;
00946     }
00947     }
00948     return result;
00949 }
00950 
00951 bool is_empty_hex(const locator& i_locator)
00952 {
00953     if(!i_locator.in_cache(is_empty_hex_)) {
00954         const surface surf = get_image(i_locator, HEXED);
00955         // emptiness of terrain image is checked during hex cut
00956         // so, maybe in cache now, let's recheck
00957         if(!i_locator.in_cache(is_empty_hex_)) {
00958             //should never reach here
00959             //but do it manually if it happens
00960             //assert(false);
00961             bool is_empty = false;
00962             mask_surface(surf, get_hexmask(), &is_empty);
00963             i_locator.add_to_cache(is_empty_hex_, is_empty);
00964         }
00965     }
00966     return i_locator.locate_in_cache(is_empty_hex_);
00967 }
00968 
00969 
00970 surface reverse_image(const surface& surf)
00971 {
00972     if(surf == NULL) {
00973         return surface(NULL);
00974     }
00975 
00976     const std::map<surface,surface>::iterator itor = reversed_images_.find(surf);
00977     if(itor != reversed_images_.end()) {
00978         // sdl_add_ref(itor->second);
00979         return itor->second;
00980     }
00981 
00982     const surface rev(flip_surface(surf));
00983     if(rev == NULL) {
00984         return surface(NULL);
00985     }
00986 
00987     reversed_images_.insert(std::pair<surface,surface>(surf,rev));
00988     // sdl_add_ref(rev);
00989     return rev;
00990 }
00991 
00992 bool exists(const image::locator& i_locator)
00993 {
00994     typedef image::locator loc;
00995     loc::type type = i_locator.get_type();
00996     if (type != loc::FILE && type != loc::SUB_FILE)
00997         return false;
00998 
00999     // The insertion will fail if there is already an element in the cache
01000     std::pair< std::map< std::string, bool >::iterator, bool >
01001         it = image_existence_map.insert(std::make_pair(i_locator.get_filename(), false));
01002     bool &cache = it.first->second;
01003     if (it.second)
01004         cache = !get_binary_file_location("images", i_locator.get_filename()).empty();
01005     return cache;
01006 }
01007 
01008 static void precache_file_existence_internal(const std::string& dir, const std::string& subdir)
01009 {
01010     const std::string checked_dir = dir + "/" + subdir;
01011     if (precached_dirs.find(checked_dir) != precached_dirs.end())
01012         return;
01013     precached_dirs.insert(checked_dir);
01014 
01015     std::vector<std::string> files_found;
01016     std::vector<std::string> dirs_found;
01017     get_files_in_dir(checked_dir, &files_found, &dirs_found,
01018             FILE_NAME_ONLY, NO_FILTER, DONT_REORDER);
01019 
01020     for(std::vector<std::string>::const_iterator f = files_found.begin();
01021             f != files_found.end(); ++f) {
01022         image_existence_map[subdir + *f] = true;
01023     }
01024 
01025     for(std::vector<std::string>::const_iterator d = dirs_found.begin();
01026             d != dirs_found.end(); ++d) {
01027         precache_file_existence_internal(dir, subdir + *d + "/");
01028     }
01029 }
01030 
01031 void precache_file_existence(const std::string& subdir)
01032 {
01033     const std::vector<std::string>& paths = get_binary_paths("images");
01034 
01035     for(std::vector<std::string>::const_iterator p = paths.begin();
01036              p != paths.end(); ++p) {
01037 
01038         precache_file_existence_internal(*p, subdir);
01039     }
01040 }
01041 
01042 bool precached_file_exists(const std::string& file)
01043 {
01044     std::map<std::string, bool>::const_iterator b =  image_existence_map.find(file);
01045     if (b != image_existence_map.end())
01046         return b->second;
01047     else
01048         return false;
01049 }
01050 
01051 } // end namespace image
01052 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:55 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs