image.cpp

Go to the documentation of this file.
00001 /* $Id: image.cpp 26213 2008-04-28 16:54:29Z mog $ */
00002 /*
00003    Copyright (C) 2003 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file image.cpp
00016 //! Routines for images: load, scale, re-color, etc.
00017 
00018 #include "global.hpp"
00019 
00020 #include "color_range.hpp"
00021 #include "config.hpp"
00022 #include "filesystem.hpp"
00023 #include "game_config.hpp"
00024 #include "image.hpp"
00025 #include "log.hpp"
00026 #include "sdl_utils.hpp"
00027 #include "util.hpp"
00028 #include "serialization/string_utils.hpp"
00029 
00030 #include "SDL_image.h"
00031 
00032 #include <cassert>
00033 #include <iostream>
00034 #include <map>
00035 #include <string>
00036 
00037 #define ERR_DP LOG_STREAM(err, display)
00038 
00039 namespace {
00040 
00041 typedef std::map<image::locator::value, int> locator_finder_t;
00042 typedef std::pair<image::locator::value, int> locator_finder_pair;
00043 locator_finder_t locator_finder;
00044 
00045 //! Definition of all image maps
00046 image::image_cache images_,hexed_images_,scaled_to_hex_images_,scaled_to_zoom_,unmasked_images_;
00047 image::image_cache brightened_images_,semi_brightened_images_;
00048 
00049 // const int cache_version_ = 0;
00050 
00051 std::map<image::locator,bool> image_existance_map;
00052 
00053 std::map<surface, surface> reversed_images_;
00054 
00055 int red_adjust = 0, green_adjust = 0, blue_adjust = 0;
00056 
00057 //! List of colors used by the TC image modification
00058 std::vector<std::string> team_colors;
00059 
00060 std::string image_mask;
00061 
00062 int zoom = image::tile_size;
00063 int cached_zoom = 0;
00064 
00065 // The "pointer to surfaces" vector is not cleared anymore
00066 // (the surface are still freed, of course).
00067 // I do not think it is a problem, as the number of different surfaces
00068 // the program may lookup has an upper limit, so its memory usage
00069 // won't grow indefinitely over time.
00070 template<typename T>
00071 void reset_cache(std::vector<image::cache_item<T> >& cache)
00072 {
00073     typename std::vector<image::cache_item<T> >::iterator beg = cache.begin();
00074     typename std::vector<image::cache_item<T> >::iterator end = cache.end();
00075 
00076     for(; beg != end; ++beg) {
00077         beg->loaded = false;
00078         beg->item = T();
00079     }
00080 }
00081 } // end anon namespace
00082 
00083 namespace image {
00084 
00085 mini_terrain_cache_map mini_terrain_cache;
00086 mini_terrain_cache_map mini_fogged_terrain_cache;
00087 
00088 void flush_cache()
00089 {
00090     reset_cache(images_);
00091     reset_cache(hexed_images_);
00092     reset_cache(scaled_to_hex_images_);
00093     reset_cache(scaled_to_zoom_);
00094     reset_cache(unmasked_images_);
00095     reset_cache(brightened_images_);
00096     reset_cache(semi_brightened_images_);
00097     mini_terrain_cache.clear();
00098     mini_fogged_terrain_cache.clear();
00099     reversed_images_.clear();
00100     image_existance_map.clear();
00101 }
00102 
00103 int locator::last_index_ = 0;
00104 
00105 void locator::init_index()
00106 {
00107     locator_finder_t::iterator i = locator_finder.find(val_);
00108 
00109     if(i == locator_finder.end()) {
00110         index_ = last_index_++;
00111         locator_finder.insert(locator_finder_pair(val_, index_));
00112 
00113         images_.push_back(cache_item<surface>());
00114         hexed_images_.push_back(cache_item<surface>());
00115         scaled_to_hex_images_.push_back(cache_item<surface>());
00116         scaled_to_zoom_.push_back(cache_item<surface>());
00117         unmasked_images_.push_back(cache_item<surface>());
00118         brightened_images_.push_back(cache_item<surface>());
00119         semi_brightened_images_.push_back(cache_item<surface>());
00120 
00121     } else {
00122         index_ = i->second;
00123     }
00124 }
00125 
00126 void locator::parse_arguments()
00127 {
00128     std::string& fn = val_.filename_;
00129     if(fn.empty()) {
00130         return;
00131     }
00132     size_t markup_field = fn.find('~');
00133 
00134     if(markup_field != std::string::npos) {
00135         val_.type_ = SUB_FILE;
00136         val_.modifications_ = fn.substr(markup_field, fn.size() - markup_field);
00137         fn = fn.substr(0,markup_field);
00138     }
00139 }
00140 
00141 locator::locator() :
00142     index_(-1)
00143 {
00144 }
00145 
00146 locator::locator(const locator &a, const std::string& mods):
00147      val_(a.val_)
00148 {
00149     if(mods.size()){
00150             val_.modifications_ += mods;
00151             val_.type_=SUB_FILE;
00152             init_index();
00153     }
00154     else index_=a.index_;
00155 }
00156 
00157 locator::locator(const char *filename) :
00158     val_(filename)
00159 {
00160     parse_arguments();
00161     init_index();
00162 }
00163 
00164 locator::locator(const std::string &filename) :
00165     val_(filename)
00166 {
00167     parse_arguments();
00168     init_index();
00169 }
00170 
00171 locator::locator(const char *filename, const std::string& modifications) :
00172     val_(filename, modifications)
00173 {
00174     init_index();
00175 }
00176 
00177 locator::locator(const std::string &filename, const std::string& modifications) :
00178     val_(filename, modifications)
00179 {
00180     init_index();
00181 }
00182 
00183 locator::locator(const std::string &filename, const gamemap::location &loc, const std::string& modifications) :
00184     val_(filename, loc, modifications)
00185 {
00186     init_index();
00187 }
00188 
00189     locator::locator(const std::string &filename, const gamemap::location &loc, int center_x, int center_y, const std::string& modifications) :
00190         val_(filename, loc, center_x, center_y, modifications)
00191 {
00192     init_index();
00193 }
00194 
00195 locator& locator::operator=(const locator &a)
00196 {
00197     index_ = a.index_;
00198     val_ = a.val_;
00199 
00200     return *this;
00201 }
00202 
00203 locator::value::value(const locator::value& a) :
00204   type_(a.type_), filename_(a.filename_), loc_(a.loc_),
00205   modifications_(a.modifications_), 
00206   center_x_(a.center_x_), center_y_(a.center_y_)
00207 {
00208 }
00209 
00210 locator::value::value() :
00211     type_(NONE)
00212 {}
00213 
00214 locator::value::value(const char *filename) :
00215   type_(FILE), filename_(filename)
00216 {
00217 }
00218 
00219 
00220 locator::value::value(const char *filename, const std::string& modifications) :
00221   type_(SUB_FILE), filename_(filename), modifications_(modifications)
00222 {
00223 }
00224 
00225 locator::value::value(const std::string& filename) :
00226   type_(FILE), filename_(filename)
00227 {
00228 }
00229 
00230 locator::value::value(const std::string& filename, const std::string& modifications) :
00231   type_(SUB_FILE), filename_(filename), modifications_(modifications)
00232 {
00233 }
00234 
00235 locator::value::value(const std::string& filename, const gamemap::location& loc, const std::string& modifications) :
00236   type_(SUB_FILE), filename_(filename), loc_(loc), modifications_(modifications)
00237 {
00238 }
00239 
00240 locator::value::value(const std::string& filename, const gamemap::location& loc, int center_x, int center_y, const std::string& modifications) :
00241   type_(SUB_FILE), filename_(filename), loc_(loc), modifications_(modifications), center_x_(center_x), center_y_(center_y)
00242 {   
00243 }
00244 
00245 bool locator::value::operator==(const value& a) const
00246 {
00247     if(a.type_ != type_) {
00248         return false;
00249     } else if(type_ == FILE) {
00250         return filename_ == a.filename_;
00251     } else if(type_ == SUB_FILE) {
00252       return filename_ == a.filename_ && loc_ == a.loc_ && modifications_ == a.modifications_ 
00253           && center_x_ == a.center_x_ && center_y_ == a.center_y_;
00254     } else {
00255         return false;
00256     }
00257 }
00258 
00259 bool locator::value::operator<(const value& a) const
00260 {
00261     if(type_ != a.type_) {
00262         return type_ < a.type_;
00263     } else if(type_ == FILE) {
00264         return filename_ < a.filename_;
00265     } else if(type_ == SUB_FILE) {
00266         if(filename_ != a.filename_)
00267             return filename_ < a.filename_;
00268         if(loc_ != a.loc_)
00269                 return loc_ < a.loc_;
00270         if(center_x_ != a.center_x_)
00271             return center_x_ < a.center_x_;
00272         if(center_y_ != a.center_y_)
00273             return center_y_ < a.center_y_;
00274 
00275         return(modifications_ < a.modifications_);
00276     } else {
00277         return false;
00278     }
00279 }
00280 
00281 surface locator::load_image_file() const
00282 {
00283     surface res;
00284 
00285     std::string location = get_binary_file_location("images", val_.filename_);
00286 
00287     bool try_units = false;
00288 
00289     do {
00290         if (!location.empty()) {
00291             res = IMG_Load(location.c_str());
00292         }
00293         if (res.null() && (!try_units)) {
00294             try_units = true;
00295             location = get_binary_file_location("images", "units/" + val_.filename_);
00296         } else {
00297             try_units = false;
00298         }
00299     } while (try_units);
00300 
00301     if (res.null()) {
00302         ERR_DP << "could not open image '" << val_.filename_ << "'\n";
00303     }
00304 
00305     return res;
00306 }
00307 
00308 surface locator::load_image_sub_file() const
00309 {
00310     const surface mother_surface(get_image(val_.filename_, UNSCALED));
00311     const surface mask(get_image(game_config::terrain_mask_image, UNSCALED));
00312 
00313     if(mother_surface == NULL)
00314         return surface(NULL);
00315     if(mask == NULL)
00316         return surface(NULL);
00317 
00318     surface surf=mother_surface;
00319     if(val_.loc_.x>-1 && val_.loc_.y>-1 && val_.center_x_>-1 && val_.center_y_>-1){
00320         int offset_x = mother_surface->w/2 - val_.center_x_;
00321         int offset_y = mother_surface->h/2 - val_.center_y_;
00322         SDL_Rect srcrect = {
00323             ((tile_size*3) / 4) * val_.loc_.x + offset_x,
00324             tile_size * val_.loc_.y + (tile_size/2) * (val_.loc_.x % 2) + offset_y,
00325             tile_size, tile_size
00326         };
00327 
00328         surface tmp(cut_surface(mother_surface, srcrect));
00329         surf=mask_surface(tmp, mask);
00330     }
00331     else if(val_.loc_.x>-1 && val_.loc_.y>-1 ){
00332       SDL_Rect srcrect = {
00333         ((tile_size*3) / 4) * val_.loc_.x,
00334         tile_size * val_.loc_.y + (tile_size/2) * (val_.loc_.x % 2),
00335         tile_size, tile_size
00336       };
00337 
00338       surface tmp(cut_surface(mother_surface, srcrect));
00339       surf=mask_surface(tmp, mask);
00340     }
00341 
00342 
00343     if(val_.modifications_.size()){
00344         bool xflip = false;
00345         bool yflip = false;
00346         std::map<Uint32, Uint32> recolor_map;
00347         std::vector<std::string> modlist = utils::paranthetical_split(val_.modifications_,'~');
00348         for(std::vector<std::string>::const_iterator i=modlist.begin();
00349             i!= modlist.end();i++){
00350             std::vector<std::string> tmpmod = utils::paranthetical_split(*i);
00351             std::vector<std::string>::const_iterator j=tmpmod.begin();
00352             while(j!= tmpmod.end()){
00353                 std::string function=*j++;
00354                 if(j==tmpmod.end()){
00355                     if(function.size()){
00356                         ERR_DP << "error parsing image modifications: "
00357                             << val_.modifications_<< "\n";
00358                     }
00359                     break;
00360                 }
00361                 std::string field = *j++;
00362 
00363                 if("TC" == function){
00364                     std::vector<std::string> param = utils::split(field,',');
00365                     if(param.size() < 2)
00366                         break;
00367 
00368                     int side_n = lexical_cast_default<int>(param[0], -1);
00369                     std::string team_color; 
00370                     if (side_n < 1) {
00371                         break;
00372                     } else if (side_n < static_cast<int>(team_colors.size())) {
00373                         team_color = team_colors[side_n - 1];
00374                     } else {
00375                     // this side is not inialized use default "n"
00376                         team_color = lexical_cast<std::string>(side_n);
00377                     }   
00378 
00379                     if(game_config::tc_info(param[1]).size()){
00380                         function="RC";
00381                         field = param[1] + ">" + team_color;
00382                     }
00383                 }
00384 
00385                 if("RC" == function){   // Re-color function
00386                     std::vector<std::string> recolor=utils::split(field,'>');
00387                     if(recolor.size()>1){
00388                         std::map<Uint32, Uint32> tmp_map;
00389                         try {
00390                             color_range const& new_color = game_config::color_info(recolor[1]);
00391                             std::vector<Uint32> const& old_color = game_config::tc_info(recolor[0]);
00392                             tmp_map = recolor_range(new_color,old_color);
00393                         } catch (config::error& e) {
00394                             ERR_DP << "caught config::error... " << e.message << std::endl;
00395                         }
00396                         for(std::map<Uint32, Uint32>::const_iterator tmp = tmp_map.begin(); tmp!= tmp_map.end(); tmp++){
00397                             recolor_map[tmp->first] = tmp->second;
00398                         }
00399                     }
00400                 }
00401                 if("FL" == function){   // Flip layer
00402                     if(field.empty() || field.find("horiz") != std::string::npos) {
00403                         xflip = !xflip;
00404                     }
00405                     if(field.find("vert") != std::string::npos) {
00406                         yflip = !yflip;
00407                     }
00408                 }
00409             }
00410         }
00411         surf = recolor_image(surf,recolor_map);
00412         if(xflip) {
00413             surf = flip_surface(surf);
00414         }
00415         if(yflip) {
00416             surf = flop_surface(surf);
00417         }
00418     }
00419     return surf;
00420 }
00421 
00422 surface locator::load_from_disk() const
00423 {
00424     switch(val_.type_) {
00425         case FILE:
00426             return load_image_file();
00427         case SUB_FILE:
00428             return load_image_sub_file();
00429         default:
00430             return surface(NULL);
00431     }
00432 }
00433 
00434 #if 0
00435 template<typename T>
00436 bool locator::in_cache(const std::vector<cache_item<T> >& cache) const
00437 {
00438     if(index_ == -1)
00439         return false;
00440 
00441     return cache[index_].loaded;
00442 }
00443 
00444 template<typename T>
00445 T locator::locate_in_cache(const std::vector<cache_item<T> >& cache) const
00446 {
00447     if(index_ == -1)
00448         return T();
00449 
00450     return cache[index_].item;
00451 }
00452 
00453 template<typename T>
00454 void locator::add_to_cache(std::vector<cache_item<T> >& cache, const T& item) const
00455 {
00456     if(index_ == -1)
00457         return;
00458 
00459     cache[index_] = cache_item<T>(item);
00460 }
00461 #endif
00462 
00463 manager::manager() {}
00464 
00465 manager::~manager()
00466 {
00467     flush_cache();
00468 }
00469 
00470 void set_wm_icon()
00471 {
00472 #if !(defined(__APPLE__))
00473     surface icon(get_image(game_config::game_icon,UNSCALED));
00474     if(icon != NULL) {
00475         ::SDL_WM_SetIcon(icon,NULL);
00476     }
00477 #endif
00478 }
00479 
00480 SDL_PixelFormat* pixel_format = NULL;
00481 
00482 void set_pixel_format(SDL_PixelFormat* format)
00483 {
00484     pixel_format = format;
00485     flush_cache();
00486 }
00487 
00488 void set_colour_adjustment(int r, int g, int b)
00489 {
00490     if(r != red_adjust || g != green_adjust || b != blue_adjust) {
00491         red_adjust = r;
00492         green_adjust = g;
00493         blue_adjust = b;
00494         reset_cache(scaled_to_hex_images_);
00495         reset_cache(brightened_images_);
00496         reset_cache(semi_brightened_images_);
00497         reversed_images_.clear();
00498     }
00499 }
00500 
00501 void set_team_colors(const std::vector<std::string>* colors)
00502 {
00503     if (colors == NULL)
00504         team_colors.clear();
00505     else {
00506         team_colors = *colors;
00507     }
00508 }
00509 
00510 void set_image_mask(const std::string& /*image*/)
00511 {
00512 
00513     // image_mask are blitted in display.cpp
00514     // so no need to flush the cache here
00515 /*
00516     if(image_mask != image) {
00517         image_mask = image;
00518         reset_cache(scaled_to_hex_images_);
00519         reset_cache(scaled_to_zoom_);
00520         reset_cache(brightened_images_);
00521         reset_cache(semi_brightened_images_);
00522         reversed_images_.clear();
00523     }
00524 */
00525 }
00526 
00527 void set_zoom(int amount)
00528 {
00529     if(amount != zoom) {
00530         zoom = amount;
00531         reset_cache(scaled_to_hex_images_);
00532         reset_cache(brightened_images_);
00533         reset_cache(semi_brightened_images_);
00534         reversed_images_.clear();
00535 
00536         // We keep these caches if:
00537         // we use default zoom (it doesn't need those)
00538         // or if they are already at the wanted zoom.
00539         if (zoom != tile_size && zoom != cached_zoom) {
00540             reset_cache(scaled_to_zoom_);
00541             reset_cache(unmasked_images_);
00542             cached_zoom = zoom;
00543         }
00544     }
00545 }
00546 
00547 static surface get_hexed(const locator i_locator)
00548 {
00549     // w.e don't want to add it to the unscaled cache,
00550     // since we will normaly never need the non-hexed one.
00551     surface image(get_image(i_locator, UNSCALED, false));
00552     // Re-cut scaled tiles according to a mask.
00553     const surface hex(get_image(game_config::terrain_mask_image,
00554                     UNSCALED));
00555     return mask_surface(image, hex);
00556 }
00557 
00558 static surface get_unmasked(const locator i_locator)
00559 {
00560     // If no scaling needed at this zoom level,
00561     // we just use the hexed image.
00562     surface image(get_image(i_locator, HEXED));
00563     if (zoom != tile_size)
00564         return scale_surface(image, zoom, zoom);
00565     else
00566         return image;
00567 }
00568 
00569 static surface get_scaled_to_hex(const locator i_locator)
00570 {
00571     surface res(get_image(i_locator, UNMASKED));
00572 
00573     // Adjusts colour if necessary.
00574     if (red_adjust != 0 ||
00575                 green_adjust != 0 || blue_adjust != 0) {
00576         res = surface(adjust_surface_colour(res,
00577                     red_adjust, green_adjust, blue_adjust));
00578     }
00579     /*
00580     const surface mask(get_image(image_mask,UNMASKED));
00581     if(mask != NULL) {
00582         SDL_SetAlpha(mask,SDL_SRCALPHA|SDL_RLEACCEL,SDL_ALPHA_OPAQUE);
00583         SDL_SetAlpha(res,SDL_SRCALPHA|SDL_RLEACCEL,SDL_ALPHA_OPAQUE);
00584 
00585         //commented out pending reply from SDL team about bug report
00586         //SDL_BlitSurface(mask,NULL,result,NULL);
00587     }*/
00588     return res;
00589 }
00590 
00591 static surface get_scaled_to_zoom(const locator i_locator)
00592 {
00593     assert(zoom != tile_size);
00594     assert(tile_size != 0);
00595 
00596     surface res(get_image(i_locator, UNSCALED));
00597     // For some reason haloes seems to have invalid images, protect against crashing
00598     if(!res.null()) {
00599         return scale_surface(res, ((res.get()->w * zoom) / tile_size), ((res.get()->h * zoom) / tile_size));
00600     } else {
00601         return surface(NULL);
00602     }
00603 }
00604 
00605 static surface get_brightened(const locator i_locator)
00606 {
00607     surface image(get_image(i_locator, SCALED_TO_HEX));
00608     return surface(brighten_image(image, ftofxp(1.5)));
00609 }
00610 
00611 static surface get_semi_brightened(const locator i_locator)
00612 {
00613     surface image(get_image(i_locator, SCALED_TO_HEX));
00614     return surface(brighten_image(image, ftofxp(1.25)));
00615 }
00616 
00617 surface get_image(const image::locator& i_locator, TYPE type, bool add_to_cache )
00618 {
00619     surface res(NULL);
00620     image_cache *imap;
00621 
00622     if(i_locator.is_void())
00623         return surface(NULL);
00624 
00625     bool is_unscaled = false;
00626 
00627     switch(type) {
00628     case UNSCALED:
00629         is_unscaled = true;
00630         imap = &images_;
00631         break;
00632     case SCALED_TO_HEX:
00633         imap = &scaled_to_hex_images_;
00634         break;
00635     case SCALED_TO_ZOOM:
00636         // Only use separate cache if scaled
00637         if(zoom != tile_size) {
00638             imap = &scaled_to_zoom_;
00639         } else {
00640             is_unscaled = true;
00641             imap = &images_;
00642         }
00643         break;
00644     case HEXED:
00645         imap = &hexed_images_;
00646         break;
00647     case UNMASKED:
00648         // Only use separate cache if scaled
00649         if(zoom != tile_size) {
00650             imap = &unmasked_images_;
00651         } else {
00652             imap = &hexed_images_;
00653         }
00654         break;
00655     case BRIGHTENED:
00656         imap = &brightened_images_;
00657         break;
00658     case SEMI_BRIGHTENED:
00659         imap = &semi_brightened_images_;
00660         break;
00661     default:
00662         return surface(NULL);
00663     }
00664 
00665     if(i_locator.in_cache(*imap))
00666         return i_locator.locate_in_cache(*imap);
00667 
00668     // If type is unscaled, directly load the image from the disk.
00669     // Else, create it from the unscaled image.
00670     if(is_unscaled) {
00671         res = i_locator.load_from_disk();
00672 
00673         if(res == NULL) {
00674             if(add_to_cache) i_locator.add_to_cache(*imap, surface(NULL));
00675             return surface(NULL);
00676         }
00677     } else {
00678 
00679         // surface base_image(get_image(i_locator, UNSCALED));
00680 
00681         switch(type) {
00682         case SCALED_TO_HEX:
00683             res = get_scaled_to_hex(i_locator);
00684             break;
00685         case SCALED_TO_ZOOM:
00686             res = get_scaled_to_zoom(i_locator);
00687             break;
00688         case HEXED:
00689             res = get_hexed(i_locator);
00690             break;
00691         case UNMASKED:
00692             res = get_unmasked(i_locator);
00693             break;
00694         case BRIGHTENED:
00695             res = get_brightened(i_locator);
00696             break;
00697         case SEMI_BRIGHTENED:
00698             res = get_semi_brightened(i_locator);
00699             break;
00700         default:
00701             return surface(NULL);
00702         }
00703     }
00704 
00705     // Optimizes surface before storing it
00706     res = create_optimized_surface(res);
00707     if(add_to_cache) i_locator.add_to_cache(*imap, res);
00708     return res;
00709 }
00710 
00711 surface reverse_image(const surface& surf)
00712 {
00713     if(surf == NULL) {
00714         return surface(NULL);
00715     }
00716 
00717     const std::map<surface,surface>::iterator itor = reversed_images_.find(surf);
00718     if(itor != reversed_images_.end()) {
00719         // sdl_add_ref(itor->second);
00720         return itor->second;
00721     }
00722 
00723     const surface rev(flip_surface(surf));
00724     if(rev == NULL) {
00725         return surface(NULL);
00726     }
00727 
00728     reversed_images_.insert(std::pair<surface,surface>(surf,rev));
00729     // sdl_add_ref(rev);
00730     return rev;
00731 }
00732 
00733 bool exists(const image::locator& i_locator)
00734 {
00735     typedef image::locator loc;
00736     loc::type type = i_locator.get_type();
00737     if (type != loc::FILE && type != loc::SUB_FILE)
00738         return false;
00739 
00740     // The insertion will fail if there is already an element in the cache
00741     std::pair< std::map< image::locator, bool >::iterator, bool >
00742         it = image_existance_map.insert(std::make_pair(i_locator, false));
00743     bool &cache = it.first->second;
00744     if (it.second)
00745         cache = !get_binary_file_location("images", i_locator.get_filename()).empty();
00746     return cache;
00747 }
00748 
00749 
00750 } // end namespace image
00751 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs