mirror of https://github.com/OpenTTD/OpenTTD
(svn r23889) -Codechange: Centralise sprite resizing in one place. (peter1138)
parent
2909e39e99
commit
e85d5b5d31
|
@ -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)
|
Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
|
||||||
{
|
{
|
||||||
/* streams of pixels (a, r, g, b channels)
|
/* 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++) {
|
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;
|
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][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];
|
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
|
uint len = 0; // total length of data
|
||||||
|
|
|
@ -130,7 +130,7 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorPro
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ZoomLevel i = zoom_min; i <= zoom_max; i++) {
|
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 */
|
/* 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;
|
temp_dst->offset[i] = offset;
|
||||||
|
|
||||||
/* cache values, because compiler can't cache it */
|
/* cache values, because compiler can't cache it */
|
||||||
int scaled_height = UnScaleByZoom(sprite->height, i);
|
int scaled_height = sprite[i].height;
|
||||||
int scaled_width = UnScaleByZoom(sprite->width, i);
|
int scaled_width = sprite[i].width;
|
||||||
int scaled_1 = ScaleByZoom(1, i);
|
|
||||||
|
|
||||||
for (int y = 0; y < scaled_height; y++) {
|
for (int y = 0; y < scaled_height; y++) {
|
||||||
uint trans = 0;
|
uint trans = 0;
|
||||||
|
@ -161,18 +160,10 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorPro
|
||||||
byte *count_dst = NULL;
|
byte *count_dst = NULL;
|
||||||
|
|
||||||
/* Store the scaled image */
|
/* Store the scaled image */
|
||||||
const SpriteLoader::CommonPixel *src = &sprite->data[ScaleByZoom(y, i) * sprite->width];
|
const SpriteLoader::CommonPixel *src = &sprite[i].data[y * sprite[i].width];
|
||||||
const SpriteLoader::CommonPixel *src_end = &src[sprite->width];
|
|
||||||
|
|
||||||
for (int x = 0; x < scaled_width; x++) {
|
for (int x = 0; x < scaled_width; x++) {
|
||||||
uint colour = 0;
|
uint colour = src++->m;
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_colour == 0 || colour == 0 || pixels == 255) {
|
if (last_colour == 0 || colour == 0 || pixels == 255) {
|
||||||
if (count_dst != NULL) {
|
if (count_dst != NULL) {
|
||||||
|
|
|
@ -1152,7 +1152,7 @@ const Sprite *GetGlyph(FontSize size, WChar key)
|
||||||
if (width > 256 || height > 256) usererror("Font glyph is too large");
|
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 */
|
/* 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.type = ST_FONT;
|
||||||
sprite.width = width;
|
sprite.width = width;
|
||||||
sprite.height = height;
|
sprite.height = height;
|
||||||
|
|
|
@ -13,11 +13,14 @@
|
||||||
#include "fileio_func.h"
|
#include "fileio_func.h"
|
||||||
#include "spriteloader/grf.hpp"
|
#include "spriteloader/grf.hpp"
|
||||||
#include "gfx_func.h"
|
#include "gfx_func.h"
|
||||||
|
#include "zoom_func.h"
|
||||||
|
#include "settings_type.h"
|
||||||
#ifdef WITH_PNG
|
#ifdef WITH_PNG
|
||||||
#include "spriteloader/png.hpp"
|
#include "spriteloader/png.hpp"
|
||||||
#endif /* WITH_PNG */
|
#endif /* WITH_PNG */
|
||||||
#include "blitter/factory.hpp"
|
#include "blitter/factory.hpp"
|
||||||
#include "core/math_func.hpp"
|
#include "core/math_func.hpp"
|
||||||
|
#include "core/mem_func.hpp"
|
||||||
|
|
||||||
#include "table/sprites.h"
|
#include "table/sprites.h"
|
||||||
#include "table/palette_convert.h"
|
#include "table/palette_convert.h"
|
||||||
|
@ -156,6 +159,177 @@ uint GetMaxSpriteID()
|
||||||
return _spritecache_items;
|
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.
|
* Load a recolour sprite into memory.
|
||||||
* @param file_slot GRF we're reading from.
|
* @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);
|
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) {
|
if (sprite_type == ST_NORMAL && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 32) {
|
||||||
#ifdef WITH_PNG
|
#ifdef WITH_PNG
|
||||||
/* Try loading 32bpp graphics in case we are 32bpp output */
|
/* Try loading 32bpp graphics in case we are 32bpp output */
|
||||||
SpriteLoaderPNG sprite_loader;
|
SpriteLoaderPNG sprite_loader;
|
||||||
SpriteLoader::Sprite sprite;
|
|
||||||
sprite.type = sprite_type;
|
|
||||||
|
|
||||||
if (sprite_loader.LoadSprite(&sprite, file_slot, sc->id, sprite_type)) {
|
sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, sc->id, sprite_type);
|
||||||
return BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, allocator);
|
|
||||||
|
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 */
|
/* If the PNG couldn't be loaded, fall back to 8bpp grfs */
|
||||||
#else
|
#else
|
||||||
|
@ -229,10 +409,9 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||||
}
|
}
|
||||||
|
|
||||||
SpriteLoaderGrf sprite_loader(sc->container_ver);
|
SpriteLoaderGrf sprite_loader(sc->container_ver);
|
||||||
SpriteLoader::Sprite sprite;
|
sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type);
|
||||||
sprite.type = 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 (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?");
|
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);
|
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
|
* Ugly: yes. Other solution: no. Blame the original author or
|
||||||
* something ;) The image should really have been a data-stream
|
* something ;) The image should really have been a data-stream
|
||||||
* (so type = 0xFF basicly). */
|
* (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);
|
Sprite *s = (Sprite *)allocator(sizeof(*s) + num);
|
||||||
s->width = sprite.width;
|
s->width = sprite[ZOOM_LVL_NORMAL].width;
|
||||||
s->height = sprite.height;
|
s->height = sprite[ZOOM_LVL_NORMAL].height;
|
||||||
s->x_offs = sprite.x_offs;
|
s->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
|
||||||
s->y_offs = sprite.y_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;
|
byte *dest = s->data;
|
||||||
while (num-- > 0) {
|
while (num-- > 0) {
|
||||||
*dest++ = src->m;
|
*dest++ = src->m;
|
||||||
|
@ -266,7 +445,13 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||||
return s;
|
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];
|
||||||
|
|
|
@ -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 sprite_type Type of the sprite we're decoding.
|
||||||
* @param num Size of the decompressed sprite.
|
* @param num Size of the decompressed sprite.
|
||||||
* @param type Type of the encoded sprite.
|
* @param type Type of the encoded sprite.
|
||||||
|
* @param zoom_lvl Requested zoom level.
|
||||||
* @return True if the sprite was successfully loaded.
|
* @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));
|
AutoFreePtr<byte> dest_orig(MallocT<byte>(num));
|
||||||
byte *dest = dest_orig;
|
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__);
|
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 */
|
/* When there are transparency pixels, this format has another trick.. decode it */
|
||||||
if (type & 0x08) {
|
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 */
|
/* Make sure to mark all transparent pixels transparent on the alpha channel too */
|
||||||
for (int i = 0; i < sprite->width * sprite->height; i++) {
|
for (int i = 0; i < sprite->width * sprite->height; i++) {
|
||||||
if (sprite->data[i].m != 0) sprite->data[i].a = 0xFF;
|
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;
|
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 */
|
/* Open the right file and go to the correct position */
|
||||||
FioSeekToFile(file_slot, file_pos);
|
FioSeekToFile(file_slot, file_pos);
|
||||||
|
@ -195,24 +180,28 @@ bool LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos
|
||||||
byte type = FioReadByte();
|
byte type = FioReadByte();
|
||||||
|
|
||||||
/* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */
|
/* 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;
|
||||||
|
|
||||||
sprite->height = FioReadByte();
|
ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL;
|
||||||
sprite->width = FioReadWord();
|
|
||||||
sprite->x_offs = FioReadWord();
|
sprite[zoom_lvl].height = FioReadByte();
|
||||||
sprite->y_offs = FioReadWord();
|
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.
|
/* 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). */
|
* 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? */
|
/* 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 */
|
/* Open the right file and go to the correct position */
|
||||||
FioSeekToFile(file_slot, file_pos);
|
FioSeekToFile(file_slot, file_pos);
|
||||||
|
@ -225,27 +214,33 @@ bool LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos
|
||||||
byte type = FioReadByte();
|
byte type = FioReadByte();
|
||||||
|
|
||||||
/* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here. */
|
/* 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 colour = type & SCC_MASK;
|
||||||
byte zoom = FioReadByte();
|
byte zoom = FioReadByte();
|
||||||
|
|
||||||
if (colour == SCC_PAL && zoom == 0) {
|
if (colour == SCC_PAL && zoom == 0) {
|
||||||
sprite->height = FioReadWord();
|
ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL;
|
||||||
sprite->width = FioReadWord();
|
|
||||||
sprite->x_offs = FioReadWord();
|
sprite[zoom_lvl].height = FioReadWord();
|
||||||
sprite->y_offs = FioReadWord();
|
sprite[zoom_lvl].width = FioReadWord();
|
||||||
|
sprite[zoom_lvl].x_offs = FioReadWord();
|
||||||
|
sprite[zoom_lvl].y_offs = FioReadWord();
|
||||||
|
|
||||||
/* Mask out colour information. */
|
/* Mask out colour information. */
|
||||||
type = type & ~SCC_MASK;
|
type = type & ~SCC_MASK;
|
||||||
|
|
||||||
/* For chunked encoding we store the decompressed size in the file,
|
/* For chunked encoding we store the decompressed size in the file,
|
||||||
* otherwise we can calculate it from the image dimensions. */
|
* 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);
|
bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl);
|
||||||
if (FioGetPos() != start_pos + num) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
|
if (FioGetPos() != start_pos + num) {
|
||||||
return valid;
|
WarnCorruptSprite(file_slot, file_pos, __LINE__);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1 << zoom_lvl;
|
||||||
} else {
|
} else {
|
||||||
/* Not the wanted zoom level or colour depth, continue searching. */
|
/* Not the wanted zoom level or colour depth, continue searching. */
|
||||||
FioSkipBytes(num - 2);
|
FioSkipBytes(num - 2);
|
||||||
|
@ -253,10 +248,10 @@ bool LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos
|
||||||
|
|
||||||
} while (FioReadDword() == id);
|
} 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) {
|
if (this->container_ver >= 2) {
|
||||||
return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type);
|
return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type);
|
||||||
|
|
|
@ -19,7 +19,7 @@ class SpriteLoaderGrf : public SpriteLoader {
|
||||||
byte container_ver;
|
byte container_ver;
|
||||||
public:
|
public:
|
||||||
SpriteLoaderGrf(byte container_ver) : container_ver(container_ver) {}
|
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 */
|
#endif /* SPRITELOADER_GRF_HPP */
|
||||||
|
|
|
@ -51,7 +51,7 @@ static bool OpenPNGFile(const char *filename, uint32 id, bool mask)
|
||||||
return false;
|
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_byte header[8];
|
||||||
png_structp png_ptr;
|
png_structp png_ptr;
|
||||||
|
@ -115,7 +115,7 @@ static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 i
|
||||||
}
|
}
|
||||||
sprite->height = height;
|
sprite->height = height;
|
||||||
sprite->width = width;
|
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)) {
|
} 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. */
|
/* 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);
|
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;
|
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)
|
||||||
{
|
{
|
||||||
|
ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL;
|
||||||
|
|
||||||
const char *filename = FioGetFilename(file_slot);
|
const char *filename = FioGetFilename(file_slot);
|
||||||
if (!LoadPNG(sprite, filename, (uint32)file_pos, false)) return false;
|
if (!LoadPNG(&sprite[zoom_lvl], filename, (uint32)file_pos, false, zoom_lvl)) return 0;
|
||||||
if (!LoadPNG(sprite, filename, (uint32)file_pos, true)) return false;
|
if (!LoadPNG(&sprite[zoom_lvl], filename, (uint32)file_pos, true, zoom_lvl)) return 0;
|
||||||
|
|
||||||
if (ZOOM_LVL_BASE != 1 && sprite_type == ST_NORMAL) {
|
return 1 << zoom_lvl;
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* WITH_PNG */
|
#endif /* WITH_PNG */
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
/** Sprite loader for graphics coming from a PNG image. */
|
/** Sprite loader for graphics coming from a PNG image. */
|
||||||
class SpriteLoaderPNG : public SpriteLoader {
|
class SpriteLoaderPNG : public SpriteLoader {
|
||||||
public:
|
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 */
|
#endif /* SPRITELOADER_PNG_HPP */
|
||||||
|
|
|
@ -43,23 +43,24 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate the sprite data of this sprite.
|
* 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.
|
* @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:
|
private:
|
||||||
/** Allocated memory to pass sprite data around */
|
/** 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.
|
* 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_slot The file "descriptor" of the file we read from.
|
||||||
* @param file_pos The position within the file the image begins.
|
* @param file_pos The position within the file the image begins.
|
||||||
* @param sprite_type The type of sprite we're trying to load.
|
* @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() { }
|
virtual ~SpriteLoader() { }
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue