summaryrefslogtreecommitdiff
path: root/src/spritecache.cpp
diff options
context:
space:
mode:
authormichi_cc <michi_cc@openttd.org>2012-02-04 13:29:13 +0000
committermichi_cc <michi_cc@openttd.org>2012-02-04 13:29:13 +0000
commite85d5b5d316b888c6013e1d590d88e0da201a1e6 (patch)
treebbfcd0df9d8985bb51aca4ef94fa92eeeda62c49 /src/spritecache.cpp
parent2909e39e99aac74b124aee113dd4fa3f430d9d6c (diff)
downloadopenttd-e85d5b5d316b888c6013e1d590d88e0da201a1e6.tar.xz
(svn r23889) -Codechange: Centralise sprite resizing in one place. (peter1138)
Diffstat (limited to 'src/spritecache.cpp')
-rw-r--r--src/spritecache.cpp215
1 files changed, 200 insertions, 15 deletions
diff --git a/src/spritecache.cpp b/src/spritecache.cpp
index 050612cbb..83c6a15ae 100644
--- a/src/spritecache.cpp
+++ b/src/spritecache.cpp
@@ -13,11 +13,14 @@
#include "fileio_func.h"
#include "spriteloader/grf.hpp"
#include "gfx_func.h"
+#include "zoom_func.h"
+#include "settings_type.h"
#ifdef WITH_PNG
#include "spriteloader/png.hpp"
#endif /* WITH_PNG */
#include "blitter/factory.hpp"
#include "core/math_func.hpp"
+#include "core/mem_func.hpp"
#include "table/sprites.h"
#include "table/palette_convert.h"
@@ -156,6 +159,177 @@ uint GetMaxSpriteID()
return _spritecache_items;
}
+static bool ResizeSpriteIn(SpriteLoader::Sprite *sprite, ZoomLevel src, ZoomLevel tgt)
+{
+ uint8 scaled_1 = UnScaleByZoom(1, (ZoomLevel)(tgt - src));
+
+ /* Check for possible memory overflow. */
+ if (sprite[src].width * scaled_1 > UINT16_MAX || sprite[src].height * scaled_1 > UINT16_MAX) return false;
+
+ sprite[tgt].width = sprite[src].width * scaled_1;
+ sprite[tgt].height = sprite[src].height * scaled_1;
+ sprite[tgt].x_offs = sprite[src].x_offs * scaled_1;
+ sprite[tgt].y_offs = sprite[src].y_offs * scaled_1;
+
+ sprite[tgt].AllocateData(tgt, sprite[tgt].width * sprite[tgt].height);
+
+ SpriteLoader::CommonPixel *dst = sprite[tgt].data;
+ for (int y = 0; y < sprite[tgt].height; y++) {
+ const SpriteLoader::CommonPixel *src_ln = &sprite[src].data[y / scaled_1 * sprite[src].width];
+ for (int x = 0; x < sprite[tgt].width; x++) {
+ *dst = src_ln[x / scaled_1];
+ dst++;
+ }
+ }
+
+ return true;
+}
+
+static void ResizeSpriteOut(SpriteLoader::Sprite *sprite, ZoomLevel zoom)
+{
+ /* Algorithm based on 32bpp_Optimized::ResizeSprite() */
+ sprite[zoom].width = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom);
+ sprite[zoom].height = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom);
+ sprite[zoom].x_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom);
+ sprite[zoom].y_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom);
+
+ sprite[zoom].AllocateData(zoom, sprite[zoom].height * sprite[zoom].width);
+
+ SpriteLoader::CommonPixel *dst = sprite[zoom].data;
+ const SpriteLoader::CommonPixel *src = sprite[zoom - 1].data;
+ const SpriteLoader::CommonPixel *src_end = src + sprite[zoom - 1].height * sprite[zoom - 1].width;
+
+ for (uint y = 0; y < sprite[zoom].height; y++) {
+ if (src >= src_end) src = src_end - sprite[zoom - 1].width;
+
+ const SpriteLoader::CommonPixel *src_ln = src + sprite[zoom - 1].width * 2;
+ for (uint x = 0; x < sprite[zoom].width; x++) {
+ if (src >= src_ln) src = src_ln - 1;
+ if ((src + 1)->a != 0) { *dst = *(src + 1); }
+ else { *dst = *src; }
+ dst++;
+ src += 2;
+ }
+
+ src = src_ln;
+ }
+}
+
+static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint pad_left, uint pad_top, uint pad_right, uint pad_bottom)
+{
+ uint width = sprite->width + pad_left + pad_right;
+ uint height = sprite->height + pad_top + pad_bottom;
+
+ if (width > UINT16_MAX || height > UINT16_MAX) return false;
+
+ /* Copy source data and reallocate sprite memory. */
+ SpriteLoader::CommonPixel *src_data = MallocT<SpriteLoader::CommonPixel>(sprite->width * sprite->height);
+ MemCpyT(src_data, sprite->data, sprite->width * sprite->height);
+ sprite->AllocateData(zoom, width * height);
+
+ /* Copy with padding to destination. */
+ SpriteLoader::CommonPixel *src = src_data;
+ SpriteLoader::CommonPixel *data = sprite->data;
+ for (uint y = 0; y < height; y++) {
+ if (y < pad_top || pad_bottom + y >= height) {
+ /* Top/bottom padding. */
+ MemSetT(data, 0, width);
+ data += width;
+ } else {
+ if (pad_left > 0) {
+ /* Pad left. */
+ MemSetT(data, 0, pad_left);
+ data += pad_left;
+ }
+
+ /* Copy pixels. */
+ MemCpyT(data, src, sprite->width);
+ src += sprite->width;
+ data += sprite->width;
+
+ if (pad_right > 0) {
+ /* Pad right. */
+ MemSetT(data, 0, pad_right);
+ data += pad_right;
+ }
+ }
+ }
+ free(src_data);
+
+ /* Update sprite size. */
+ sprite->width = width;
+ sprite->height = height;
+ sprite->x_offs -= pad_left;
+ sprite->y_offs -= pad_top;
+
+ return true;
+}
+
+static bool PadSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail)
+{
+ int left = sprite[ZOOM_LVL_NORMAL].x_offs;
+ int top = sprite[ZOOM_LVL_NORMAL].y_offs;
+ int right = sprite[ZOOM_LVL_NORMAL].x_offs + sprite[ZOOM_LVL_NORMAL].width;
+ int bottom = sprite[ZOOM_LVL_NORMAL].y_offs + sprite[ZOOM_LVL_NORMAL].height;
+
+ /* Find combined bounds of all zoom levels.*/
+ for (ZoomLevel zoom = ZOOM_LVL_OUT_2X; zoom != ZOOM_LVL_END; zoom++) {
+ if (HasBit(sprite_avail, zoom)) {
+ uint8 scaled_1 = ScaleByZoom(1, zoom);
+
+ left = min(left, sprite[zoom].x_offs * scaled_1);
+ top = min(top, sprite[zoom].y_offs * scaled_1);
+ right = max(right, (sprite[zoom].x_offs + sprite[zoom].width - 1) * scaled_1);
+ bottom = max(bottom, (sprite[zoom].y_offs + sprite[zoom].height - 1) * scaled_1);
+ }
+ }
+
+ /* Pad too small sprites. */
+ SetBit(sprite_avail, ZOOM_LVL_NORMAL);
+ for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
+ if (HasBit(sprite_avail, zoom)) {
+ int pad_left = sprite[zoom].x_offs - UnScaleByZoom(left, zoom);
+ int pad_top = sprite[zoom].y_offs - UnScaleByZoom(top, zoom);
+ int pad_right = UnScaleByZoom(right, zoom) - (sprite[zoom].x_offs + sprite[zoom].width);
+ int pad_bottom = UnScaleByZoom(bottom, zoom) - (sprite[zoom].y_offs + sprite[zoom].height);
+
+ if (pad_left != 0 || pad_right != 0 || pad_top != 0 || pad_bottom != 0) {
+ if (!PadSingleSprite(&sprite[zoom], zoom, pad_left, pad_top, pad_right, pad_bottom)) return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos)
+{
+ /* Create a fully zoomed image if it does not exist */
+ ZoomLevel first_avail = static_cast<ZoomLevel>(FIND_FIRST_BIT(sprite_avail));
+ if (first_avail != ZOOM_LVL_NORMAL) {
+ if (!ResizeSpriteIn(sprite, first_avail, ZOOM_LVL_NORMAL)) return false;
+ }
+
+ /* Pad sprites to make sizes match. */
+ if (!PadSprites(sprite, sprite_avail)) return false;
+
+ /* Create other missing zoom levels */
+ for (ZoomLevel zoom = ZOOM_LVL_OUT_2X; zoom != ZOOM_LVL_END; zoom++) {
+ if (HasBit(sprite_avail, zoom)) {
+ /* Check that size and offsets match the fully zoomed image. */
+ assert(sprite[zoom].width == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom));
+ assert(sprite[zoom].height == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom));
+ assert(sprite[zoom].x_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom));
+ assert(sprite[zoom].y_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom));
+ }
+
+ /* Zoom level is not available, or unusable, so create it */
+ if (!HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom);
+ }
+
+ return true;
+}
+
/**
* Load a recolour sprite into memory.
* @param file_slot GRF we're reading from.
@@ -208,15 +382,21 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
DEBUG(sprite, 9, "Load sprite %d", id);
+ SpriteLoader::Sprite sprite[ZOOM_LVL_COUNT];
+ uint8 sprite_avail = 0;
+ sprite[ZOOM_LVL_NORMAL].type = sprite_type;
+
if (sprite_type == ST_NORMAL && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 32) {
#ifdef WITH_PNG
/* Try loading 32bpp graphics in case we are 32bpp output */
SpriteLoaderPNG sprite_loader;
- SpriteLoader::Sprite sprite;
- sprite.type = sprite_type;
- if (sprite_loader.LoadSprite(&sprite, file_slot, sc->id, sprite_type)) {
- return BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, allocator);
+ sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, sc->id, sprite_type);
+
+ if (sprite_avail != 0) {
+ if (ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
+ return BlitterFactoryBase::GetCurrentBlitter()->Encode(sprite, allocator);
+ }
}
/* If the PNG couldn't be loaded, fall back to 8bpp grfs */
#else
@@ -229,10 +409,9 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
}
SpriteLoaderGrf sprite_loader(sc->container_ver);
- SpriteLoader::Sprite sprite;
- sprite.type = sprite_type;
+ sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type);
- if (!sprite_loader.LoadSprite(&sprite, file_slot, file_pos, sprite_type)) {
+ if (sprite_avail == 0) {
if (sprite_type == ST_MAPGEN) return NULL;
if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
@@ -248,15 +427,15 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
* Ugly: yes. Other solution: no. Blame the original author or
* something ;) The image should really have been a data-stream
* (so type = 0xFF basicly). */
- uint num = sprite.width * sprite.height;
+ uint num = sprite[ZOOM_LVL_NORMAL].width * sprite[ZOOM_LVL_NORMAL].height;
Sprite *s = (Sprite *)allocator(sizeof(*s) + num);
- s->width = sprite.width;
- s->height = sprite.height;
- s->x_offs = sprite.x_offs;
- s->y_offs = sprite.y_offs;
+ s->width = sprite[ZOOM_LVL_NORMAL].width;
+ s->height = sprite[ZOOM_LVL_NORMAL].height;
+ s->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
+ s->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
- SpriteLoader::CommonPixel *src = sprite.data;
+ SpriteLoader::CommonPixel *src = sprite[ZOOM_LVL_NORMAL].data;
byte *dest = s->data;
while (num-- > 0) {
*dest++ = src->m;
@@ -266,7 +445,13 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
return s;
}
- return BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, allocator);
+ if (sprite_type == ST_NORMAL) {
+ if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
+ if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
+ return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
+ }
+ }
+ return BlitterFactoryBase::GetCurrentBlitter()->Encode(sprite, allocator);
}
@@ -704,4 +889,4 @@ void GfxClearSpriteCache()
}
}
-/* static */ ReusableBuffer<SpriteLoader::CommonPixel> SpriteLoader::Sprite::buffer;
+/* static */ ReusableBuffer<SpriteLoader::CommonPixel> SpriteLoader::Sprite::buffer[ZOOM_LVL_COUNT];