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 uint16 *src_n  = (const uint16 *)(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 uint16 *)((const byte *)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 uint16 *src_n_ln = (const uint16 *)((const byte *)src_n + *(const uint32 *)src_n);
00063     src_n += 2;
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[GB(m, 0, 8)];
00123                 if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
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[GB(m, 0, 8)];
00136                 if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), 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   uint16 *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<uint16>(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       uint16 *dst_n = (uint16 *)(dst_n_ln + 1);
00306 
00307       uint16 *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 == 65535) {
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             /* Get brightest value */
00332             uint8 rgb_max = max(src->r, max(src->g, src->b));
00333 
00334             /* Black pixel (8bpp or old 32bpp image), so use default value */
00335             if (rgb_max == 0) rgb_max = DEFAULT_BRIGHTNESS;
00336             *dst_n |= rgb_max << 8;
00337 
00338             /* Pre-convert the mapping channel to a RGB value */
00339             uint32 colour = this->AdjustBrightness(this->LookupColourInPalette(src->m), rgb_max);
00340             dst_px->r = GB(colour, 16, 8);
00341             dst_px->g = GB(colour, 8,  8);
00342             dst_px->b = GB(colour, 0,  8);
00343           } else {
00344             dst_px->r = src->r;
00345             dst_px->g = src->g;
00346             dst_px->b = src->b;
00347           }
00348           dst_px++;
00349           dst_n++;
00350         } else if (len == 1) {
00351           dst_px++;
00352           *dst_n = src->m;
00353           dst_n++;
00354         }
00355 
00356         src++;
00357       }
00358 
00359       if (last != 3) {
00360         *dst_len = len;
00361       }
00362 
00363       dst_px = (Colour *)AlignPtr(dst_px, 4);
00364       dst_n  = (uint16 *)AlignPtr(dst_n, 4);
00365 
00366       *dst_px_ln = (uint8 *)dst_px - (uint8 *)dst_px_ln;
00367       *dst_n_ln  = (uint8 *)dst_n  - (uint8 *)dst_n_ln;
00368 
00369       dst_px_ln = (uint32 *)dst_px;
00370       dst_n_ln =  (uint32 *)dst_n;
00371     }
00372 
00373     lengths[z][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[z]; // all are aligned to 4B boundary
00374     lengths[z][1] = (byte *)dst_n_ln  - (byte *)dst_n_orig[z];
00375 
00376     free(src_orig->data);
00377     free(src_orig);
00378   }
00379 
00380   uint len = 0; // total length of data
00381   for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
00382     len += lengths[z][0] + lengths[z][1];
00383   }
00384 
00385   Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sizeof(SpriteData) + len);
00386 
00387   dest_sprite->height = sprite->height;
00388   dest_sprite->width  = sprite->width;
00389   dest_sprite->x_offs = sprite->x_offs;
00390   dest_sprite->y_offs = sprite->y_offs;
00391 
00392   SpriteData *dst = (SpriteData *)dest_sprite->data;
00393 
00394   for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
00395     dst->offset[z][0] = z == zoom_min ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1];
00396     dst->offset[z][1] = lengths[z][0] + dst->offset[z][0];
00397 
00398     memcpy(dst->data + dst->offset[z][0], dst_px_orig[z], lengths[z][0]);
00399     memcpy(dst->data + dst->offset[z][1], dst_n_orig[z],  lengths[z][1]);
00400 
00401     free(dst_px_orig[z]);
00402     free(dst_n_orig[z]);
00403   }
00404 
00405   return dest_sprite;
00406 }