1
0
mirror of https://github.com/OpenTTD/OpenTTD.git synced 2025-08-20 04:59:11 +00:00

(svn r23889) -Codechange: Centralise sprite resizing in one place. (peter1138)

This commit is contained in:
michi_cc
2012-02-04 13:29:13 +00:00
parent 2909e39e99
commit e85d5b5d31
9 changed files with 257 additions and 151 deletions

View File

@@ -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];