32bpp_optimized.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "../stdafx.h"
00013 #include "../zoom_func.h"
00014 #include "../settings_type.h"
00015 #include "../core/math_func.hpp"
00016 #include "32bpp_optimized.hpp"
00017 
00019 static FBlitter_32bppOptimized iFBlitter_32bppOptimized;
00020 
00028 template <BlitterMode mode>
00029 inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
00030 {
00031   const SpriteData *src = (const SpriteData *)bp->sprite;
00032 
00033   /* src_px : each line begins with uint32 n = 'number of bytes in this line',
00034    *          then n times is the Colour struct for this line */
00035   const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
00036   /* src_n  : each line begins with uint32 n = 'number of bytes in this line',
00037    *          then interleaved stream of 'm' and 'n' channels. 'm' is remap,
00038    *          'n' is number of bytes with the same alpha channel class */
00039   const uint8  *src_n  = (const uint8  *)(src->data + src->offset[zoom][1]);
00040 
00041   /* skip upper lines in src_px and src_n */
00042   for (uint i = bp->skip_top; i != 0; i--) {
00043     src_px = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
00044     src_n += *(const uint32 *)src_n;
00045   }
00046 
00047   /* skip lines in dst */
00048   uint32 *dst = (uint32 *)bp->dst + bp->top * bp->pitch + bp->left;
00049 
00050   /* store so we don't have to access it via bp everytime (compiler assumes pointer aliasing) */
00051   const byte *remap = bp->remap;
00052 
00053   for (int y = 0; y < bp->height; y++) {
00054     /* next dst line begins here */
00055     uint32 *dst_ln = dst + bp->pitch;
00056 
00057     /* next src line begins here */
00058     const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
00059     src_px++;
00060 
00061     /* next src_n line begins here */
00062     const uint8 *src_n_ln = src_n + *(const uint32 *)src_n;
00063     src_n += 4;
00064 
00065     /* we will end this line when we reach this point */
00066     uint32 *dst_end = dst + bp->skip_left;
00067 
00068     /* number of pixels with the same aplha channel class */
00069     uint n;
00070 
00071     while (dst < dst_end) {
00072       n = *src_n++;
00073 
00074       if (src_px->a == 0) {
00075         dst += n;
00076         src_px ++;
00077         src_n++;
00078       } else {
00079         if (dst + n > dst_end) {
00080           uint d = dst_end - dst;
00081           src_px += d;
00082           src_n += d;
00083 
00084           dst = dst_end - bp->skip_left;
00085           dst_end = dst + bp->width;
00086 
00087           n = min<uint>(n - d, (uint)bp->width);
00088           goto draw;
00089         }
00090         dst += n;
00091         src_px += n;
00092         src_n += n;
00093       }
00094     }
00095 
00096     dst -= bp->skip_left;
00097     dst_end -= bp->skip_left;
00098 
00099     dst_end += bp->width;
00100 
00101     while (dst < dst_end) {
00102       n = min<uint>(*src_n++, (uint)(dst_end - dst));
00103 
00104       if (src_px->a == 0) {
00105         dst += n;
00106         src_px++;
00107         src_n++;
00108         continue;
00109       }
00110 
00111       draw:;
00112 
00113       switch (mode) {
00114         case BM_COLOUR_REMAP:
00115           if (src_px->a == 255) {
00116             do {
00117               uint m = *src_n;
00118               /* In case the m-channel is zero, do not remap this pixel in any way */
00119               if (m == 0) {
00120                 *dst = src_px->data;
00121               } else {
00122                 uint r = remap[m];
00123                 if (r != 0) *dst = this->LookupColourInPalette(r);
00124               }
00125               dst++;
00126               src_px++;
00127               src_n++;
00128             } while (--n != 0);
00129           } else {
00130             do {
00131               uint m = *src_n;
00132               if (m == 0) {
00133                 *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
00134               } else {
00135                 uint r = remap[m];
00136                 if (r != 0) *dst = ComposeColourPANoCheck(this->LookupColourInPalette(r), src_px->a, *dst);
00137               }
00138               dst++;
00139               src_px++;
00140               src_n++;
00141             } while (--n != 0);
00142           }
00143           break;
00144 
00145         case BM_TRANSPARENT:
00146           /* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
00147            *  This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
00148            *  we produce a result the newgrf maker didn't expect ;) */
00149 
00150           /* Make the current colour a bit more black, so it looks like this image is transparent */
00151           src_n += n;
00152           if (src_px->a == 255) {
00153             src_px += n;
00154             do {
00155               *dst = MakeTransparent(*dst, 3, 4);
00156               dst++;
00157             } while (--n != 0);
00158           } else {
00159             do {
00160               *dst = MakeTransparent(*dst, (256 * 4 - src_px->a), 256 * 4);
00161               dst++;
00162               src_px++;
00163             } while (--n != 0);
00164           }
00165           break;
00166 
00167         default:
00168           if (src_px->a == 255) {
00169             /* faster than memcpy(), n is usually low */
00170             src_n += n;
00171             do {
00172               *dst = src_px->data;
00173               dst++;
00174               src_px++;
00175             } while (--n != 0);
00176           } else {
00177             src_n += n;
00178             do {
00179               *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
00180               dst++;
00181               src_px++;
00182             } while (--n != 0);
00183           }
00184           break;
00185       }
00186     }
00187 
00188     dst = dst_ln;
00189     src_px = src_px_ln;
00190     src_n  = src_n_ln;
00191   }
00192 }
00193 
00201 void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
00202 {
00203   switch (mode) {
00204     default: NOT_REACHED();
00205     case BM_NORMAL:       Draw<BM_NORMAL>      (bp, zoom); return;
00206     case BM_COLOUR_REMAP: Draw<BM_COLOUR_REMAP>(bp, zoom); return;
00207     case BM_TRANSPARENT:  Draw<BM_TRANSPARENT> (bp, zoom); return;
00208   }
00209 }
00210 
00218 static const SpriteLoader::Sprite *ResizeSprite(const SpriteLoader::Sprite *sprite_src, ZoomLevel zoom)
00219 {
00220   SpriteLoader::Sprite *sprite = MallocT<SpriteLoader::Sprite>(1);
00221 
00222   if (zoom == ZOOM_LVL_NORMAL) {
00223     memcpy(sprite, sprite_src, sizeof(*sprite));
00224     uint size = sprite_src->height * sprite_src->width;
00225     sprite->data = MallocT<SpriteLoader::CommonPixel>(size);
00226     memcpy(sprite->data, sprite_src->data, size * sizeof(SpriteLoader::CommonPixel));
00227     return sprite;
00228   }
00229 
00230   sprite->height = UnScaleByZoom(sprite_src->height, zoom);
00231   sprite->width  = UnScaleByZoom(sprite_src->width,  zoom);
00232   sprite->x_offs = UnScaleByZoom(sprite_src->x_offs, zoom);
00233   sprite->y_offs = UnScaleByZoom(sprite_src->y_offs, zoom);
00234 
00235   uint size = sprite->height * sprite->width;
00236   SpriteLoader::CommonPixel *dst = sprite->data = CallocT<SpriteLoader::CommonPixel>(size);
00237 
00238   const SpriteLoader::CommonPixel *src = (SpriteLoader::CommonPixel *)sprite_src->data;
00239   const SpriteLoader::CommonPixel *src_end = src + sprite_src->height * sprite_src->width;
00240 
00241   uint scaled_1 = ScaleByZoom(1, zoom);
00242 
00243   for (uint y = 0; y < sprite->height; y++) {
00244     if (src >= src_end) src = src_end - sprite_src->width;
00245 
00246     const SpriteLoader::CommonPixel *src_ln = src + sprite_src->width * scaled_1;
00247     for (uint x = 0; x < sprite->width; x++) {
00248       if (src >= src_ln) src = src_ln - 1;
00249       *dst = *src;
00250       dst++;
00251       src += scaled_1;
00252     }
00253 
00254     src = src_ln;
00255   }
00256 
00257   return sprite;
00258 }
00259 
00260 Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
00261 {
00262   /* streams of pixels (a, r, g, b channels)
00263    *
00264    * stored in separated stream so data are always aligned on 4B boundary */
00265   Colour *dst_px_orig[ZOOM_LVL_COUNT];
00266 
00267   /* interleaved stream of 'm' channel and 'n' channel
00268    * 'n' is number if following pixels with the same alpha channel class
00269    * there are 3 classes: 0, 255, others
00270    *
00271    * it has to be stored in one stream so fewer registers are used -
00272    * x86 has problems with register allocation even with this solution */
00273   uint8  *dst_n_orig[ZOOM_LVL_COUNT];
00274 
00275   /* lengths of streams */
00276   uint32 lengths[ZOOM_LVL_COUNT][2];
00277 
00278   ZoomLevel zoom_min;
00279   ZoomLevel zoom_max;
00280 
00281   if (sprite->type == ST_FONT) {
00282     zoom_min = ZOOM_LVL_NORMAL;
00283     zoom_max = ZOOM_LVL_NORMAL;
00284   } else {
00285     zoom_min = _settings_client.gui.zoom_min;
00286     zoom_max = _settings_client.gui.zoom_max;
00287     if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX;
00288   }
00289 
00290   for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
00291     const SpriteLoader::Sprite *src_orig = ResizeSprite(sprite, z);
00292 
00293     uint size = src_orig->height * src_orig->width;
00294 
00295     dst_px_orig[z] = CallocT<Colour>(size + src_orig->height * 2);
00296     dst_n_orig[z]  = CallocT<uint8>(size * 2 + src_orig->height * 4 * 2);
00297 
00298     uint32 *dst_px_ln = (uint32 *)dst_px_orig[z];
00299     uint32 *dst_n_ln  = (uint32 *)dst_n_orig[z];
00300 
00301     const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *)src_orig->data;
00302 
00303     for (uint y = src_orig->height; y > 0; y--) {
00304       Colour *dst_px = (Colour *)(dst_px_ln + 1);
00305       uint8 *dst_n = (uint8 *)(dst_n_ln + 1);
00306 
00307       uint8 *dst_len = dst_n++;
00308 
00309       uint last = 3;
00310       int len = 0;
00311 
00312       for (uint x = src_orig->width; x > 0; x--) {
00313         uint8 a = src->a;
00314         uint t = a > 0 && a < 255 ? 1 : a;
00315 
00316         if (last != t || len == 255) {
00317           if (last != 3) {
00318             *dst_len = len;
00319             dst_len = dst_n++;
00320           }
00321           len = 0;
00322         }
00323 
00324         last = t;
00325         len++;
00326 
00327         if (a != 0) {
00328           dst_px->a = a;
00329           *dst_n = src->m;
00330           if (src->m != 0) {
00331             /* Pre-convert the mapping channel to a RGB value */
00332             uint32 colour = this->LookupColourInPalette(src->m);
00333             dst_px->r = GB(colour, 16, 8);
00334             dst_px->g = GB(colour, 8,  8);
00335             dst_px->b = GB(colour, 0,  8);
00336           } else {
00337             dst_px->r = src->r;
00338             dst_px->g = src->g;
00339             dst_px->b = src->b;
00340           }
00341           dst_px++;
00342           dst_n++;
00343         } else if (len == 1) {
00344           dst_px++;
00345           *dst_n = src->m;
00346           dst_n++;
00347         }
00348 
00349         src++;
00350       }
00351 
00352       if (last != 3) {
00353         *dst_len = len;
00354       }
00355 
00356       dst_px = (Colour *)AlignPtr(dst_px, 4);
00357       dst_n  = (uint8 *)AlignPtr(dst_n, 4);
00358 
00359       *dst_px_ln = (uint8 *)dst_px - (uint8 *)dst_px_ln;
00360       *dst_n_ln  = (uint8 *)dst_n  - (uint8 *)dst_n_ln;
00361 
00362       dst_px_ln = (uint32 *)dst_px;
00363       dst_n_ln =  (uint32 *)dst_n;
00364     }
00365 
00366     lengths[z][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[z]; // all are aligned to 4B boundary
00367     lengths[z][1] = (byte *)dst_n_ln  - (byte *)dst_n_orig[z];
00368 
00369     free(src_orig->data);
00370     free(src_orig);
00371   }
00372 
00373   uint len = 0; // total length of data
00374   for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
00375     len += lengths[z][0] + lengths[z][1];
00376   }
00377 
00378   Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sizeof(SpriteData) + len);
00379 
00380   dest_sprite->height = sprite->height;
00381   dest_sprite->width  = sprite->width;
00382   dest_sprite->x_offs = sprite->x_offs;
00383   dest_sprite->y_offs = sprite->y_offs;
00384 
00385   SpriteData *dst = (SpriteData *)dest_sprite->data;
00386 
00387   for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
00388     dst->offset[z][0] = z == zoom_min ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1];
00389     dst->offset[z][1] = lengths[z][0] + dst->offset[z][0];
00390 
00391     memcpy(dst->data + dst->offset[z][0], dst_px_orig[z], lengths[z][0]);
00392     memcpy(dst->data + dst->offset[z][1], dst_n_orig[z],  lengths[z][1]);
00393 
00394     free(dst_px_orig[z]);
00395     free(dst_n_orig[z]);
00396   }
00397 
00398   return dest_sprite;
00399 }