spritecache.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "fileio_func.h"
00014 #include "spriteloader/grf.hpp"
00015 #include "gfx_func.h"
00016 #include "error.h"
00017 #include "zoom_func.h"
00018 #include "settings_type.h"
00019 #include "blitter/factory.hpp"
00020 #include "core/math_func.hpp"
00021 #include "core/mem_func.hpp"
00022 
00023 #include "table/sprites.h"
00024 #include "table/strings.h"
00025 #include "table/palette_convert.h"
00026 
00027 /* Default of 4MB spritecache */
00028 uint _sprite_cache_size = 4;
00029 
00030 typedef SimpleTinyEnumT<SpriteType, byte> SpriteTypeByte;
00031 
00032 struct SpriteCache {
00033   void *ptr;
00034   size_t file_pos;
00035   uint32 id;
00036   uint16 file_slot;
00037   int16 lru;
00038   SpriteTypeByte type; 
00039   bool warned;         
00040   byte container_ver;  
00041 };
00042 
00043 
00044 static uint _spritecache_items = 0;
00045 static SpriteCache *_spritecache = NULL;
00046 
00047 
00048 static inline SpriteCache *GetSpriteCache(uint index)
00049 {
00050   return &_spritecache[index];
00051 }
00052 
00053 static inline bool IsMapgenSpriteID(SpriteID sprite)
00054 {
00055   return IsInsideMM(sprite, 4845, 4882);
00056 }
00057 
00058 static SpriteCache *AllocateSpriteCache(uint index)
00059 {
00060   if (index >= _spritecache_items) {
00061     /* Add another 1024 items to the 'pool' */
00062     uint items = Align(index + 1, 1024);
00063 
00064     DEBUG(sprite, 4, "Increasing sprite cache to %u items (" PRINTF_SIZE " bytes)", items, items * sizeof(*_spritecache));
00065 
00066     _spritecache = ReallocT(_spritecache, items);
00067 
00068     /* Reset the new items and update the count */
00069     memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) * sizeof(*_spritecache));
00070     _spritecache_items = items;
00071   }
00072 
00073   return GetSpriteCache(index);
00074 }
00075 
00076 
00077 struct MemBlock {
00078   size_t size;
00079   byte data[];
00080 };
00081 
00082 static uint _sprite_lru_counter;
00083 static MemBlock *_spritecache_ptr;
00084 static uint _allocated_sprite_cache_size = 0;
00085 static int _compact_cache_counter;
00086 
00087 static void CompactSpriteCache();
00088 static void *AllocSprite(size_t mem_req);
00089 
00096 bool SkipSpriteData(byte type, uint16 num)
00097 {
00098   if (type & 2) {
00099     FioSkipBytes(num);
00100   } else {
00101     while (num > 0) {
00102       int8 i = FioReadByte();
00103       if (i >= 0) {
00104         int size = (i == 0) ? 0x80 : i;
00105         if (size > num) return false;
00106         num -= size;
00107         FioSkipBytes(size);
00108       } else {
00109         i = -(i >> 3);
00110         num -= i;
00111         FioReadByte();
00112       }
00113     }
00114   }
00115   return true;
00116 }
00117 
00118 /* Check if the given Sprite ID exists */
00119 bool SpriteExists(SpriteID id)
00120 {
00121   /* Special case for Sprite ID zero -- its position is also 0... */
00122   if (id == 0) return true;
00123   if (id >= _spritecache_items) return false;
00124   return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file_slot == 0);
00125 }
00126 
00132 SpriteType GetSpriteType(SpriteID sprite)
00133 {
00134   if (!SpriteExists(sprite)) return ST_INVALID;
00135   return GetSpriteCache(sprite)->type;
00136 }
00137 
00143 uint GetOriginFileSlot(SpriteID sprite)
00144 {
00145   if (!SpriteExists(sprite)) return 0;
00146   return GetSpriteCache(sprite)->file_slot;
00147 }
00148 
00157 uint GetMaxSpriteID()
00158 {
00159   return _spritecache_items;
00160 }
00161 
00162 static bool ResizeSpriteIn(SpriteLoader::Sprite *sprite, ZoomLevel src, ZoomLevel tgt)
00163 {
00164   uint8 scaled_1 = UnScaleByZoom(1, (ZoomLevel)(tgt - src));
00165 
00166   /* Check for possible memory overflow. */
00167   if (sprite[src].width * scaled_1 > UINT16_MAX || sprite[src].height * scaled_1 > UINT16_MAX) return false;
00168 
00169   sprite[tgt].width  = sprite[src].width  * scaled_1;
00170   sprite[tgt].height = sprite[src].height * scaled_1;
00171   sprite[tgt].x_offs = sprite[src].x_offs * scaled_1;
00172   sprite[tgt].y_offs = sprite[src].y_offs * scaled_1;
00173 
00174   sprite[tgt].AllocateData(tgt, sprite[tgt].width * sprite[tgt].height);
00175 
00176   SpriteLoader::CommonPixel *dst = sprite[tgt].data;
00177   for (int y = 0; y < sprite[tgt].height; y++) {
00178     const SpriteLoader::CommonPixel *src_ln = &sprite[src].data[y / scaled_1 * sprite[src].width];
00179     for (int x = 0; x < sprite[tgt].width; x++) {
00180       *dst = src_ln[x / scaled_1];
00181       dst++;
00182     }
00183   }
00184 
00185   return true;
00186 }
00187 
00188 static void ResizeSpriteOut(SpriteLoader::Sprite *sprite, ZoomLevel zoom)
00189 {
00190   /* Algorithm based on 32bpp_Optimized::ResizeSprite() */
00191   sprite[zoom].width  = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width,  zoom);
00192   sprite[zoom].height = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom);
00193   sprite[zoom].x_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom);
00194   sprite[zoom].y_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom);
00195 
00196   sprite[zoom].AllocateData(zoom, sprite[zoom].height * sprite[zoom].width);
00197 
00198   SpriteLoader::CommonPixel *dst = sprite[zoom].data;
00199   const SpriteLoader::CommonPixel *src = sprite[zoom - 1].data;
00200   const SpriteLoader::CommonPixel *src_end = src + sprite[zoom - 1].height * sprite[zoom - 1].width;
00201 
00202   for (uint y = 0; y < sprite[zoom].height; y++) {
00203     const SpriteLoader::CommonPixel *src_ln = src + sprite[zoom - 1].width;
00204     assert(src_ln <= src_end);
00205     for (uint x = 0; x < sprite[zoom].width; x++) {
00206       assert(src < src_ln);
00207       if (src + 1 != src_ln && (src + 1)->a != 0) {
00208         *dst = *(src + 1);
00209       } else {
00210         *dst = *src;
00211       }
00212       dst++;
00213       src += 2;
00214     }
00215     src = src_ln + sprite[zoom - 1].width;
00216   }
00217 }
00218 
00219 static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint pad_left, uint pad_top, uint pad_right, uint pad_bottom)
00220 {
00221   uint width  = sprite->width + pad_left + pad_right;
00222   uint height = sprite->height + pad_top + pad_bottom;
00223 
00224   if (width > UINT16_MAX || height > UINT16_MAX) return false;
00225 
00226   /* Copy source data and reallocate sprite memory. */
00227   SpriteLoader::CommonPixel *src_data = MallocT<SpriteLoader::CommonPixel>(sprite->width * sprite->height);
00228   MemCpyT(src_data, sprite->data, sprite->width * sprite->height);
00229   sprite->AllocateData(zoom, width * height);
00230 
00231   /* Copy with padding to destination. */
00232   SpriteLoader::CommonPixel *src = src_data;
00233   SpriteLoader::CommonPixel *data = sprite->data;
00234   for (uint y = 0; y < height; y++) {
00235     if (y < pad_top || pad_bottom + y >= height) {
00236       /* Top/bottom padding. */
00237       MemSetT(data, 0, width);
00238       data += width;
00239     } else {
00240       if (pad_left > 0) {
00241         /* Pad left. */
00242         MemSetT(data, 0, pad_left);
00243         data += pad_left;
00244       }
00245 
00246       /* Copy pixels. */
00247       MemCpyT(data, src, sprite->width);
00248       src += sprite->width;
00249       data += sprite->width;
00250 
00251       if (pad_right > 0) {
00252         /* Pad right. */
00253         MemSetT(data, 0, pad_right);
00254         data += pad_right;
00255       }
00256     }
00257   }
00258   free(src_data);
00259 
00260   /* Update sprite size. */
00261   sprite->width   = width;
00262   sprite->height  = height;
00263   sprite->x_offs -= pad_left;
00264   sprite->y_offs -= pad_top;
00265 
00266   return true;
00267 }
00268 
00269 static bool PadSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail)
00270 {
00271   /* Get minimum top left corner coordinates. */
00272   int min_xoffs = INT32_MAX;
00273   int min_yoffs = INT32_MAX;
00274   for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
00275     if (HasBit(sprite_avail, zoom)) {
00276       min_xoffs = min(min_xoffs, ScaleByZoom(sprite[zoom].x_offs, zoom));
00277       min_yoffs = min(min_yoffs, ScaleByZoom(sprite[zoom].y_offs, zoom));
00278     }
00279   }
00280 
00281   /* Get maximum dimensions taking necessary padding at the top left into account. */
00282   int max_width  = INT32_MIN;
00283   int max_height = INT32_MIN;
00284   for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
00285     if (HasBit(sprite_avail, zoom)) {
00286       max_width  = max(max_width, ScaleByZoom(sprite[zoom].width + sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom), zoom));
00287       max_height = max(max_height, ScaleByZoom(sprite[zoom].height + sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom), zoom));
00288     }
00289   }
00290 
00291   /* Pad sprites where needed. */
00292   for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
00293     if (HasBit(sprite_avail, zoom)) {
00294       /* Scaling the sprite dimensions in the blitter is done with rounding up,
00295        * so a negative padding here is not an error. */
00296       int pad_left   = max(0, sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom));
00297       int pad_top    = max(0, sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom));
00298       int pad_right  = max(0, UnScaleByZoom(max_width, zoom) - sprite[zoom].width - pad_left);
00299       int pad_bottom = max(0, UnScaleByZoom(max_height, zoom) - sprite[zoom].height - pad_top);
00300 
00301       if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0) {
00302         if (!PadSingleSprite(&sprite[zoom], zoom, pad_left, pad_top, pad_right, pad_bottom)) return false;
00303       }
00304     }
00305   }
00306 
00307   return true;
00308 }
00309 
00310 static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos)
00311 {
00312   /* Create a fully zoomed image if it does not exist */
00313   ZoomLevel first_avail = static_cast<ZoomLevel>(FIND_FIRST_BIT(sprite_avail));
00314   if (first_avail != ZOOM_LVL_NORMAL) {
00315     if (!ResizeSpriteIn(sprite, first_avail, ZOOM_LVL_NORMAL)) return false;
00316     SetBit(sprite_avail, ZOOM_LVL_NORMAL);
00317   }
00318 
00319   /* Pad sprites to make sizes match. */
00320   if (!PadSprites(sprite, sprite_avail)) return false;
00321 
00322   /* Create other missing zoom levels */
00323   for (ZoomLevel zoom = ZOOM_LVL_OUT_2X; zoom != ZOOM_LVL_END; zoom++) {
00324     if (HasBit(sprite_avail, zoom)) {
00325       /* Check that size and offsets match the fully zoomed image. */
00326       assert(sprite[zoom].width  == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width,  zoom));
00327       assert(sprite[zoom].height == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom));
00328       assert(sprite[zoom].x_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom));
00329       assert(sprite[zoom].y_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom));
00330     }
00331 
00332     /* Zoom level is not available, or unusable, so create it */
00333     if (!HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom);
00334   }
00335 
00336   return  true;
00337 }
00338 
00345 static void *ReadRecolourSprite(uint16 file_slot, uint num)
00346 {
00347   /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
00348    * number of recolour sprites that are 17 bytes that only exist in DOS
00349    * GRFs which are the same as 257 byte recolour sprites, but with the last
00350    * 240 bytes zeroed.  */
00351   static const uint RECOLOUR_SPRITE_SIZE = 257;
00352   byte *dest = (byte *)AllocSprite(max(RECOLOUR_SPRITE_SIZE, num));
00353 
00354   if (_palette_remap_grf[file_slot]) {
00355     byte *dest_tmp = AllocaM(byte, max(RECOLOUR_SPRITE_SIZE, num));
00356 
00357     /* Only a few recolour sprites are less than 257 bytes */
00358     if (num < RECOLOUR_SPRITE_SIZE) memset(dest_tmp, 0, RECOLOUR_SPRITE_SIZE);
00359     FioReadBlock(dest_tmp, num);
00360 
00361     /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
00362     for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) {
00363       dest[i] = _palmap_w2d[dest_tmp[_palmap_d2w[i - 1] + 1]];
00364     }
00365   } else {
00366     FioReadBlock(dest, num);
00367   }
00368 
00369   return dest;
00370 }
00371 
00380 static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_type, AllocatorProc *allocator)
00381 {
00382   uint8 file_slot = sc->file_slot;
00383   size_t file_pos = sc->file_pos;
00384 
00385   assert(sprite_type != ST_RECOLOUR);
00386   assert(IsMapgenSpriteID(id) == (sprite_type == ST_MAPGEN));
00387   assert(sc->type == sprite_type);
00388 
00389   DEBUG(sprite, 9, "Load sprite %d", id);
00390 
00391   SpriteLoader::Sprite sprite[ZOOM_LVL_COUNT];
00392   uint8 sprite_avail = 0;
00393   sprite[ZOOM_LVL_NORMAL].type = sprite_type;
00394 
00395   SpriteLoaderGrf sprite_loader(sc->container_ver);
00396   if (sprite_type != ST_MAPGEN && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 32) {
00397     /* Try for 32bpp sprites first. */
00398     sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, true);
00399   }
00400   if (sprite_avail == 0) {
00401     sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, false);
00402   }
00403 
00404   if (sprite_avail == 0) {
00405     if (sprite_type == ST_MAPGEN) return NULL;
00406     if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
00407     return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
00408   }
00409 
00410   if (sprite_type == ST_MAPGEN) {
00411     /* Ugly hack to work around the problem that the old landscape
00412      *  generator assumes that those sprites are stored uncompressed in
00413      *  the memory, and they are only read directly by the code, never
00414      *  send to the blitter. So do not send it to the blitter (which will
00415      *  result in a data array in the format the blitter likes most), but
00416      *  extract the data directly and store that as sprite.
00417      * Ugly: yes. Other solution: no. Blame the original author or
00418      *  something ;) The image should really have been a data-stream
00419      *  (so type = 0xFF basically). */
00420     uint num = sprite[ZOOM_LVL_NORMAL].width * sprite[ZOOM_LVL_NORMAL].height;
00421 
00422     Sprite *s = (Sprite *)allocator(sizeof(*s) + num);
00423     s->width  = sprite[ZOOM_LVL_NORMAL].width;
00424     s->height = sprite[ZOOM_LVL_NORMAL].height;
00425     s->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
00426     s->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
00427 
00428     SpriteLoader::CommonPixel *src = sprite[ZOOM_LVL_NORMAL].data;
00429     byte *dest = s->data;
00430     while (num-- > 0) {
00431       *dest++ = src->m;
00432       src++;
00433     }
00434 
00435     return s;
00436   }
00437 
00438   if (sprite_type == ST_NORMAL) {
00439     if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
00440       if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
00441       return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
00442     }
00443   }
00444   return BlitterFactoryBase::GetCurrentBlitter()->Encode(sprite, allocator);
00445 }
00446 
00447 
00449 static std::map<uint32, size_t> _grf_sprite_offsets;
00450 
00456 size_t GetGRFSpriteOffset(uint32 id)
00457 {
00458   return _grf_sprite_offsets.find(id) != _grf_sprite_offsets.end() ? _grf_sprite_offsets[id] : SIZE_MAX;
00459 }
00460 
00465 void ReadGRFSpriteOffsets(byte container_version)
00466 {
00467   _grf_sprite_offsets.clear();
00468 
00469   if (container_version >= 2) {
00470     /* Seek to sprite section of the GRF. */
00471     size_t data_offset = FioReadDword();
00472     size_t old_pos = FioGetPos();
00473     FioSeekTo(data_offset, SEEK_CUR);
00474 
00475     /* Loop over all sprite section entries and store the file
00476      * offset for each newly encountered ID. */
00477     uint32 id, prev_id = 0;
00478     while ((id = FioReadDword()) != 0) {
00479       if (id != prev_id) _grf_sprite_offsets[id] = FioGetPos() - 4;
00480       prev_id = id;
00481       FioSkipBytes(FioReadDword());
00482     }
00483 
00484     /* Continue processing the data section. */
00485     FioSeekTo(old_pos, SEEK_SET);
00486   }
00487 }
00488 
00489 
00498 bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte container_version)
00499 {
00500   size_t file_pos = FioGetPos();
00501 
00502   /* Read sprite header. */
00503   uint32 num = container_version >= 2 ? FioReadDword() : FioReadWord();
00504   if (num == 0) return false;
00505   byte grf_type = FioReadByte();
00506 
00507   SpriteType type;
00508   void *data = NULL;
00509   if (grf_type == 0xFF) {
00510     /* Some NewGRF files have "empty" pseudo-sprites which are 1
00511      * byte long. Catch these so the sprites won't be displayed. */
00512     if (num == 1) {
00513       FioReadByte();
00514       return false;
00515     }
00516     type = ST_RECOLOUR;
00517     data = ReadRecolourSprite(file_slot, num);
00518   } else if (container_version >= 2 && grf_type == 0xFD) {
00519     if (num != 4) {
00520       /* Invalid sprite section include, ignore. */
00521       FioSkipBytes(num);
00522       return false;
00523     }
00524     /* It is not an error if no sprite with the provided ID is found in the sprite section. */
00525     file_pos = GetGRFSpriteOffset(FioReadDword());
00526     type = ST_NORMAL;
00527   } else {
00528     FioSkipBytes(7);
00529     type = SkipSpriteData(grf_type, num - 8) ? ST_NORMAL : ST_INVALID;
00530     /* Inline sprites are not supported for container version >= 2. */
00531     if (container_version >= 2) return false;
00532   }
00533 
00534   if (type == ST_INVALID) return false;
00535 
00536   if (load_index >= MAX_SPRITES) {
00537     usererror("Tried to load too many sprites (#%d; max %d)", load_index, MAX_SPRITES);
00538   }
00539 
00540   bool is_mapgen = IsMapgenSpriteID(load_index);
00541 
00542   if (is_mapgen) {
00543     if (type != ST_NORMAL) usererror("Uhm, would you be so kind not to load a NewGRF that changes the type of the map generator sprites?");
00544     type = ST_MAPGEN;
00545   }
00546 
00547   SpriteCache *sc = AllocateSpriteCache(load_index);
00548   sc->file_slot = file_slot;
00549   sc->file_pos = file_pos;
00550   sc->ptr = data;
00551   sc->lru = 0;
00552   sc->id = file_sprite_id;
00553   sc->type = type;
00554   sc->warned = false;
00555   sc->container_ver = container_version;
00556 
00557   return true;
00558 }
00559 
00560 
00561 void DupSprite(SpriteID old_spr, SpriteID new_spr)
00562 {
00563   SpriteCache *scnew = AllocateSpriteCache(new_spr); // may reallocate: so put it first
00564   SpriteCache *scold = GetSpriteCache(old_spr);
00565 
00566   scnew->file_slot = scold->file_slot;
00567   scnew->file_pos = scold->file_pos;
00568   scnew->ptr = NULL;
00569   scnew->id = scold->id;
00570   scnew->type = scold->type;
00571   scnew->warned = false;
00572   scnew->container_ver = scold->container_ver;
00573 }
00574 
00581 static const size_t S_FREE_MASK = sizeof(size_t) - 1;
00582 
00583 /* to make sure nobody adds things to MemBlock without checking S_FREE_MASK first */
00584 assert_compile(sizeof(MemBlock) == sizeof(size_t));
00585 /* make sure it's a power of two */
00586 assert_compile((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
00587 
00588 static inline MemBlock *NextBlock(MemBlock *block)
00589 {
00590   return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK));
00591 }
00592 
00593 static size_t GetSpriteCacheUsage()
00594 {
00595   size_t tot_size = 0;
00596   MemBlock *s;
00597 
00598   for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
00599     if (!(s->size & S_FREE_MASK)) tot_size += s->size;
00600   }
00601 
00602   return tot_size;
00603 }
00604 
00605 
00606 void IncreaseSpriteLRU()
00607 {
00608   /* Increase all LRU values */
00609   if (_sprite_lru_counter > 16384) {
00610     SpriteID i;
00611 
00612     DEBUG(sprite, 3, "Fixing lru %u, inuse=" PRINTF_SIZE, _sprite_lru_counter, GetSpriteCacheUsage());
00613 
00614     for (i = 0; i != _spritecache_items; i++) {
00615       SpriteCache *sc = GetSpriteCache(i);
00616       if (sc->ptr != NULL) {
00617         if (sc->lru >= 0) {
00618           sc->lru = -1;
00619         } else if (sc->lru != -32768) {
00620           sc->lru--;
00621         }
00622       }
00623     }
00624     _sprite_lru_counter = 0;
00625   }
00626 
00627   /* Compact sprite cache every now and then. */
00628   if (++_compact_cache_counter >= 740) {
00629     CompactSpriteCache();
00630     _compact_cache_counter = 0;
00631   }
00632 }
00633 
00638 static void CompactSpriteCache()
00639 {
00640   MemBlock *s;
00641 
00642   DEBUG(sprite, 3, "Compacting sprite cache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
00643 
00644   for (s = _spritecache_ptr; s->size != 0;) {
00645     if (s->size & S_FREE_MASK) {
00646       MemBlock *next = NextBlock(s);
00647       MemBlock temp;
00648       SpriteID i;
00649 
00650       /* Since free blocks are automatically coalesced, this should hold true. */
00651       assert(!(next->size & S_FREE_MASK));
00652 
00653       /* If the next block is the sentinel block, we can safely return */
00654       if (next->size == 0) break;
00655 
00656       /* Locate the sprite belonging to the next pointer. */
00657       for (i = 0; GetSpriteCache(i)->ptr != next->data; i++) {
00658         assert(i != _spritecache_items);
00659       }
00660 
00661       GetSpriteCache(i)->ptr = s->data; // Adjust sprite array entry
00662       /* Swap this and the next block */
00663       temp = *s;
00664       memmove(s, next, next->size);
00665       s = NextBlock(s);
00666       *s = temp;
00667 
00668       /* Coalesce free blocks */
00669       while (NextBlock(s)->size & S_FREE_MASK) {
00670         s->size += NextBlock(s)->size & ~S_FREE_MASK;
00671       }
00672     } else {
00673       s = NextBlock(s);
00674     }
00675   }
00676 }
00677 
00682 static void DeleteEntryFromSpriteCache(uint item)
00683 {
00684   /* Mark the block as free (the block must be in use) */
00685   MemBlock *s = (MemBlock*)GetSpriteCache(item)->ptr - 1;
00686   assert(!(s->size & S_FREE_MASK));
00687   s->size |= S_FREE_MASK;
00688   GetSpriteCache(item)->ptr = NULL;
00689 
00690   /* And coalesce adjacent free blocks */
00691   for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
00692     if (s->size & S_FREE_MASK) {
00693       while (NextBlock(s)->size & S_FREE_MASK) {
00694         s->size += NextBlock(s)->size & ~S_FREE_MASK;
00695       }
00696     }
00697   }
00698 }
00699 
00700 static void DeleteEntryFromSpriteCache()
00701 {
00702   uint best = UINT_MAX;
00703   int cur_lru;
00704 
00705   DEBUG(sprite, 3, "DeleteEntryFromSpriteCache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
00706 
00707   cur_lru = 0xffff;
00708   for (SpriteID i = 0; i != _spritecache_items; i++) {
00709     SpriteCache *sc = GetSpriteCache(i);
00710     if (sc->type != ST_RECOLOUR && sc->ptr != NULL && sc->lru < cur_lru) {
00711       cur_lru = sc->lru;
00712       best = i;
00713     }
00714   }
00715 
00716   /* Display an error message and die, in case we found no sprite at all.
00717    * This shouldn't really happen, unless all sprites are locked. */
00718   if (best == UINT_MAX) error("Out of sprite memory");
00719 
00720   DeleteEntryFromSpriteCache(best);
00721 }
00722 
00723 static void *AllocSprite(size_t mem_req)
00724 {
00725   mem_req += sizeof(MemBlock);
00726 
00727   /* Align this to correct boundary. This also makes sure at least one
00728    * bit is not used, so we can use it for other things. */
00729   mem_req = Align(mem_req, S_FREE_MASK + 1);
00730 
00731   for (;;) {
00732     MemBlock *s;
00733 
00734     for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
00735       if (s->size & S_FREE_MASK) {
00736         size_t cur_size = s->size & ~S_FREE_MASK;
00737 
00738         /* Is the block exactly the size we need or
00739          * big enough for an additional free block? */
00740         if (cur_size == mem_req ||
00741             cur_size >= mem_req + sizeof(MemBlock)) {
00742           /* Set size and in use */
00743           s->size = mem_req;
00744 
00745           /* Do we need to inject a free block too? */
00746           if (cur_size != mem_req) {
00747             NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK;
00748           }
00749 
00750           return s->data;
00751         }
00752       }
00753     }
00754 
00755     /* Reached sentinel, but no block found yet. Delete some old entry. */
00756     DeleteEntryFromSpriteCache();
00757   }
00758 }
00759 
00769 static void *HandleInvalidSpriteRequest(SpriteID sprite, SpriteType requested, SpriteCache *sc, AllocatorProc *allocator)
00770 {
00771   static const char * const sprite_types[] = {
00772     "normal",        // ST_NORMAL
00773     "map generator", // ST_MAPGEN
00774     "character",     // ST_FONT
00775     "recolour",      // ST_RECOLOUR
00776   };
00777 
00778   SpriteType available = sc->type;
00779   if (requested == ST_FONT && available == ST_NORMAL) {
00780     if (sc->ptr == NULL) sc->type = ST_FONT;
00781     return GetRawSprite(sprite, sc->type, allocator);
00782   }
00783 
00784   byte warning_level = sc->warned ? 6 : 0;
00785   sc->warned = true;
00786   DEBUG(sprite, warning_level, "Tried to load %s sprite #%d as a %s sprite. Probable cause: NewGRF interference", sprite_types[available], sprite, sprite_types[requested]);
00787 
00788   switch (requested) {
00789     case ST_NORMAL:
00790       if (sprite == SPR_IMG_QUERY) usererror("Uhm, would you be so kind not to load a NewGRF that makes the 'query' sprite a non-normal sprite?");
00791       /* FALL THROUGH */
00792     case ST_FONT:
00793       return GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
00794     case ST_RECOLOUR:
00795       if (sprite == PALETTE_TO_DARK_BLUE) usererror("Uhm, would you be so kind not to load a NewGRF that makes the 'PALETTE_TO_DARK_BLUE' sprite a non-remap sprite?");
00796       return GetRawSprite(PALETTE_TO_DARK_BLUE, ST_RECOLOUR, allocator);
00797     case ST_MAPGEN:
00798       /* this shouldn't happen, overriding of ST_MAPGEN sprites is checked in LoadNextSprite()
00799        * (the only case the check fails is when these sprites weren't even loaded...) */
00800     default:
00801       NOT_REACHED();
00802   }
00803 }
00804 
00813 void *GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator)
00814 {
00815   assert(type != ST_MAPGEN || IsMapgenSpriteID(sprite));
00816   assert(type < ST_INVALID);
00817 
00818   if (!SpriteExists(sprite)) {
00819     DEBUG(sprite, 1, "Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs", sprite);
00820 
00821     /* SPR_IMG_QUERY is a BIG FAT RED ? */
00822     sprite = SPR_IMG_QUERY;
00823   }
00824 
00825   SpriteCache *sc = GetSpriteCache(sprite);
00826 
00827   if (sc->type != type) return HandleInvalidSpriteRequest(sprite, type, sc, allocator);
00828 
00829   if (allocator == NULL) {
00830     /* Load sprite into/from spritecache */
00831 
00832     /* Update LRU */
00833     sc->lru = ++_sprite_lru_counter;
00834 
00835     /* Load the sprite, if it is not loaded, yet */
00836     if (sc->ptr == NULL) sc->ptr = ReadSprite(sc, sprite, type, AllocSprite);
00837 
00838     return sc->ptr;
00839   } else {
00840     /* Do not use the spritecache, but a different allocator. */
00841     return ReadSprite(sc, sprite, type, allocator);
00842   }
00843 }
00844 
00845 
00846 static void GfxInitSpriteCache()
00847 {
00848   /* initialize sprite cache heap */
00849   int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth();
00850   uint target_size = (bpp > 0 ? _sprite_cache_size * bpp / 8 : 1) * 1024 * 1024;
00851 
00852   /* Remember 'target_size' from the previous allocation attempt, so we do not try to reach the target_size multiple times in case of failure. */
00853   static uint last_alloc_attempt = 0;
00854 
00855   if (_spritecache_ptr == NULL || (_allocated_sprite_cache_size != target_size && target_size != last_alloc_attempt)) {
00856     delete[] reinterpret_cast<byte *>(_spritecache_ptr);
00857 
00858     last_alloc_attempt = target_size;
00859     _allocated_sprite_cache_size = target_size;
00860 
00861     do {
00862       try {
00863         /* Try to allocate 50% more to make sure we do not allocate almost all available. */
00864         _spritecache_ptr = reinterpret_cast<MemBlock *>(new byte[_allocated_sprite_cache_size + _allocated_sprite_cache_size / 2]);
00865       } catch (std::bad_alloc &oom) {
00866         _spritecache_ptr = NULL;
00867       }
00868 
00869       if (_spritecache_ptr != NULL) {
00870         /* Allocation succeeded, but we wanted less. */
00871         delete[] reinterpret_cast<byte *>(_spritecache_ptr);
00872         _spritecache_ptr = reinterpret_cast<MemBlock *>(new byte[_allocated_sprite_cache_size]);
00873       } else if (_allocated_sprite_cache_size < 2 * 1024 * 1024) {
00874         usererror("Cannot allocate spritecache");
00875       } else {
00876         /* Try again to allocate half. */
00877         _allocated_sprite_cache_size >>= 1;
00878       }
00879     } while (_spritecache_ptr == NULL);
00880 
00881     if (_allocated_sprite_cache_size != target_size) {
00882       DEBUG(misc, 0, "Not enough memory to allocate %d MiB of spritecache. Spritecache was reduced to %d MiB.", target_size / 1024 / 1024, _allocated_sprite_cache_size / 1024 / 1024);
00883 
00884       ErrorMessageData msg(STR_CONFIG_ERROR_OUT_OF_MEMORY, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG);
00885       msg.SetDParam(0, target_size);
00886       msg.SetDParam(1, _allocated_sprite_cache_size);
00887       ScheduleErrorMessage(msg);
00888     }
00889   }
00890 
00891   /* A big free block */
00892   _spritecache_ptr->size = (_allocated_sprite_cache_size - sizeof(MemBlock)) | S_FREE_MASK;
00893   /* Sentinel block (identified by size == 0) */
00894   NextBlock(_spritecache_ptr)->size = 0;
00895 }
00896 
00897 void GfxInitSpriteMem()
00898 {
00899   GfxInitSpriteCache();
00900 
00901   /* Reset the spritecache 'pool' */
00902   free(_spritecache);
00903   _spritecache_items = 0;
00904   _spritecache = NULL;
00905 
00906   _compact_cache_counter = 0;
00907 }
00908 
00913 void GfxClearSpriteCache()
00914 {
00915   /* Clear sprite ptr for all cached items */
00916   for (uint i = 0; i != _spritecache_items; i++) {
00917     SpriteCache *sc = GetSpriteCache(i);
00918     if (sc->type != ST_RECOLOUR && sc->ptr != NULL) DeleteEntryFromSpriteCache(i);
00919   }
00920 }
00921 
00922 /* static */ ReusableBuffer<SpriteLoader::CommonPixel> SpriteLoader::Sprite::buffer[ZOOM_LVL_COUNT];