diff options
author | truelight <truelight@openttd.org> | 2007-06-11 11:50:49 +0000 |
---|---|---|
committer | truelight <truelight@openttd.org> | 2007-06-11 11:50:49 +0000 |
commit | 27febd474ccfccd8e5dc09014a7ad919575575aa (patch) | |
tree | 2fcb96fde1458283649c1303c0436fcfb369de55 /src/blitter | |
parent | 251a8e36e3848ca54dff31e0ed6bfd5825ec3ce2 (diff) | |
download | openttd-27febd474ccfccd8e5dc09014a7ad919575575aa.tar.xz |
(svn r10092) -Codechange: code-seperated the spriteloader and blitter from the rest of the code
-Add: make it possible to pick your own blitter (-b <blitter>, -h for overview)
-Add: added a new optimized 8bpp blitter (default, caches sprites of all zoom-levels)
-Add: added a debug 8bpp blitter and a very slow normal 8bpp blitter
Diffstat (limited to 'src/blitter')
-rw-r--r-- | src/blitter/8bpp_debug.cpp | 53 | ||||
-rw-r--r-- | src/blitter/8bpp_debug.hpp | 30 | ||||
-rw-r--r-- | src/blitter/8bpp_optimized.cpp | 199 | ||||
-rw-r--r-- | src/blitter/8bpp_optimized.hpp | 30 | ||||
-rw-r--r-- | src/blitter/8bpp_slow.cpp | 65 | ||||
-rw-r--r-- | src/blitter/8bpp_slow.hpp | 30 | ||||
-rw-r--r-- | src/blitter/blitter.hpp | 168 |
7 files changed, 575 insertions, 0 deletions
diff --git a/src/blitter/8bpp_debug.cpp b/src/blitter/8bpp_debug.cpp new file mode 100644 index 000000000..dea100c3f --- /dev/null +++ b/src/blitter/8bpp_debug.cpp @@ -0,0 +1,53 @@ +#include "../stdafx.h" +#include "../zoom.hpp" +#include "../gfx.h" +#include "../functions.h" +#include "8bpp_debug.hpp" + +static FBlitter_8bppDebug iFBlitter_8bppDebug; + +extern void* AllocSprite(size_t); + +void Blitter_8bppDebug::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +{ + const byte *src, *src_line; + Pixel8 *dst, *dst_line; + + /* Find where to start reading in the source sprite */ + src_line = (const byte *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, zoom); + dst_line = (Pixel8 *)bp->dst + bp->top * bp->pitch + bp->left; + + for (int y = 0; y < bp->height; y++) { + dst = dst_line; + dst_line += bp->pitch; + + src = src_line; + src_line += bp->sprite_width * ScaleByZoom(1, zoom); + + for (int x = 0; x < bp->width; x++) { + if (*src != 0) *dst = *src; + dst++; + src += ScaleByZoom(1, zoom); + } + assert(src <= src_line); + } +} + +Sprite *Blitter_8bppDebug::Encode(SpriteLoader::Sprite *sprite) +{ + Sprite *dest_sprite; + dest_sprite = (Sprite *)AllocSprite(sizeof(*dest_sprite) + sprite->height * sprite->width); + + dest_sprite->height = sprite->height; + dest_sprite->width = sprite->width; + dest_sprite->x_offs = sprite->x_offs; + dest_sprite->y_offs = sprite->y_offs; + + /* Write a random color as sprite; this makes debugging really easy */ + uint color = InteractiveRandom() % 150 + 2; + for (int i = 0; i < sprite->height * sprite->width; i++) { + dest_sprite->data[i] = (sprite->data[i].m == 0) ? 0 : color; + } + + return dest_sprite; +} diff --git a/src/blitter/8bpp_debug.hpp b/src/blitter/8bpp_debug.hpp new file mode 100644 index 000000000..67b12b652 --- /dev/null +++ b/src/blitter/8bpp_debug.hpp @@ -0,0 +1,30 @@ +/* $Id$ */ + +/** @file debug.hpp */ + +#ifndef BLITTER_8BPP_DEBUG_HPP +#define BLITTER_8BPP_DEBUG_HPP + +#include "blitter.hpp" + +typedef Pixel Pixel8; + +class Blitter_8bppDebug : public Blitter { +public: + uint8 GetScreenDepth() { return 8; } + + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); + + Sprite *Encode(SpriteLoader::Sprite *sprite); +}; + +class FBlitter_8bppDebug: public BlitterFactory<FBlitter_8bppDebug> { +public: + /* virtual */ const char *GetName() { return "8bpp-debug"; } + + /* virtual */ const char *GetDescription() { return "8bpp Debug Blitter (testing only)"; } + + /* virtual */ Blitter *CreateInstance() { return new Blitter_8bppDebug(); } +}; + +#endif /* BLITTER_8BPP_DEBUG_HPP */ diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp new file mode 100644 index 000000000..00c427441 --- /dev/null +++ b/src/blitter/8bpp_optimized.cpp @@ -0,0 +1,199 @@ +#include "../stdafx.h" +#include "../zoom.hpp" +#include "../gfx.h" +#include "../debug.h" +#include "8bpp_optimized.hpp" + +static FBlitter_8bppOptimized iFBlitter_8bppOptimized; + +extern void* AllocSprite(size_t); + +void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +{ + const byte *src, *src_next; + Pixel8 *dst, *dst_line; + uint offset = 0; + + /* Find the offset of this zoom-level */ + offset = ((const byte *)bp->sprite)[(int)zoom * 2] | ((const byte *)bp->sprite)[(int)zoom * 2 + 1] << 8; + + /* Find where to start reading in the source sprite */ + src = (const byte *)bp->sprite + offset; + dst_line = (Pixel8 *)bp->dst + bp->top * bp->pitch + bp->left; + + /* Skip over the top lines in the source image */ + for (int y = 0; y < bp->skip_top; y++) { + uint trans, pixels; + for (;;) { + trans = *src++; + pixels = *src++; + if (trans == 0 && pixels == 0) break; + src += pixels; + } + } + + src_next = src; + + for (int y = 0; y < bp->height; y++) { + dst = dst_line; + dst_line += bp->pitch; + + uint skip_left = bp->skip_left; + int width = bp->width; + + for (;;) { + src = src_next; + uint8 trans = *src++; + uint8 pixels = *src++; + src_next = src + pixels; + if (trans == 0 && pixels == 0) break; + if (width <= 0) continue; + + if (skip_left != 0) { + if (skip_left < trans) { + trans -= skip_left; + skip_left = 0; + } else { + skip_left -= trans; + trans = 0; + } + if (skip_left < pixels) { + src += skip_left; + pixels -= skip_left; + skip_left = 0; + } else { + src += pixels; + skip_left -= pixels; + pixels = 0; + } + } + if (skip_left != 0) continue; + + /* Skip transparent pixels */ + dst += trans; + width -= trans; + if (width <= 0) continue; + if (pixels > width) pixels = width; + width -= pixels; + + switch (mode) { + case BM_COLOUR_REMAP: + for (uint x = 0; x < pixels; x++) { + if (bp->remap[*src] != 0) *dst = bp->remap[*src]; + dst++; src++; + } + break; + + case BM_TRANSPARENT: + for (uint x = 0; x < pixels; x++) { + *dst = bp->remap[*dst]; + dst++; src++; + } + break; + + default: + memcpy(dst, src, pixels); + dst += pixels; src += pixels; + break; + } + } + } +} + +Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite) +{ + Sprite *dest_sprite; + byte *temp_dst; + uint memory = 0; + uint index = 0; + + /* Make memory for all zoom-levels */ + memory += (int)ZOOM_LVL_END * sizeof(uint16); + for (int i = 0; i < (int)ZOOM_LVL_END; i++) { + memory += UnScaleByZoom(sprite->height, (ZoomLevel)i) * UnScaleByZoom(sprite->width, (ZoomLevel)i); + index += 2; + } + + /* We have no idea how much memory we really need, so just guess something */ + memory *= 5; + temp_dst = MallocT<byte>(memory); + + /* Make the sprites per zoom-level */ + for (int i = 0; i < (int)ZOOM_LVL_END; i++) { + /* Store the scaled image */ + const SpriteLoader::CommonPixel *src; + + /* Store the index table */ + temp_dst[i * 2] = index & 0xFF; + temp_dst[i * 2 + 1] = (index >> 8) & 0xFF; + + byte *dst = &temp_dst[index]; + + for (int y = 0; y < UnScaleByZoom(sprite->height, (ZoomLevel)i); y++) { + uint trans = 0; + uint pixels = 0; + uint last_color = 0; + uint count_index = 0; + src = &sprite->data[ScaleByZoom(y, (ZoomLevel)i) * sprite->width]; + + for (int x = 0; x < UnScaleByZoom(sprite->width, (ZoomLevel)i); x++) { + uint color = 0; + int count = 0; + + /* Get the color keeping in mind the zoom-level */ + for (int j = 0; j < ScaleByZoom(1, (ZoomLevel)i); j++) { + if (src->m != 0) { color = src->m; count++; } + src++; + } + /* If more than 12.5% of the pixels are non-transparent, make thisone non-transparent too */ + if (count < ScaleByZoom(1, (ZoomLevel)i) / 8) color = 0; + + if (last_color == 0 || color == 0) { + if (count_index != 0) { + /* Write how many non-transparent bytes we get */ + temp_dst[count_index] = pixels; + pixels = 0; + count_index = 0; + } + /* As long as we find transparency bytes, keep counting */ + if (color == 0) { + last_color = 0; + trans++; + continue; + } + /* No longer transparency, so write the amount of transparent bytes */ + *dst = trans; + dst++; index++; + trans = 0; + /* Reserve a byte for the pixel counter */ + count_index = index; + dst++; index++; + } + last_color = color; + pixels++; + *dst = color; + dst++; index++; + } + + if (count_index != 0) temp_dst[count_index] = pixels; + + /* Write line-ending */ + *dst = 0; dst++; index++; + *dst = 0; dst++; index++; + } + } + + /* Safety check, to make sure we guessed the size correctly */ + assert(index < memory); + + /* Allocate the exact amount of memory we need */ + dest_sprite = (Sprite *)AllocSprite(sizeof(*dest_sprite) + index); + + dest_sprite->height = sprite->height; + dest_sprite->width = sprite->width; + dest_sprite->x_offs = sprite->x_offs; + dest_sprite->y_offs = sprite->y_offs; + memcpy(dest_sprite->data, temp_dst, index); + + return dest_sprite; +} diff --git a/src/blitter/8bpp_optimized.hpp b/src/blitter/8bpp_optimized.hpp new file mode 100644 index 000000000..8f251fb2c --- /dev/null +++ b/src/blitter/8bpp_optimized.hpp @@ -0,0 +1,30 @@ +/* $Id$ */ + +/** @file 8bpp_nice.hpp */ + +#ifndef BLITTER_8BPP_OPTIMIZED_HPP +#define BLITTER_8BPP_OPTIMIZED_HPP + +#include "blitter.hpp" + +typedef Pixel Pixel8; + +class Blitter_8bppOptimized : public Blitter { +public: + uint8 GetScreenDepth() { return 8; } + + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); + + Sprite *Encode(SpriteLoader::Sprite *sprite); +}; + +class FBlitter_8bppOptimized: public BlitterFactory<FBlitter_8bppOptimized> { +public: + /* virtual */ const char *GetName() { return "8bpp-optimzed"; } + + /* virtual */ const char *GetDescription() { return "8bpp Optimized Blitter (compression + all-ZoomLevel cache)"; } + + /* virtual */ Blitter *CreateInstance() { return new Blitter_8bppOptimized(); } +}; + +#endif /* BLITTER_8BPP_OPTIMIZED_HPP */ diff --git a/src/blitter/8bpp_slow.cpp b/src/blitter/8bpp_slow.cpp new file mode 100644 index 000000000..99c43a4f5 --- /dev/null +++ b/src/blitter/8bpp_slow.cpp @@ -0,0 +1,65 @@ +#include "../stdafx.h" +#include "../zoom.hpp" +#include "../gfx.h" +#include "8bpp_slow.hpp" + +static FBlitter_8bppSimple iFBlitter_8bppSimple; + +extern void* AllocSprite(size_t); + +void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +{ + const byte *src, *src_line; + Pixel8 *dst, *dst_line; + + /* Find where to start reading in the source sprite */ + src_line = (const byte *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, zoom); + dst_line = (Pixel8 *)bp->dst + bp->top * bp->pitch + bp->left; + + for (int y = 0; y < bp->height; y++) { + dst = dst_line; + dst_line += bp->pitch; + + src = src_line; + src_line += bp->sprite_width * ScaleByZoom(1, zoom); + + for (int x = 0; x < bp->width; x++) { + uint color = 0; + + switch (mode) { + case BM_COLOUR_REMAP: + color = bp->remap[*src]; + break; + + case BM_TRANSPARENT: + if (*src != 0) color = bp->remap[*dst]; + break; + + default: + color = *src; + break; + } + if (color != 0) *dst = color; + dst++; + src += ScaleByZoom(1, zoom); + } + } +} + +Sprite *Blitter_8bppSimple::Encode(SpriteLoader::Sprite *sprite) +{ + Sprite *dest_sprite; + dest_sprite = (Sprite *)AllocSprite(sizeof(*dest_sprite) + sprite->height * sprite->width); + + dest_sprite->height = sprite->height; + dest_sprite->width = sprite->width; + dest_sprite->x_offs = sprite->x_offs; + dest_sprite->y_offs = sprite->y_offs; + + /* Copy over only the 'remap' channel, as that is what we care about in 8bpp */ + for (int i = 0; i < sprite->height * sprite->width; i++) { + dest_sprite->data[i] = sprite->data[i].m; + } + + return dest_sprite; +} diff --git a/src/blitter/8bpp_slow.hpp b/src/blitter/8bpp_slow.hpp new file mode 100644 index 000000000..ac60e3262 --- /dev/null +++ b/src/blitter/8bpp_slow.hpp @@ -0,0 +1,30 @@ +/* $Id$ */ + +/** @file 8bpp.hpp */ + +#ifndef BLITTER_8BPP_SIMPLE_HPP +#define BLITTER_8BPP_SIMPLE_HPP + +#include "blitter.hpp" + +typedef Pixel Pixel8; + +class Blitter_8bppSimple : public Blitter { +public: + uint8 GetScreenDepth() { return 8; } + + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); + + Sprite *Encode(SpriteLoader::Sprite *sprite); +}; + +class FBlitter_8bppSimple: public BlitterFactory<FBlitter_8bppSimple> { +public: + /* virtual */ const char *GetName() { return "8bpp-simple"; } + + /* virtual */ const char *GetDescription() { return "8bpp Simple Blitter (relative slow, but never wrong)"; } + + /* virtual */ Blitter *CreateInstance() { return new Blitter_8bppSimple(); } +}; + +#endif /* BLITTER_8BPP_SIMPLE_HPP */ diff --git a/src/blitter/blitter.hpp b/src/blitter/blitter.hpp new file mode 100644 index 000000000..4920da426 --- /dev/null +++ b/src/blitter/blitter.hpp @@ -0,0 +1,168 @@ +/* $Id$ */ + +/** @file blitter.hpp */ + +#ifndef BLITTER_HPP +#define BLITTER_HPP + +#include "../spriteloader/spriteloader.hpp" +#include "../spritecache.h" +#include <string> +#include <map> + +enum BlitterMode { + BM_NORMAL, + BM_COLOUR_REMAP, + BM_TRANSPARENT, +}; + +/** + * How all blitters should look like. Extend this class to make your own. + */ +class Blitter { +public: + struct BlitterParams { + const void *sprite; ///< Pointer to the sprite how ever the encoder stored it + const byte *remap; ///< XXX -- Temporary storage for remap array + + int skip_left, skip_top; ///< How much pixels of the source to skip on the left and top (based on zoom of dst) + int width, height; ///< The width and height in pixels that needs to be drawn to dst + int sprite_width; ///< Real width of the sprite + int sprite_height; ///< Real height of the sprite + int left, top; ///< The offset in the 'dst' in pixels to start drawing + + void *dst; ///< Destination buffer + int pitch; ///< The pitch of the destination buffer + }; + + /** + * Get the screen depth this blitter works for. + * This is either: 8, 16, 24 or 32. + */ + virtual uint8 GetScreenDepth() = 0; + + /** + * Draw an image to the screen, given an amount of params defined above. + */ + virtual void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) = 0; + + /** + * Convert a sprite from the loader to our own format. + */ + virtual Sprite *Encode(SpriteLoader::Sprite *sprite) = 0; + + virtual ~Blitter() { } +}; + +/** + * The base factory, keeping track of all blitters. + */ +class BlitterFactoryBase { +private: + char *name; + typedef std::map<std::string, BlitterFactoryBase *> Blitters; + + static Blitters &GetBlitters() + { + static Blitters &s_blitters = *new Blitters(); + return s_blitters; + } + + static Blitter **GetActiveBlitter() + { + static Blitter *s_blitter = NULL; + return &s_blitter; + } + +protected: + /** + * Register a blitter internally, based on his name. + * @param name the name of the blitter. + * @note an assert() will be trigger if 2 blitters with the same name try to register. + */ + void RegisterBlitter(const char *name) + { + /* Don't register nameless Blitters */ + if (name == NULL) return; + + this->name = strdup(name); + std::pair<Blitters::iterator, bool> P = GetBlitters().insert(Blitters::value_type(name, this)); + assert(P.second); + } + +public: + BlitterFactoryBase() : + name(NULL) + {} + + virtual ~BlitterFactoryBase() { if (this->name != NULL) GetBlitters().erase(this->name); free(this->name); } + + /** + * Find the requested blitter and return his class. + * @param name the blitter to select. + * @post Sets the blitter so GetCurrentBlitter() returns it too. + */ + static Blitter *SelectBlitter(const char *name) + { + if (GetBlitters().size() == 0) return NULL; + + Blitters::iterator it = GetBlitters().begin(); + for (; it != GetBlitters().end(); it++) { + BlitterFactoryBase *b = (*it).second; + if (strcasecmp(name, b->name) == 0) { + Blitter *newb = b->CreateInstance(); + *GetActiveBlitter() = newb; + return newb; + } + } + return NULL; + } + + /** + * Get the current active blitter (always set by calling SelectBlitter). + */ + static Blitter *GetCurrentBlitter() + { + return *GetActiveBlitter(); + } + + + static char *GetBlittersInfo(char *p, const char *last) + { + p += snprintf(p, last - p, "List of blitters:\n"); + Blitters::iterator it = GetBlitters().begin(); + for (; it != GetBlitters().end(); it++) { + BlitterFactoryBase *b = (*it).second; + p += snprintf(p, last - p, "%18s: %s\n", b->name, b->GetDescription()); + } + p += snprintf(p, last - p, "\n"); + + return p; + } + + /** + * Get a nice description of the blitter-class. + */ + virtual const char *GetDescription() = 0; + + /** + * Create an instance of this Blitter-class. + */ + virtual Blitter *CreateInstance() = 0; +}; + +/** + * A template factory, so ->GetName() works correctly. This because else some compiler will complain. + */ +template <class T> +class BlitterFactory: public BlitterFactoryBase { +public: + BlitterFactory() { this->RegisterBlitter(((T *)this)->GetName()); } + + /** + * Get the long, human readable, name for the Blitter-class. + */ + const char *GetName(); +}; + +#endif /* BLITTER_HPP */ |