diff options
-rw-r--r-- | src/blitter/32bpp_optimized.cpp | 54 | ||||
-rw-r--r-- | src/blitter/8bpp_optimized.cpp | 19 | ||||
-rw-r--r-- | src/fontcache.cpp | 2 | ||||
-rw-r--r-- | src/spritecache.cpp | 215 | ||||
-rw-r--r-- | src/spriteloader/grf.cpp | 73 | ||||
-rw-r--r-- | src/spriteloader/grf.hpp | 2 | ||||
-rw-r--r-- | src/spriteloader/png.cpp | 30 | ||||
-rw-r--r-- | src/spriteloader/png.hpp | 2 | ||||
-rw-r--r-- | src/spriteloader/spriteloader.hpp | 11 |
9 files changed, 257 insertions, 151 deletions
diff --git a/src/blitter/32bpp_optimized.cpp b/src/blitter/32bpp_optimized.cpp index fbbf7f055..90a310075 100644 --- a/src/blitter/32bpp_optimized.cpp +++ b/src/blitter/32bpp_optimized.cpp @@ -207,55 +207,6 @@ void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, } } -/** - * Resizes the sprite in a very simple way, takes every n-th pixel and every n-th row - * - * @param sprite_src sprite to resize - * @param zoom resizing scale - * @return resized sprite - */ -static const SpriteLoader::Sprite *ResizeSprite(const SpriteLoader::Sprite *sprite_src, ZoomLevel zoom) -{ - SpriteLoader::Sprite *sprite = MallocT<SpriteLoader::Sprite>(1); - - if (zoom == ZOOM_LVL_NORMAL) { - memcpy(sprite, sprite_src, sizeof(*sprite)); - uint size = sprite_src->height * sprite_src->width; - sprite->data = MallocT<SpriteLoader::CommonPixel>(size); - memcpy(sprite->data, sprite_src->data, size * sizeof(SpriteLoader::CommonPixel)); - return sprite; - } - - sprite->height = UnScaleByZoom(sprite_src->height, zoom); - sprite->width = UnScaleByZoom(sprite_src->width, zoom); - sprite->x_offs = UnScaleByZoom(sprite_src->x_offs, zoom); - sprite->y_offs = UnScaleByZoom(sprite_src->y_offs, zoom); - - uint size = sprite->height * sprite->width; - SpriteLoader::CommonPixel *dst = sprite->data = CallocT<SpriteLoader::CommonPixel>(size); - - const SpriteLoader::CommonPixel *src = (SpriteLoader::CommonPixel *)sprite_src->data; - const SpriteLoader::CommonPixel *src_end = src + sprite_src->height * sprite_src->width; - - uint scaled_1 = ScaleByZoom(1, zoom); - - for (uint y = 0; y < sprite->height; y++) { - if (src >= src_end) src = src_end - sprite_src->width; - - const SpriteLoader::CommonPixel *src_ln = src + sprite_src->width * scaled_1; - for (uint x = 0; x < sprite->width; x++) { - if (src >= src_ln) src = src_ln - 1; - *dst = *src; - dst++; - src += scaled_1; - } - - src = src_ln; - } - - return sprite; -} - Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorProc *allocator) { /* streams of pixels (a, r, g, b channels) @@ -287,7 +238,7 @@ Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorPr } for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { - const SpriteLoader::Sprite *src_orig = ResizeSprite(sprite, z); + const SpriteLoader::Sprite *src_orig = &sprite[z]; uint size = src_orig->height * src_orig->width; @@ -371,9 +322,6 @@ Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorPr lengths[z][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[z]; // all are aligned to 4B boundary lengths[z][1] = (byte *)dst_n_ln - (byte *)dst_n_orig[z]; - - free(src_orig->data); - free(src_orig); } uint len = 0; // total length of data diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp index 300c79b52..acef6949e 100644 --- a/src/blitter/8bpp_optimized.cpp +++ b/src/blitter/8bpp_optimized.cpp @@ -130,7 +130,7 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorPro } for (ZoomLevel i = zoom_min; i <= zoom_max; i++) { - memory += UnScaleByZoom(sprite->height, i) * UnScaleByZoom(sprite->width, i); + memory += sprite[i].width * sprite[i].height; } /* We have no idea how much memory we really need, so just guess something */ @@ -150,9 +150,8 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorPro temp_dst->offset[i] = offset; /* cache values, because compiler can't cache it */ - int scaled_height = UnScaleByZoom(sprite->height, i); - int scaled_width = UnScaleByZoom(sprite->width, i); - int scaled_1 = ScaleByZoom(1, i); + int scaled_height = sprite[i].height; + int scaled_width = sprite[i].width; for (int y = 0; y < scaled_height; y++) { uint trans = 0; @@ -161,18 +160,10 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorPro byte *count_dst = NULL; /* Store the scaled image */ - const SpriteLoader::CommonPixel *src = &sprite->data[ScaleByZoom(y, i) * sprite->width]; - const SpriteLoader::CommonPixel *src_end = &src[sprite->width]; + const SpriteLoader::CommonPixel *src = &sprite[i].data[y * sprite[i].width]; for (int x = 0; x < scaled_width; x++) { - uint colour = 0; - - /* Get the colour keeping in mind the zoom-level */ - for (int j = 0; j < scaled_1; j++) { - if (src->m != 0) colour = src->m; - /* Because of the scaling it might happen we read outside the buffer. Avoid that. */ - if (++src == src_end) break; - } + uint colour = src++->m; if (last_colour == 0 || colour == 0 || pixels == 255) { if (count_dst != NULL) { diff --git a/src/fontcache.cpp b/src/fontcache.cpp index d35419597..bed9bc4b0 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -1152,7 +1152,7 @@ const Sprite *GetGlyph(FontSize size, WChar key) if (width > 256 || height > 256) usererror("Font glyph is too large"); /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */ - sprite.AllocateData(width * height); + sprite.AllocateData(ZOOM_LVL_NORMAL, width * height); sprite.type = ST_FONT; sprite.width = width; sprite.height = height; 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]; diff --git a/src/spriteloader/grf.cpp b/src/spriteloader/grf.cpp index 673647ba4..df0ce3702 100644 --- a/src/spriteloader/grf.cpp +++ b/src/spriteloader/grf.cpp @@ -59,9 +59,10 @@ static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line) * @param sprite_type Type of the sprite we're decoding. * @param num Size of the decompressed sprite. * @param type Type of the encoded sprite. + * @param zoom_lvl Requested zoom level. * @return True if the sprite was successfully loaded. */ -bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, int64 num, byte type) +bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl) { AutoFreePtr<byte> dest_orig(MallocT<byte>(num)); byte *dest = dest_orig; @@ -96,7 +97,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi if (num != 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__); - sprite->AllocateData(sprite->width * sprite->height * ZOOM_LVL_BASE * ZOOM_LVL_BASE); + sprite->AllocateData(zoom_lvl, sprite->width * sprite->height); /* When there are transparency pixels, this format has another trick.. decode it */ if (type & 0x08) { @@ -161,22 +162,6 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi } } - if (ZOOM_LVL_BASE != 1 && sprite_type == ST_NORMAL) { - /* Simple scaling, back-to-front so that no intermediate buffers are needed. */ - int width = sprite->width * ZOOM_LVL_BASE; - int height = sprite->height * ZOOM_LVL_BASE; - for (int y = height - 1; y >= 0; y--) { - for (int x = width - 1; x >= 0; x--) { - sprite->data[y * width + x] = sprite->data[y / ZOOM_LVL_BASE * sprite->width + x / ZOOM_LVL_BASE]; - } - } - - sprite->width *= ZOOM_LVL_BASE; - sprite->height *= ZOOM_LVL_BASE; - sprite->x_offs *= ZOOM_LVL_BASE; - sprite->y_offs *= ZOOM_LVL_BASE; - } - /* Make sure to mark all transparent pixels transparent on the alpha channel too */ for (int i = 0; i < sprite->width * sprite->height; i++) { if (sprite->data[i].m != 0) sprite->data[i].a = 0xFF; @@ -185,7 +170,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi return true; } -bool LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) +uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) { /* Open the right file and go to the correct position */ FioSeekToFile(file_slot, file_pos); @@ -195,24 +180,28 @@ bool LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos byte type = FioReadByte(); /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */ - if (type == 0xFF) return false; + if (type == 0xFF) return 0; + + ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL; - sprite->height = FioReadByte(); - sprite->width = FioReadWord(); - sprite->x_offs = FioReadWord(); - sprite->y_offs = FioReadWord(); + sprite[zoom_lvl].height = FioReadByte(); + sprite[zoom_lvl].width = FioReadWord(); + sprite[zoom_lvl].x_offs = FioReadWord(); + sprite[zoom_lvl].y_offs = FioReadWord(); /* 0x02 indicates it is a compressed sprite, so we can't rely on 'num' to be valid. * In case it is uncompressed, the size is 'num' - 8 (header-size). */ - num = (type & 0x02) ? sprite->width * sprite->height : num - 8; + num = (type & 0x02) ? sprite[zoom_lvl].width * sprite[zoom_lvl].height : num - 8; - return DecodeSingleSprite(sprite, file_slot, file_pos, sprite_type, num, type); + if (DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, num, type, zoom_lvl)) return 1 << zoom_lvl; + + return 0; } -bool LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) +uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) { /* Is the sprite not present/stripped in the GRF? */ - if (file_pos == SIZE_MAX) return false; + if (file_pos == SIZE_MAX) return 0; /* Open the right file and go to the correct position */ FioSeekToFile(file_slot, file_pos); @@ -225,27 +214,33 @@ bool LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos byte type = FioReadByte(); /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here. */ - if (type == 0xFF) return false; + if (type == 0xFF) return 0; byte colour = type & SCC_MASK; byte zoom = FioReadByte(); if (colour == SCC_PAL && zoom == 0) { - sprite->height = FioReadWord(); - sprite->width = FioReadWord(); - sprite->x_offs = FioReadWord(); - sprite->y_offs = FioReadWord(); + ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL; + + sprite[zoom_lvl].height = FioReadWord(); + sprite[zoom_lvl].width = FioReadWord(); + sprite[zoom_lvl].x_offs = FioReadWord(); + sprite[zoom_lvl].y_offs = FioReadWord(); /* Mask out colour information. */ type = type & ~SCC_MASK; /* For chunked encoding we store the decompressed size in the file, * otherwise we can calculate it from the image dimensions. */ - uint decomp_size = (type & 0x08) ? FioReadDword() : sprite->width * sprite->height; + uint decomp_size = (type & 0x08) ? FioReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height; - bool valid = DecodeSingleSprite(sprite, file_slot, file_pos, sprite_type, decomp_size, type); - if (FioGetPos() != start_pos + num) return WarnCorruptSprite(file_slot, file_pos, __LINE__); - return valid; + bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl); + if (FioGetPos() != start_pos + num) { + WarnCorruptSprite(file_slot, file_pos, __LINE__); + return 0; + } + + return 1 << zoom_lvl; } else { /* Not the wanted zoom level or colour depth, continue searching. */ FioSkipBytes(num - 2); @@ -253,10 +248,10 @@ bool LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos } while (FioReadDword() == id); - return false; + return 0; } -bool SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) +uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) { if (this->container_ver >= 2) { return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type); diff --git a/src/spriteloader/grf.hpp b/src/spriteloader/grf.hpp index 97e51c0a1..e33033f6f 100644 --- a/src/spriteloader/grf.hpp +++ b/src/spriteloader/grf.hpp @@ -19,7 +19,7 @@ class SpriteLoaderGrf : public SpriteLoader { byte container_ver; public: SpriteLoaderGrf(byte container_ver) : container_ver(container_ver) {} - bool LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type); + uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type); }; #endif /* SPRITELOADER_GRF_HPP */ diff --git a/src/spriteloader/png.cpp b/src/spriteloader/png.cpp index 76c914cbf..73403f520 100644 --- a/src/spriteloader/png.cpp +++ b/src/spriteloader/png.cpp @@ -51,7 +51,7 @@ static bool OpenPNGFile(const char *filename, uint32 id, bool mask) return false; } -static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, volatile bool mask) +static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, volatile bool mask, ZoomLevel zoom) { png_byte header[8]; png_structp png_ptr; @@ -115,7 +115,7 @@ static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 i } sprite->height = height; sprite->width = width; - sprite->AllocateData(sprite->width * sprite->height * ZOOM_LVL_BASE * ZOOM_LVL_BASE); + sprite->AllocateData(zoom, sprite->width * sprite->height); } else if (sprite->height != png_get_image_height(png_ptr, info_ptr) || sprite->width != png_get_image_width(png_ptr, info_ptr)) { /* Make sure the mask image isn't larger than the sprite image. */ DEBUG(misc, 0, "Ignoring mask for SpriteID %d as it isn't the same dimension as the masked sprite", id); @@ -206,29 +206,15 @@ static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 i return true; } -bool SpriteLoaderPNG::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) +uint8 SpriteLoaderPNG::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) { - const char *filename = FioGetFilename(file_slot); - if (!LoadPNG(sprite, filename, (uint32)file_pos, false)) return false; - if (!LoadPNG(sprite, filename, (uint32)file_pos, true)) return false; - - if (ZOOM_LVL_BASE != 1 && sprite_type == ST_NORMAL) { - /* Simple scaling, back-to-front so that no intermediate buffers are needed. */ - int width = sprite->width * ZOOM_LVL_BASE; - int height = sprite->height * ZOOM_LVL_BASE; - for (int y = height - 1; y >= 0; y--) { - for (int x = width - 1; x >= 0; x--) { - sprite->data[y * width + x] = sprite->data[y / ZOOM_LVL_BASE * sprite->width + x / ZOOM_LVL_BASE]; - } - } + ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL; - sprite->width *= ZOOM_LVL_BASE; - sprite->height *= ZOOM_LVL_BASE; - sprite->x_offs *= ZOOM_LVL_BASE; - sprite->y_offs *= ZOOM_LVL_BASE; - } + const char *filename = FioGetFilename(file_slot); + if (!LoadPNG(&sprite[zoom_lvl], filename, (uint32)file_pos, false, zoom_lvl)) return 0; + if (!LoadPNG(&sprite[zoom_lvl], filename, (uint32)file_pos, true, zoom_lvl)) return 0; - return true; + return 1 << zoom_lvl; } #endif /* WITH_PNG */ diff --git a/src/spriteloader/png.hpp b/src/spriteloader/png.hpp index 55f733e63..92d152e0f 100644 --- a/src/spriteloader/png.hpp +++ b/src/spriteloader/png.hpp @@ -17,7 +17,7 @@ /** Sprite loader for graphics coming from a PNG image. */ class SpriteLoaderPNG : public SpriteLoader { public: - bool LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type); + uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type); }; #endif /* SPRITELOADER_PNG_HPP */ diff --git a/src/spriteloader/spriteloader.hpp b/src/spriteloader/spriteloader.hpp index b1d9b5c58..030eb1850 100644 --- a/src/spriteloader/spriteloader.hpp +++ b/src/spriteloader/spriteloader.hpp @@ -43,23 +43,24 @@ public: /** * Allocate the sprite data of this sprite. + * @param zoom Zoom level to allocate the data for. * @param size the minimum size of the data field. */ - void AllocateData(size_t size) { this->data = Sprite::buffer.ZeroAllocate(size); } + void AllocateData(ZoomLevel zoom, size_t size) { this->data = Sprite::buffer[zoom].ZeroAllocate(size); } private: /** Allocated memory to pass sprite data around */ - static ReusableBuffer<SpriteLoader::CommonPixel> buffer; + static ReusableBuffer<SpriteLoader::CommonPixel> buffer[ZOOM_LVL_COUNT]; }; /** * Load a sprite from the disk and return a sprite struct which is the same for all loaders. - * @param sprite The sprite to fill with data. + * @param[out] sprite The sprites to fill with data. * @param file_slot The file "descriptor" of the file we read from. * @param file_pos The position within the file the image begins. * @param sprite_type The type of sprite we're trying to load. - * @return true iff loading went okay. + * @return Bit mask of the zoom levels successfully loaded or 0 if no sprite could be loaded. */ - virtual bool LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) = 0; + virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type) = 0; virtual ~SpriteLoader() { } }; |