# 32bpp_sse2.cpp

Go to the documentation of this file.

    00001 /* $Id: 32bpp_sse2.cpp 26223 2014-01-03 18:43:10Z planetmaker $ */
    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 #ifdef WITH_SSE
    00013
    00014 #include "../stdafx.h"
    00015 #include "../zoom_func.h"
    00016 #include "../settings_type.h"
    00017 #include "32bpp_sse2.hpp"
    00018
    00020 static FBlitter_32bppSSE2 iFBlitter_32bppSSE2;
    00021
    00029 IGNORE_UNINITIALIZED_WARNING_START
    00030 template <BlitterMode mode, Blitter_32bppSSE2::ReadMode read_mode, Blitter_32bppSSE2::BlockType bt_last>
    00031 inline void Blitter_32bppSSE2::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
    00032 {
    00033   Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left;
    00034   int effective_width = bp->width;
    00035
    00036   /* Find where to start reading in the source sprite */
    00037   const SpriteData * const sd = (const SpriteData *) bp->sprite;
    00038   const SpriteInfo * const si = &sd->infos[zoom];
    00039   const MapValue *src_mv_line = (const MapValue *) &sd->data[si->mv_offset] + bp->skip_top * si->sprite_width;
    00040   const Colour *src_rgba_line = (const Colour *) ((const byte *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size);
    00041
    00042   if (read_mode != RM_WITH_MARGIN) {
    00043     src_rgba_line += bp->skip_left;
    00044     src_mv_line += bp->skip_left;
    00045   }
    00046
    00047   /* Load these variables into register before loop. */
    00048   const __m128i clear_hi    = CLEAR_HIGH_BYTE_MASK;
    00049   const __m128i tr_nom_base = TRANSPARENT_NOM_BASE;
    00050
    00051   for (int y = bp->height; y != 0; y--) {
    00052     Colour *dst = dst_line;
    00053     const Colour *src = src_rgba_line + META_LENGTH;
    00054     const MapValue *src_mv = src_mv_line;
    00055
    00056     switch (mode) {
    00057       default: {
    00058         switch (read_mode) {
    00059           case RM_WITH_MARGIN: {
    00060             src += src_rgba_line[0].data;
    00061             dst += src_rgba_line[0].data;
    00062             const int width_diff = si->sprite_width - bp->width;
    00063             effective_width = bp->width - (int) src_rgba_line[0].data;
    00064             const int delta_diff = (int) src_rgba_line[1].data - width_diff;
    00065             const int new_width = effective_width - (delta_diff & ~1);
    00066             effective_width = delta_diff > 0 ? new_width : effective_width;
    00067             if (effective_width <= 0) break;
    00068             /* FALLTHROUGH */
    00069           }
    00070
    00071           case RM_WITH_SKIP: {
    00072             for (uint x = (uint) effective_width / 2; x > 0; x--) {
    00073               __m128i srcABCD = _mm_loadu_si128((const __m128i*) src);
    00074               __m128i dstABCD = _mm_loadu_si128((__m128i*) dst);
    00075               ALPHA_BLEND_2();
    00076               *(uint64*) dst = EXTR64(srcABCD, 0);
    00077               src += 2;
    00078               dst += 2;
    00079             }
    00080             if (bt_last == BT_ODD) {
    00081               __m128i srcABCD = _mm_loadu_si128((const __m128i*) src);
    00082               __m128i dstABCD = _mm_loadu_si128((__m128i*) dst);
    00083               ALPHA_BLEND_2();
    00084               (*dst).data = EXTR32(srcABCD, 0);
    00085             }
    00086             break;
    00087           }
    00088
    00089           default: NOT_REACHED();
    00090         }
    00091         break;
    00092       }
    00093       case BM_COLOUR_REMAP: {
    00094         switch (read_mode) {
    00095           case RM_WITH_MARGIN: {
    00096             src += src_rgba_line[0].data;
    00097             src_mv += src_rgba_line[0].data;
    00098             dst += src_rgba_line[0].data;
    00099             const int width_diff = si->sprite_width - bp->width;
    00100             effective_width = bp->width - (int) src_rgba_line[0].data;
    00101             const int delta_diff = (int) src_rgba_line[1].data - width_diff;
    00102             const int nd = effective_width - delta_diff;
    00103             effective_width = delta_diff > 0 ? nd : effective_width;
    00104             if (effective_width <= 0) break;
    00105             /* FALLTHROUGH */
    00106           }
    00107
    00108           case RM_WITH_SKIP: {
    00109             const byte *remap = bp->remap;
    00110             for (uint x = (uint) effective_width; x != 0; x--) {
    00111               /* In case the m-channel is zero, do not remap this pixel in any way */
    00112               if (src_mv->m == 0) {
    00113                 if (src->a < 255) {
    00114                   __m128i srcABCD = _mm_loadu_si128((const __m128i*) src);
    00115                   __m128i dstABCD = _mm_loadu_si128((__m128i*) dst);
    00116                   ALPHA_BLEND_2();
    00117                   (*dst).data = EXTR32(srcABCD, 0);
    00118                 } else {
    00119                   *dst = src->data;
    00120                 }
    00121               } else {
    00122                 const uint r = remap[src_mv->m];
    00123                 if (r != 0) {
    00124                   Colour remapped_colour = AdjustBrightness(this->LookupColourInPalette(r), src_mv->v);
    00125                   if (src->a < 255) {
    00126                     __m128i srcABCD;
    00127                     __m128i dstABCD = _mm_loadu_si128((__m128i*) dst);
    00128                     remapped_colour.a = src->a;
    00129                     INSR32(remapped_colour.data, srcABCD, 0);
    00130                     ALPHA_BLEND_2();
    00131                     (*dst).data = EXTR32(srcABCD, 0);
    00132                   } else
    00133                     *dst = remapped_colour;
    00134                 }
    00135               }
    00136               src_mv++;
    00137               dst++;
    00138               src++;
    00139             }
    00140             break;
    00141           }
    00142
    00143           default: NOT_REACHED();
    00144         }
    00145         src_mv_line += si->sprite_width;
    00146         break;
    00147       }
    00148       case BM_TRANSPARENT: {
    00149         /* Make the current colour a bit more black, so it looks like this image is transparent.
    00150          * rgb = rgb * ((256/4) * 4 - (alpha/4)) / ((256/4) * 4)
    00151          */
    00152         __m128i srcABCD = _mm_loadu_si128((const __m128i*) src);
    00153         __m128i dstABCD = _mm_loadu_si128((__m128i*) dst);
    00154         for (uint x = (uint) bp->width / 2; x > 0; x--) {
    00155           __m128i srcAB = _mm_unpacklo_epi8(srcABCD, _mm_setzero_si128());
    00156           __m128i dstAB = _mm_unpacklo_epi8(dstABCD, _mm_setzero_si128());
    00157           __m128i dstCD = _mm_unpackhi_epi8(dstABCD, _mm_setzero_si128());
    00158           __m128i alphaAB = _mm_shufflelo_epi16(srcAB, 0x3F);
    00159           alphaAB = _mm_shufflehi_epi16(alphaAB, 0x3F);
    00160           alphaAB = _mm_srli_epi16(alphaAB, 2); // Reduce to 64 levels of shades so the max value fits in 16 bits.
    00161           __m128i nom = _mm_sub_epi16(tr_nom_base, alphaAB);
    00162           dstAB = _mm_mullo_epi16(dstAB, nom);
    00163           dstAB = _mm_srli_epi16(dstAB, 8);
    00164           dstAB = _mm_packus_epi16(dstAB, dstCD);
    00165           Colour *old_dst = dst;
    00166           src += 2;
    00167           dst += 2;
    00168           dstABCD = _mm_loadu_si128((__m128i*) dst);
    00169           _mm_storeu_si128((__m128i *) old_dst, dstAB);
    00170           srcABCD = _mm_loadu_si128((const __m128i*) src);
    00171         }
    00172         if (bp->width & 1) {
    00173           __m128i srcAB = _mm_unpacklo_epi8(srcABCD, _mm_setzero_si128());
    00174           __m128i dstAB = _mm_unpacklo_epi8(dstABCD, _mm_setzero_si128());
    00175           __m128i alphaAB = _mm_shufflelo_epi16(srcAB, 0x3F);
    00176           alphaAB = _mm_shufflehi_epi16(alphaAB, 0x3F);
    00177           alphaAB = _mm_srli_epi16(alphaAB, 2);
    00178           __m128i nom = _mm_sub_epi16(tr_nom_base, alphaAB);
    00179           dstAB = _mm_mullo_epi16(dstAB, nom);
    00180           dstAB = _mm_srli_epi16(dstAB, 8);
    00181           dstAB = _mm_packus_epi16(dstAB, dstAB);
    00182           (*dst).data = EXTR32(dstAB, 0);
    00183         }
    00184         break;
    00185       }
    00186     }
    00187
    00188     src_rgba_line = (const Colour*) ((const byte*) src_rgba_line + si->sprite_line_size);
    00189     dst_line += bp->pitch;
    00190   }
    00191 }
    00192 IGNORE_UNINITIALIZED_WARNING_STOP
    00193
    00201 void Blitter_32bppSSE2::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
    00202 {
    00203   switch (mode) {
    00204     case BM_NORMAL: {
    00205       const BlockType bt_last = (BlockType) (bp->width & 1);
    00206       if (bp->skip_left != 0 || bp->width <= MARGIN_NORMAL_THRESHOLD) {
    00207         switch (bt_last) {
    00208           case BT_EVEN: Draw<BM_NORMAL, RM_WITH_SKIP, BT_EVEN>(bp, zoom); return;
    00209           case BT_ODD:  Draw<BM_NORMAL, RM_WITH_SKIP, BT_ODD>(bp, zoom); return;
    00210           default: NOT_REACHED();
    00211         }
    00212       } else {
    00213         switch (bt_last) {
    00214           case BT_EVEN: Draw<BM_NORMAL, RM_WITH_MARGIN, BT_EVEN>(bp, zoom); return;
    00215           case BT_ODD:  Draw<BM_NORMAL, RM_WITH_MARGIN, BT_ODD>(bp, zoom); return;
    00216           default: NOT_REACHED();
    00217         }
    00218       }
    00219       break;
    00220     }
    00221     case BM_COLOUR_REMAP:
    00222       if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) {
    00223         Draw<BM_COLOUR_REMAP, RM_WITH_SKIP, BT_NONE>(bp, zoom); return;
    00224       } else {
    00225         Draw<BM_COLOUR_REMAP, RM_WITH_MARGIN, BT_NONE>(bp, zoom); return;
    00226       }
    00227     case BM_TRANSPARENT:  Draw<BM_TRANSPARENT, RM_NONE, BT_NONE>(bp, zoom); return;
    00228     default: NOT_REACHED();
    00229   }
    00230 }
    00231
    00232 Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
    00233 {
    00234   /* First uint32 of a line = ~1 & the number of transparent pixels from the left.
    00235    * Second uint32 of a line = the number of transparent pixels from the right.
    00236    * Then all RGBA then all MV.
    00237    */
    00238   ZoomLevel zoom_min = ZOOM_LVL_NORMAL;
    00239   ZoomLevel zoom_max = ZOOM_LVL_NORMAL;
    00240   if (sprite->type != ST_FONT) {
    00241     zoom_min = _settings_client.gui.zoom_min;
    00242     zoom_max = _settings_client.gui.zoom_max;
    00243     if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX;
    00244   }
    00245
    00246   /* Calculate sizes and allocate. */
    00247   SpriteData sd;
    00248   uint all_sprites_size = 0;
    00249   for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
    00250     const SpriteLoader::Sprite *src_sprite = &sprite[z];
    00251     sd.infos[z].sprite_width = src_sprite->width;
    00252     sd.infos[z].sprite_offset = all_sprites_size;
    00253     sd.infos[z].sprite_line_size = sizeof(Colour) * src_sprite->width + sizeof(uint32) * META_LENGTH;
    00254
    00255     const uint rgba_size = sd.infos[z].sprite_line_size * src_sprite->height;
    00256     sd.infos[z].mv_offset = all_sprites_size + rgba_size;
    00257
    00258     const uint mv_size = sizeof(MapValue) * src_sprite->width * src_sprite->height;
    00259     all_sprites_size += rgba_size + mv_size;
    00260   }
    00261
    00262   Sprite *dst_sprite = (Sprite *) allocator(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size);
    00263   dst_sprite->height = sprite->height;
    00264   dst_sprite->width  = sprite->width;
    00265   dst_sprite->x_offs = sprite->x_offs;
    00266   dst_sprite->y_offs = sprite->y_offs;
    00267   memcpy(dst_sprite->data, &sd, sizeof(SpriteData));
    00268
    00269   /* Copy colours. */
    00270   for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
    00271     const SpriteLoader::Sprite *src_sprite = &sprite[z];
    00272     const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *) src_sprite->data;
    00273     Colour *dst_rgba_line = (Colour *) &dst_sprite->data[sizeof(SpriteData) + sd.infos[z].sprite_offset];
    00274     MapValue *dst_mv = (MapValue *) &dst_sprite->data[sizeof(SpriteData) + sd.infos[z].mv_offset];
    00275     for (uint y = src_sprite->height; y != 0; y--) {
    00276       Colour *dst_rgba = dst_rgba_line + META_LENGTH;
    00277       for (uint x = src_sprite->width; x != 0; x--) {
    00278         if (src->a != 0) {
    00279           dst_rgba->a = src->a;
    00280           dst_mv->m = src->m;
    00281           if (src->m != 0) {
    00282             /* Get brightest value (or default brightness if it's a black pixel). */
    00283             const uint8 rgb_max = max(src->r, max(src->g, src->b));
    00284             dst_mv->v = (rgb_max == 0) ? Blitter_32bppBase::DEFAULT_BRIGHTNESS : rgb_max;
    00285
    00286             /* Pre-convert the mapping channel to a RGB value. */
    00287             const Colour colour = AdjustBrightness(Blitter_32bppBase::LookupColourInPalette(src->m), dst_mv->v);
    00288             dst_rgba->r = colour.r;
    00289             dst_rgba->g = colour.g;
    00290             dst_rgba->b = colour.b;
    00291           } else {
    00292             dst_rgba->r = src->r;
    00293             dst_rgba->g = src->g;
    00294             dst_rgba->b = src->b;
    00295             dst_mv->v = Blitter_32bppBase::DEFAULT_BRIGHTNESS;
    00296           }
    00297         } else {
    00298           dst_rgba->data = 0;
    00299           *(uint16*) dst_mv = 0;
    00300         }
    00301         dst_rgba++;
    00302         dst_mv++;
    00303         src++;
    00304       }
    00305
    00306       /* Count the number of transparent pixels from the left. */
    00307       dst_rgba = dst_rgba_line + META_LENGTH;
    00308       uint32 nb_pix_transp = 0;
    00309       for (uint x = src_sprite->width; x != 0; x--) {
    00310         if (dst_rgba->a == 0) nb_pix_transp++;
    00311         else break;
    00312         dst_rgba++;
    00313       }
    00314       (*dst_rgba_line).data = nb_pix_transp & ~1; // "& ~1" to preserve the last block type
    00315
    00316       Colour *nb_right = dst_rgba_line + 1;
    00317       dst_rgba_line = (Colour*) ((byte*) dst_rgba_line + sd.infos[z].sprite_line_size);
    00318
    00319       /* Count the number of transparent pixels from the right. */
    00320       dst_rgba = dst_rgba_line - 1;
    00321       nb_pix_transp = 0;
    00322       for (uint x = src_sprite->width; x != 0; x--) {
    00323         if (dst_rgba->a == 0) nb_pix_transp++;
    00324         else break;
    00325         dst_rgba--;
    00326       }
    00327       (*nb_right).data = nb_pix_transp; // no "& ~1" here, must be done when we know bp->width
    00328     }
    00329   }
    00330
    00331   return dst_sprite;
    00332 }
    00333
    00337 inline Colour Blitter_32bppSSE2::AdjustBrightness(Colour colour, uint8 brightness)
    00338 {
    00339   /* Shortcut for normal brightness. */
    00340   if (brightness == DEFAULT_BRIGHTNESS) return colour;
    00341
    00342   return Blitter_32bppSSE2::ReallyAdjustBrightness(colour, brightness);
    00343 }
    00344
    00345 IGNORE_UNINITIALIZED_WARNING_START
    00346 /* static */ Colour Blitter_32bppSSE2::ReallyAdjustBrightness(Colour colour, uint8 brightness)
    00347 {
    00348   ALIGN(16) uint64 c16 = colour.b | (uint64) colour.g << 16 | (uint64) colour.r << 32;
    00349   c16 *= brightness;
    00350   uint64 c16_ob = c16; // Helps out of order execution.
    00351   c16 /= DEFAULT_BRIGHTNESS;
    00352   c16 &= 0x01FF01FF01FF;
    00353
    00354   /* Sum overbright (maximum for each rgb is 508, 9 bits, -255 is changed in -256 so we just have to take the 8 lower bits into account). */
    00355   c16_ob = (((c16_ob >> (8 + 7)) & 0x0100010001) * 0xFF) & c16;
    00356   uint64 ob = (uint16) c16_ob + (uint16) (c16_ob >> 16) + (uint16) (c16_ob >> 32);
    00357
    00358   const uint32 alpha32 = colour.data & 0xFF000000;
    00359   __m128i ret;
    00360   INSR64(c16, ret, 0);
    00361   if (ob != 0) {
    00362     /* Reduce overbright strength. */
    00363     ob /= 2;
    00364     __m128i ob128;
    00365     INSR64(ob | ob << 16 | ob << 32, ob128, 0);
    00366     __m128i white = OVERBRIGHT_VALUE_MASK;
    00367     __m128i c128 = ret;
    00368     ret = _mm_subs_epu16(white, c128); /* PSUBUSW,   (255 - rgb) */
    00369     ret = _mm_mullo_epi16(ret, ob128); /* PMULLW, ob*(255 - rgb) */
    00370     ret = _mm_srli_epi16(ret, 8);      /* PSRLW,  ob*(255 - rgb)/256 */
    00371     ret = _mm_add_epi16(ret, c128);    /* PADDW,  ob*(255 - rgb)/256 + rgb */
    00372   }
    00373
    00374   ret = _mm_packus_epi16(ret, ret);      /* PACKUSWB, saturate and pack. */
    00375   return alpha32 | EXTR32(ret, 0);
    00376 }
    00377 IGNORE_UNINITIALIZED_WARNING_STOP
    00378
    00379 #endif /* WITH_SSE */