# 32bpp_anim.cpp

Go to the documentation of this file.

    00001 /* $Id: 32bpp_anim.cpp 15428 2009-02-09 02:57:15Z rubidium $ */
    00002
    00005 #include "../stdafx.h"
    00006 #include "../core/alloc_func.hpp"
    00007 #include "../core/math_func.hpp"
    00008 #include "../gfx_func.h"
    00009 #include "../video/video_driver.hpp"
    00010 #include "32bpp_anim.hpp"
    00011
    00012 #include "../table/sprites.h"
    00013
    00014 static FBlitter_32bppAnim iFBlitter_32bppAnim;
    00015
    00016 template <BlitterMode mode>
    00017 inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
    00018 {
    00019   const SpriteData *src = (const SpriteData *)bp->sprite;
    00020
    00021   const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
    00022   const uint8  *src_n  = (const uint8  *)(src->data + src->offset[zoom][1]);
    00023
    00024   for (uint i = bp->skip_top; i != 0; i--) {
    00025     src_px = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
    00026     src_n += *(const uint32 *)src_n;
    00027   }
    00028
    00029   uint32 *dst = (uint32 *)bp->dst + bp->top * bp->pitch + bp->left;
    00030   uint8 *anim = this->anim_buf + ((uint32 *)bp->dst - (uint32 *)_screen.dst_ptr) + bp->top * this->anim_buf_width + bp->left;
    00031
    00032   const byte *remap = bp->remap; // store so we don't have to access it via bp everytime
    00033
    00034   for (int y = 0; y < bp->height; y++) {
    00035     uint32 *dst_ln = dst + bp->pitch;
    00036     uint8 *anim_ln = anim + this->anim_buf_width;
    00037
    00038     const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
    00039     src_px++;
    00040
    00041     const uint8 *src_n_ln = src_n + *(uint32 *)src_n;
    00042     src_n += 4;
    00043
    00044     uint32 *dst_end = dst + bp->skip_left;
    00045
    00046     uint n;
    00047
    00048     while (dst < dst_end) {
    00049       n = *src_n++;
    00050
    00051       if (src_px->a == 0) {
    00052         dst += n;
    00053         src_px ++;
    00054         src_n++;
    00055
    00056         if (dst > dst_end) anim += dst - dst_end;
    00057       } else {
    00058         if (dst + n > dst_end) {
    00059           uint d = dst_end - dst;
    00060           src_px += d;
    00061           src_n += d;
    00062
    00063           dst = dst_end - bp->skip_left;
    00064           dst_end = dst + bp->width;
    00065
    00066           n = min<uint>(n - d, (uint)bp->width);
    00067           goto draw;
    00068         }
    00069         dst += n;
    00070         src_px += n;
    00071         src_n += n;
    00072       }
    00073     }
    00074
    00075     dst -= bp->skip_left;
    00076     dst_end -= bp->skip_left;
    00077
    00078     dst_end += bp->width;
    00079
    00080     while (dst < dst_end) {
    00081       n = min<uint>(*src_n++, (uint)(dst_end - dst));
    00082
    00083       if (src_px->a == 0) {
    00084         anim += n;
    00085         dst += n;
    00086         src_px++;
    00087         src_n++;
    00088         continue;
    00089       }
    00090
    00091       draw:;
    00092
    00093       switch (mode) {
    00094         case BM_COLOUR_REMAP:
    00095           if (src_px->a == 255) {
    00096             do {
    00097               uint m = *src_n;
    00098               /* In case the m-channel is zero, do not remap this pixel in any way */
    00099               if (m == 0) {
    00100                 *dst = *src_px;
    00101                 *anim = 0;
    00102               } else {
    00103                 uint r = remap[m];
    00104                 *anim = r;
    00105                 if (r != 0) *dst = this->LookupColourInPalette(r);
    00106               }
    00107               anim++;
    00108               dst++;
    00109               src_px++;
    00110               src_n++;
    00111             } while (--n != 0);
    00112           } else {
    00113             do {
    00114               uint m = *src_n;
    00115               if (m == 0) {
    00116                 *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
    00117                 *anim = 0;
    00118               } else {
    00119                 uint r = remap[m];
    00120                 *anim = r;
    00121                 if (r != 0) *dst = ComposeColourPANoCheck(this->LookupColourInPalette(r), src_px->a, *dst);
    00122               }
    00123               anim++;
    00124               dst++;
    00125               src_px++;
    00126               src_n++;
    00127             } while (--n != 0);
    00128           }
    00129           break;
    00130
    00131         case BM_TRANSPARENT:
    00132           /* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
    00133            *  This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
    00134            *  we produce a result the newgrf maker didn't expect ;) */
    00135
    00136           /* Make the current colour a bit more black, so it looks like this image is transparent */
    00137           src_n += n;
    00138           if (src_px->a == 255) {
    00139             src_px += n;
    00140             do {
    00141               *dst = MakeTransparent(*dst, 3, 4);
    00142               *anim = remap[*anim];
    00143               anim++;
    00144               dst++;
    00145             } while (--n != 0);
    00146           } else {
    00147             do {
    00148               *dst = MakeTransparent(*dst, (256 * 4 - src_px->a), 256 * 4);
    00149               *anim = remap[*anim];
    00150               anim++;
    00151               dst++;
    00152               src_px++;
    00153             } while (--n != 0);
    00154           }
    00155           break;
    00156
    00157         default:
    00158           if (src_px->a == 255) {
    00159             do {
    00160               /* Compiler assumes pointer aliasing, can't optimise this on its own */
    00161               uint m = *src_n++;
    00162               /* Above 217 (PALETTE_ANIM_SIZE_START) is palette animation */
    00163               *anim++ = m;
    00164               *dst++ = (m >= PALETTE_ANIM_SIZE_START) ? this->LookupColourInPalette(m) : *src_px;
    00165               src_px++;
    00166             } while (--n != 0);
    00167           } else {
    00168             do {
    00169               uint m = *src_n++;
    00170               *anim++ = m;
    00171               if (m >= PALETTE_ANIM_SIZE_START) {
    00172                 *dst = ComposeColourPANoCheck(this->LookupColourInPalette(m), src_px->a, *dst);
    00173               } else {
    00174                 *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
    00175               }
    00176               dst++;
    00177               src_px++;
    00178             } while (--n != 0);
    00179           }
    00180           break;
    00181       }
    00182     }
    00183
    00184     anim = anim_ln;
    00185     dst = dst_ln;
    00186     src_px = src_px_ln;
    00187     src_n  = src_n_ln;
    00188   }
    00189 }
    00190
    00191 void Blitter_32bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
    00192 {
    00193   if (_screen_disable_anim) {
    00194     /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */
    00195     Blitter_32bppOptimized::Draw(bp, mode, zoom);
    00196     return;
    00197   }
    00198
    00199   if (_screen.width != this->anim_buf_width || _screen.height != this->anim_buf_height) {
    00200     /* The size of the screen changed; we can assume we can wipe all data from our buffer */
    00201     free(this->anim_buf);
    00202     this->anim_buf = CallocT<uint8>(_screen.width * _screen.height);
    00203     this->anim_buf_width = _screen.width;
    00204     this->anim_buf_height = _screen.height;
    00205   }
    00206
    00207   switch (mode) {
    00208     default: NOT_REACHED();
    00209     case BM_NORMAL:       Draw<BM_NORMAL>      (bp, zoom); return;
    00210     case BM_COLOUR_REMAP: Draw<BM_COLOUR_REMAP>(bp, zoom); return;
    00211     case BM_TRANSPARENT:  Draw<BM_TRANSPARENT> (bp, zoom); return;
    00212   }
    00213 }
    00214
    00215 void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height, int pal)
    00216 {
    00217   if (_screen_disable_anim) {
    00218     /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawColourMappingRect() */
    00219     Blitter_32bppOptimized::DrawColourMappingRect(dst, width, height, pal);
    00220     return;
    00221   }
    00222
    00223   uint32 *udst = (uint32 *)dst;
    00224   uint8 *anim;
    00225
    00226   anim = this->anim_buf + ((uint32 *)dst - (uint32 *)_screen.dst_ptr);
    00227
    00228   if (pal == PALETTE_TO_TRANSPARENT) {
    00229     do {
    00230       for (int i = 0; i != width; i++) {
    00231         *udst = MakeTransparent(*udst, 154);
    00232         *anim = 0;
    00233         udst++;
    00234         anim++;
    00235       }
    00236       udst = udst - width + _screen.pitch;
    00237       anim = anim - width + this->anim_buf_width;
    00238     } while (--height);
    00239     return;
    00240   }
    00241   if (pal == PALETTE_TO_STRUCT_GREY) {
    00242     do {
    00243       for (int i = 0; i != width; i++) {
    00244         *udst = MakeGrey(*udst);
    00245         *anim = 0;
    00246         udst++;
    00247         anim++;
    00248       }
    00249       udst = udst - width + _screen.pitch;
    00250       anim = anim - width + this->anim_buf_width;
    00251     } while (--height);
    00252     return;
    00253   }
    00254
    00255   DEBUG(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('%d')", pal);
    00256 }
    00257
    00258 void Blitter_32bppAnim::SetPixel(void *video, int x, int y, uint8 colour)
    00259 {
    00260   *((uint32 *)video + x + y * _screen.pitch) = LookupColourInPalette(colour);
    00261
    00262   /* Set the colour in the anim-buffer too, if we are rendering to the screen */
    00263   if (_screen_disable_anim) return;
    00264   this->anim_buf[((uint32 *)video - (uint32 *)_screen.dst_ptr) + x + y * this->anim_buf_width] = colour;
    00265 }
    00266
    00267 void Blitter_32bppAnim::SetPixelIfEmpty(void *video, int x, int y, uint8 colour)
    00268 {
    00269   uint32 *dst = (uint32 *)video + x + y * _screen.pitch;
    00270   if (*dst == 0) {
    00271     *dst = LookupColourInPalette(colour);
    00272     /* Set the colour in the anim-buffer too, if we are rendering to the screen */
    00273     if (_screen_disable_anim) return;
    00274     this->anim_buf[((uint32 *)video - (uint32 *)_screen.dst_ptr) + x + y * this->anim_buf_width] = colour;
    00275   }
    00276 }
    00277
    00278 void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colour)
    00279 {
    00280   if (_screen_disable_anim) {
    00281     /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
    00282     Blitter_32bppOptimized::DrawRect(video, width, height, colour);
    00283     return;
    00284   }
    00285
    00286   uint32 colour32 = LookupColourInPalette(colour);
    00287   uint8 *anim_line;
    00288
    00289   anim_line = ((uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
    00290
    00291   do {
    00292     uint32 *dst = (uint32 *)video;
    00293     uint8 *anim = anim_line;
    00294
    00295     for (int i = width; i > 0; i--) {
    00296       *dst = colour32;
    00297       /* Set the colour in the anim-buffer too */
    00298       *anim = colour;
    00299       dst++;
    00300       anim++;
    00301     }
    00302     video = (uint32 *)video + _screen.pitch;
    00303     anim_line += this->anim_buf_width;
    00304   } while (--height);
    00305 }
    00306
    00307 void Blitter_32bppAnim::CopyFromBuffer(void *video, const void *src, int width, int height)
    00308 {
    00309   assert(!_screen_disable_anim);
    00310   assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
    00311   uint32 *dst = (uint32 *)video;
    00312   uint32 *usrc = (uint32 *)src;
    00313   uint8 *anim_line;
    00314
    00315   anim_line = ((uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
    00316
    00317   for (; height > 0; height--) {
    00318     memcpy(dst, usrc, width * sizeof(uint32));
    00319     usrc += width;
    00320     dst += _screen.pitch;
    00321     /* Copy back the anim-buffer */
    00322     memcpy(anim_line, usrc, width * sizeof(uint8));
    00323     usrc = (uint32 *)((uint8 *)usrc + width);
    00324     anim_line += this->anim_buf_width;
    00325   }
    00326
    00327   /* We update the palette (or the pixels that do animation) immediatly, to avoid graphical glitches */
    00328   this->PaletteAnimate(PALETTE_ANIM_SIZE_START, (_use_palette == PAL_DOS) ? PALETTE_ANIM_SIZE_DOS : PALETTE_ANIM_SIZE_WIN);
    00329 }
    00330
    00331 void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, int height)
    00332 {
    00333   assert(!_screen_disable_anim);
    00334   assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
    00335   uint32 *udst = (uint32 *)dst;
    00336   uint32 *src = (uint32 *)video;
    00337   uint8 *anim_line;
    00338
    00339   if (this->anim_buf == NULL) return;
    00340
    00341   anim_line = ((uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
    00342
    00343   for (; height > 0; height--) {
    00344     memcpy(udst, src, width * sizeof(uint32));
    00345     src += _screen.pitch;
    00346     udst += width;
    00347     /* Copy the anim-buffer */
    00348     memcpy(udst, anim_line, width * sizeof(uint8));
    00349     udst = (uint32 *)((uint8 *)udst + width);
    00350     anim_line += this->anim_buf_width;
    00351   }
    00352 }
    00353
    00354 void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y)
    00355 {
    00356   assert(!_screen_disable_anim);
    00357   assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
    00358   uint8 *dst, *src;
    00359
    00360   /* We need to scroll the anim-buffer too */
    00361   if (scroll_y > 0) {
    00362     dst = this->anim_buf + left + (top + height - 1) * this->anim_buf_width;
    00363     src = dst - scroll_y * this->anim_buf_width;
    00364
    00365     /* Adjust left & width */
    00366     if (scroll_x >= 0) dst += scroll_x;
    00367     else               src -= scroll_x;
    00368
    00369     uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
    00370     uint th = height - scroll_y;
    00371     for (; th > 0; th--) {
    00372       memcpy(dst, src, tw * sizeof(uint8));
    00373       src -= this->anim_buf_width;
    00374       dst -= this->anim_buf_width;
    00375     }
    00376   } else {
    00377     /* Calculate pointers */
    00378     dst = this->anim_buf + left + top * this->anim_buf_width;
    00379     src = dst - scroll_y * this->anim_buf_width;
    00380
    00381     /* Adjust left & width */
    00382     if (scroll_x >= 0) dst += scroll_x;
    00383     else               src -= scroll_x;
    00384
    00385     /* the y-displacement may be 0 therefore we have to use memmove,
    00386      * because source and destination may overlap */
    00387     uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
    00388     uint th = height + scroll_y;
    00389     for (; th > 0; th--) {
    00390       memmove(dst, src, tw * sizeof(uint8));
    00391       src += this->anim_buf_width;
    00392       dst += this->anim_buf_width;
    00393     }
    00394   }
    00395
    00396   Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y);
    00397 }
    00398
    00399 int Blitter_32bppAnim::BufferSize(int width, int height)
    00400 {
    00401   return width * height * (sizeof(uint32) + sizeof(uint8));
    00402 }
    00403
    00404 void Blitter_32bppAnim::PaletteAnimate(uint start, uint count)
    00405 {
    00406   assert(!_screen_disable_anim);
    00407
    00408   /* Never repaint the transparency pixel */
    00409   if (start == 0) {
    00410     start++;
    00411     count--;
    00412   }
    00413
    00414   const uint8 *anim = this->anim_buf;
    00415   uint32 *dst = (uint32 *)_screen.dst_ptr;
    00416
    00417   /* Let's walk the anim buffer and try to find the pixels */
    00418   for (int y = this->anim_buf_height; y != 0 ; y--) {
    00419     for (int x = this->anim_buf_width; x != 0 ; x--) {
    00420       uint colour = *anim;
    00421       if (IsInsideBS(colour, start, count)) {
    00422         /* Update this pixel */
    00423         *dst = LookupColourInPalette(colour);
    00424       }
    00425       dst++;
    00426       anim++;
    00427     }
    00428     dst += _screen.pitch - this->anim_buf_width;
    00429   }
    00430
    00431   /* Make sure the backend redraws the whole screen */
    00432   _video_driver->MakeDirty(0, 0, _screen.width, _screen.height);
    00433 }
    00434
    00435 Blitter::PaletteAnimation Blitter_32bppAnim::UsePaletteAnimation()
    00436 {
    00437   return Blitter::PALETTE_ANIMATION_BLITTER;
    00438 }