ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

transparent_surface.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  *
00022  * The bottom part of this is file is adapted from SDL_rotozoom.c. The
00023  * relevant copyright notice for those specific functions can be found at the
00024  * top of that section.
00025  *
00026  */
00027 
00028 
00029 
00030 #include "common/algorithm.h"
00031 #include "common/endian.h"
00032 #include "common/util.h"
00033 #include "common/rect.h"
00034 #include "common/math.h"
00035 #include "common/textconsole.h"
00036 #include "graphics/primitives.h"
00037 #include "graphics/transparent_surface.h"
00038 #include "graphics/transform_tools.h"
00039 
00040 namespace Graphics {
00041 
00042 static const int kBModShift = 0;//img->format.bShift;
00043 static const int kGModShift = 8;//img->format.gShift;
00044 static const int kRModShift = 16;//img->format.rShift;
00045 static const int kAModShift = 24;//img->format.aShift;
00046 
00047 #ifdef SCUMM_LITTLE_ENDIAN
00048 static const int kAIndex = 0;
00049 static const int kBIndex = 1;
00050 static const int kGIndex = 2;
00051 static const int kRIndex = 3;
00052 
00053 #else
00054 static const int kAIndex = 3;
00055 static const int kBIndex = 2;
00056 static const int kGIndex = 1;
00057 static const int kRIndex = 0;
00058 #endif
00059 
00060 void doBlitOpaqueFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep);
00061 void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep);
00062 void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
00063 void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
00064 void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
00065 void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
00066 
00067 TransparentSurface::TransparentSurface() : Surface(), _alphaMode(ALPHA_FULL) {}
00068 
00069 TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _alphaMode(ALPHA_FULL) {
00070     if (copyData) {
00071         copyFrom(surf);
00072     } else {
00073         w = surf.w;
00074         h = surf.h;
00075         pitch = surf.pitch;
00076         format = surf.format;
00077         // We need to cast the const qualifier away here because 'pixels'
00078         // always needs to be writable. 'surf' however is a constant Surface,
00079         // thus getPixels will always return const pixel data.
00080         pixels = const_cast<void *>(surf.getPixels());
00081     }
00082 }
00083 
00087 void doBlitOpaqueFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
00088 
00089     byte *in;
00090     byte *out;
00091 
00092     for (uint32 i = 0; i < height; i++) {
00093         out = outo;
00094         in = ino;
00095         memcpy(out, in, width * 4);
00096         for (uint32 j = 0; j < width; j++) {
00097             out[kAIndex] = 0xFF;
00098             out += 4;
00099         }
00100         outo += pitch;
00101         ino += inoStep;
00102     }
00103 }
00104 
00108 void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
00109 
00110     byte *in;
00111     byte *out;
00112 
00113     for (uint32 i = 0; i < height; i++) {
00114         out = outo;
00115         in = ino;
00116         for (uint32 j = 0; j < width; j++) {
00117             uint32 pix = *(uint32 *)in;
00118             int a = in[kAIndex];
00119 
00120             if (a != 0) {   // Full opacity (Any value not exactly 0 is Opaque here)
00121                 *(uint32 *)out = pix;
00122                 out[kAIndex] = 0xFF;
00123             }
00124             out += 4;
00125             in += inStep;
00126         }
00127         outo += pitch;
00128         ino += inoStep;
00129     }
00130 }
00131 
00143 void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
00144     byte *in;
00145     byte *out;
00146 
00147     if (color == 0xffffffff) {
00148 
00149         for (uint32 i = 0; i < height; i++) {
00150             out = outo;
00151             in = ino;
00152             for (uint32 j = 0; j < width; j++) {
00153 
00154                 if (in[kAIndex] != 0) {
00155                     out[kAIndex] = 255;
00156                     out[kRIndex] = ((in[kRIndex] * in[kAIndex]) + out[kRIndex] * (255 - in[kAIndex])) >> 8;
00157                     out[kGIndex] = ((in[kGIndex] * in[kAIndex]) + out[kGIndex] * (255 - in[kAIndex])) >> 8;
00158                     out[kBIndex] = ((in[kBIndex] * in[kAIndex]) + out[kBIndex] * (255 - in[kAIndex])) >> 8;
00159                 }
00160 
00161                 in += inStep;
00162                 out += 4;
00163             }
00164             outo += pitch;
00165             ino += inoStep;
00166         }
00167     } else {
00168 
00169         byte ca = (color >> kAModShift) & 0xFF;
00170         byte cr = (color >> kRModShift) & 0xFF;
00171         byte cg = (color >> kGModShift) & 0xFF;
00172         byte cb = (color >> kBModShift) & 0xFF;
00173 
00174         for (uint32 i = 0; i < height; i++) {
00175             out = outo;
00176             in = ino;
00177             for (uint32 j = 0; j < width; j++) {
00178 
00179                 uint32 ina = in[kAIndex] * ca >> 8;
00180 
00181                 if (ina != 0) {
00182                     out[kAIndex] = 255;
00183                     out[kBIndex] = (out[kBIndex] * (255 - ina) >> 8);
00184                     out[kGIndex] = (out[kGIndex] * (255 - ina) >> 8);
00185                     out[kRIndex] = (out[kRIndex] * (255 - ina) >> 8);
00186 
00187                     out[kBIndex] = out[kBIndex] + (in[kBIndex] * ina * cb >> 16);
00188                     out[kGIndex] = out[kGIndex] + (in[kGIndex] * ina * cg >> 16);
00189                     out[kRIndex] = out[kRIndex] + (in[kRIndex] * ina * cr >> 16);
00190                 }
00191 
00192                 in += inStep;
00193                 out += 4;
00194             }
00195             outo += pitch;
00196             ino += inoStep;
00197         }
00198     }
00199 }
00200 
00204 void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
00205     byte *in;
00206     byte *out;
00207 
00208     if (color == 0xffffffff) {
00209 
00210         for (uint32 i = 0; i < height; i++) {
00211             out = outo;
00212             in = ino;
00213             for (uint32 j = 0; j < width; j++) {
00214 
00215                 if (in[kAIndex] != 0) {
00216                     out[kRIndex] = MIN((in[kRIndex] * in[kAIndex] >> 8) + out[kRIndex], 255);
00217                     out[kGIndex] = MIN((in[kGIndex] * in[kAIndex] >> 8) + out[kGIndex], 255);
00218                     out[kBIndex] = MIN((in[kBIndex] * in[kAIndex] >> 8) + out[kBIndex], 255);
00219                 }
00220 
00221                 in += inStep;
00222                 out += 4;
00223             }
00224             outo += pitch;
00225             ino += inoStep;
00226         }
00227     } else {
00228 
00229         byte ca = (color >> kAModShift) & 0xFF;
00230         byte cr = (color >> kRModShift) & 0xFF;
00231         byte cg = (color >> kGModShift) & 0xFF;
00232         byte cb = (color >> kBModShift) & 0xFF;
00233 
00234         for (uint32 i = 0; i < height; i++) {
00235             out = outo;
00236             in = ino;
00237             for (uint32 j = 0; j < width; j++) {
00238 
00239                 uint32 ina = in[kAIndex] * ca >> 8;
00240 
00241                 if (cb != 255) {
00242                     out[kBIndex] = MIN<uint>(out[kBIndex] + ((in[kBIndex] * cb * ina) >> 16), 255u);
00243                 } else {
00244                     out[kBIndex] = MIN<uint>(out[kBIndex] + (in[kBIndex] * ina >> 8), 255u);
00245                 }
00246 
00247                 if (cg != 255) {
00248                     out[kGIndex] = MIN<uint>(out[kGIndex] + ((in[kGIndex] * cg * ina) >> 16), 255u);
00249                 } else {
00250                     out[kGIndex] = MIN<uint>(out[kGIndex] + (in[kGIndex] * ina >> 8), 255u);
00251                 }
00252 
00253                 if (cr != 255) {
00254                     out[kRIndex] = MIN<uint>(out[kRIndex] + ((in[kRIndex] * cr * ina) >> 16), 255u);
00255                 } else {
00256                     out[kRIndex] = MIN<uint>(out[kRIndex] + (in[kRIndex] * ina >> 8), 255u);
00257                 }
00258 
00259                 in += inStep;
00260                 out += 4;
00261             }
00262             outo += pitch;
00263             ino += inoStep;
00264         }
00265     }
00266 }
00267 
00271 void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
00272     byte *in;
00273     byte *out;
00274 
00275     if (color == 0xffffffff) {
00276 
00277         for (uint32 i = 0; i < height; i++) {
00278             out = outo;
00279             in = ino;
00280             for (uint32 j = 0; j < width; j++) {
00281 
00282                 if (in[kAIndex] != 0) {
00283                     out[kRIndex] = MAX(out[kRIndex] - ((in[kRIndex] * out[kRIndex]) * in[kAIndex] >> 16), 0);
00284                     out[kGIndex] = MAX(out[kGIndex] - ((in[kGIndex] * out[kGIndex]) * in[kAIndex] >> 16), 0);
00285                     out[kBIndex] = MAX(out[kBIndex] - ((in[kBIndex] * out[kBIndex]) * in[kAIndex] >> 16), 0);
00286                 }
00287 
00288                 in += inStep;
00289                 out += 4;
00290             }
00291             outo += pitch;
00292             ino += inoStep;
00293         }
00294     } else {
00295 
00296         byte cr = (color >> kRModShift) & 0xFF;
00297         byte cg = (color >> kGModShift) & 0xFF;
00298         byte cb = (color >> kBModShift) & 0xFF;
00299 
00300         for (uint32 i = 0; i < height; i++) {
00301             out = outo;
00302             in = ino;
00303             for (uint32 j = 0; j < width; j++) {
00304 
00305                 out[kAIndex] = 255;
00306                 if (cb != 255) {
00307                     out[kBIndex] = MAX(out[kBIndex] - ((in[kBIndex] * cb  * (out[kBIndex]) * in[kAIndex]) >> 24), 0);
00308                 } else {
00309                     out[kBIndex] = MAX(out[kBIndex] - (in[kBIndex] * (out[kBIndex]) * in[kAIndex] >> 16), 0);
00310                 }
00311 
00312                 if (cg != 255) {
00313                     out[kGIndex] = MAX(out[kGIndex] - ((in[kGIndex] * cg  * (out[kGIndex]) * in[kAIndex]) >> 24), 0);
00314                 } else {
00315                     out[kGIndex] = MAX(out[kGIndex] - (in[kGIndex] * (out[kGIndex]) * in[kAIndex] >> 16), 0);
00316                 }
00317 
00318                 if (cr != 255) {
00319                     out[kRIndex] = MAX(out[kRIndex] - ((in[kRIndex] * cr * (out[kRIndex]) * in[kAIndex]) >> 24), 0);
00320                 } else {
00321                     out[kRIndex] = MAX(out[kRIndex] - (in[kRIndex] * (out[kRIndex]) * in[kAIndex] >> 16), 0);
00322                 }
00323 
00324                 in += inStep;
00325                 out += 4;
00326             }
00327             outo += pitch;
00328             ino += inoStep;
00329         }
00330     }
00331 }
00332 
00336 void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
00337     byte *in;
00338     byte *out;
00339 
00340     if (color == 0xffffffff) {
00341         for (uint32 i = 0; i < height; i++) {
00342             out = outo;
00343             in = ino;
00344             for (uint32 j = 0; j < width; j++) {
00345 
00346                 if (in[kAIndex] != 0) {
00347                     out[kRIndex] = MIN((in[kRIndex] * in[kAIndex] >> 8) * out[kRIndex] >> 8, 255);
00348                     out[kGIndex] = MIN((in[kGIndex] * in[kAIndex] >> 8) * out[kGIndex] >> 8, 255);
00349                     out[kBIndex] = MIN((in[kBIndex] * in[kAIndex] >> 8) * out[kBIndex] >> 8, 255);
00350                 }
00351 
00352                 in += inStep;
00353                 out += 4;
00354             }
00355             outo += pitch;
00356             ino += inoStep;
00357         }
00358     } else {
00359         byte ca = (color >> kAModShift) & 0xFF;
00360         byte cr = (color >> kRModShift) & 0xFF;
00361         byte cg = (color >> kGModShift) & 0xFF;
00362         byte cb = (color >> kBModShift) & 0xFF;
00363 
00364         for (uint32 i = 0; i < height; i++) {
00365             out = outo;
00366             in = ino;
00367             for (uint32 j = 0; j < width; j++) {
00368 
00369                 uint32 ina = in[kAIndex] * ca >> 8;
00370 
00371                 if (cb != 255) {
00372                     out[kBIndex] = MIN<uint>(out[kBIndex] * ((in[kBIndex] * cb * ina) >> 16) >> 8, 255u);
00373                 } else {
00374                     out[kBIndex] = MIN<uint>(out[kBIndex] * (in[kBIndex] * ina >> 8) >> 8, 255u);
00375                 }
00376 
00377                 if (cg != 255) {
00378                     out[kGIndex] = MIN<uint>(out[kGIndex] * ((in[kGIndex] * cg * ina) >> 16) >> 8, 255u);
00379                 } else {
00380                     out[kGIndex] = MIN<uint>(out[kGIndex] * (in[kGIndex] * ina >> 8) >> 8, 255u);
00381                 }
00382 
00383                 if (cr != 255) {
00384                     out[kRIndex] = MIN<uint>(out[kRIndex] * ((in[kRIndex] * cr * ina) >> 16) >> 8, 255u);
00385                 } else {
00386                     out[kRIndex] = MIN<uint>(out[kRIndex] * (in[kRIndex] * ina >> 8) >> 8, 255u);
00387                 }
00388 
00389                 in += inStep;
00390                 out += 4;
00391             }
00392             outo += pitch;
00393             ino += inoStep;
00394         }
00395     }
00396 
00397 }
00398 
00399 Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
00400 
00401     Common::Rect retSize;
00402     retSize.top = 0;
00403     retSize.left = 0;
00404     retSize.setWidth(0);
00405     retSize.setHeight(0);
00406     // Check if we need to draw anything at all
00407     int ca = (color >> kAModShift) & 0xff;
00408 
00409     if (ca == 0) {
00410         return retSize;
00411     }
00412 
00413     // Create an encapsulating surface for the data
00414     TransparentSurface srcImage(*this, false);
00415     // TODO: Is the data really in the screen format?
00416     if (format.bytesPerPixel != 4) {
00417         warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
00418         return retSize;
00419     }
00420 
00421     if (pPartRect) {
00422 
00423         int xOffset = pPartRect->left;
00424         int yOffset = pPartRect->top;
00425 
00426         if (flipping & FLIP_V) {
00427             yOffset = srcImage.h - pPartRect->bottom;
00428         }
00429 
00430         if (flipping & FLIP_H) {
00431             xOffset = srcImage.w - pPartRect->right;
00432         }
00433 
00434         srcImage.pixels = getBasePtr(xOffset, yOffset);
00435         srcImage.w = pPartRect->width();
00436         srcImage.h = pPartRect->height();
00437 
00438         debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
00439               pPartRect->left,  pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
00440     } else {
00441 
00442         debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
00443               srcImage.w, srcImage.h, color, width, height);
00444     }
00445 
00446     if (width == -1) {
00447         width = srcImage.w;
00448     }
00449     if (height == -1) {
00450         height = srcImage.h;
00451     }
00452 
00453 #ifdef SCALING_TESTING
00454     // Hardcode scaling to 66% to test scaling
00455     width = width * 2 / 3;
00456     height = height * 2 / 3;
00457 #endif
00458 
00459     Graphics::Surface *img = nullptr;
00460     Graphics::Surface *imgScaled = nullptr;
00461     byte *savedPixels = nullptr;
00462     if ((width != srcImage.w) || (height != srcImage.h)) {
00463         // Scale the image
00464         img = imgScaled = srcImage.scale(width, height);
00465         savedPixels = (byte *)img->getPixels();
00466     } else {
00467         img = &srcImage;
00468     }
00469 
00470     // Handle off-screen clipping
00471     if (posY < 0) {
00472         img->h = MAX(0, (int)img->h - -posY);
00473         if (!(flipping & FLIP_V))
00474             img->setPixels((byte *)img->getBasePtr(0, -posY));
00475         posY = 0;
00476     }
00477 
00478     if (posX < 0) {
00479         img->w = MAX(0, (int)img->w - -posX);
00480         if (!(flipping & FLIP_H))
00481             img->setPixels((byte *)img->getBasePtr(-posX, 0));
00482         posX = 0;
00483     }
00484 
00485     if (img->w > target.w - posX) {
00486         if (flipping & FLIP_H)
00487             img->setPixels((byte *)img->getBasePtr(img->w - target.w + posX, 0));
00488         img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0));
00489     }
00490 
00491     if (img->h > target.h - posY) {
00492         if (flipping & FLIP_V)
00493             img->setPixels((byte *)img->getBasePtr(0, img->h - target.h + posY));
00494         img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0));
00495     }
00496 
00497     // Flip surface
00498     if ((img->w > 0) && (img->h > 0)) {
00499         int xp = 0, yp = 0;
00500 
00501         int inStep = 4;
00502         int inoStep = img->pitch;
00503         if (flipping & FLIP_H) {
00504             inStep = -inStep;
00505             xp = img->w - 1;
00506         }
00507 
00508         if (flipping & FLIP_V) {
00509             inoStep = -inoStep;
00510             yp = img->h - 1;
00511         }
00512 
00513         byte *ino = (byte *)img->getBasePtr(xp, yp);
00514         byte *outo = (byte *)target.getBasePtr(posX, posY);
00515 
00516         if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_OPAQUE) {
00517             doBlitOpaqueFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
00518         } else if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_BINARY) {
00519             doBlitBinaryFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
00520         } else {
00521             if (blendMode == BLEND_ADDITIVE) {
00522                 doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00523             } else if (blendMode == BLEND_SUBTRACTIVE) {
00524                 doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00525             } else if (blendMode == BLEND_MULTIPLY) {
00526                 doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00527             } else {
00528                 assert(blendMode == BLEND_NORMAL);
00529                 doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00530             }
00531         }
00532 
00533     }
00534 
00535     retSize.setWidth(img->w);
00536     retSize.setHeight(img->h);
00537 
00538     if (imgScaled) {
00539         imgScaled->setPixels(savedPixels);
00540         imgScaled->free();
00541         delete imgScaled;
00542     }
00543 
00544     return retSize;
00545 }
00546 
00547 Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rect clippingArea, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
00548     Common::Rect retSize;
00549     retSize.top = 0;
00550     retSize.left = 0;
00551     retSize.setWidth(0);
00552     retSize.setHeight(0);
00553     // Check if we need to draw anything at all
00554     int ca = (color >> kAModShift) & 0xff;
00555 
00556     if (ca == 0) {
00557         return retSize;
00558     }
00559 
00560     // Create an encapsulating surface for the data
00561     TransparentSurface srcImage(*this, false);
00562     // TODO: Is the data really in the screen format?
00563     if (format.bytesPerPixel != 4) {
00564         warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
00565         return retSize;
00566     }
00567 
00568     if (pPartRect) {
00569 
00570         int xOffset = pPartRect->left;
00571         int yOffset = pPartRect->top;
00572 
00573         if (flipping & FLIP_V) {
00574             yOffset = srcImage.h - pPartRect->bottom;
00575         }
00576 
00577         if (flipping & FLIP_H) {
00578             xOffset = srcImage.w - pPartRect->right;
00579         }
00580 
00581         srcImage.pixels = getBasePtr(xOffset, yOffset);
00582         srcImage.w = pPartRect->width();
00583         srcImage.h = pPartRect->height();
00584 
00585         debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
00586             pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
00587     } else {
00588 
00589         debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
00590             srcImage.w, srcImage.h, color, width, height);
00591     }
00592 
00593     if (width == -1) {
00594         width = srcImage.w;
00595     }
00596     if (height == -1) {
00597         height = srcImage.h;
00598     }
00599 
00600 #ifdef SCALING_TESTING
00601     // Hardcode scaling to 66% to test scaling
00602     width = width * 2 / 3;
00603     height = height * 2 / 3;
00604 #endif
00605 
00606     Graphics::Surface *img = nullptr;
00607     Graphics::Surface *imgScaled = nullptr;
00608     byte *savedPixels = nullptr;
00609     if ((width != srcImage.w) || (height != srcImage.h)) {
00610         // Scale the image
00611         img = imgScaled = srcImage.scale(width, height);
00612         savedPixels = (byte *)img->getPixels();
00613     } else {
00614         img = &srcImage;
00615     }
00616 
00617     // Handle off-screen clipping
00618     if (posY < clippingArea.top) {
00619         img->h = MAX(0, (int)img->h - (clippingArea.top - posY));
00620         if (!(flipping & FLIP_V))
00621             img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY));
00622         posY = clippingArea.top;
00623     }
00624 
00625     if (posX < clippingArea.left) {
00626         img->w = MAX(0, (int)img->w - (clippingArea.left - posX));
00627         if (!(flipping & FLIP_H))
00628             img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0));
00629         posX = clippingArea.left;
00630     }
00631 
00632     if (img->w > clippingArea.right - posX) {
00633         if (flipping & FLIP_H)
00634             img->setPixels((byte *)img->getBasePtr(img->w - clippingArea.right + posX, 0));
00635         img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0));
00636     }
00637 
00638     if (img->h > clippingArea.bottom - posY) {
00639         if (flipping & FLIP_V)
00640             img->setPixels((byte *)img->getBasePtr(0, img->h - clippingArea.bottom + posY));
00641         img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0));
00642     }
00643 
00644     // Flip surface
00645     if ((img->w > 0) && (img->h > 0)) {
00646         int xp = 0, yp = 0;
00647 
00648         int inStep = 4;
00649         int inoStep = img->pitch;
00650         if (flipping & FLIP_H) {
00651             inStep = -inStep;
00652             xp = img->w - 1;
00653         }
00654 
00655         if (flipping & FLIP_V) {
00656             inoStep = -inoStep;
00657             yp = img->h - 1;
00658         }
00659 
00660         byte *ino = (byte *)img->getBasePtr(xp, yp);
00661         byte *outo = (byte *)target.getBasePtr(posX, posY);
00662 
00663         if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_OPAQUE) {
00664             doBlitOpaqueFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
00665         } else if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_BINARY) {
00666             doBlitBinaryFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
00667         } else {
00668             if (blendMode == BLEND_ADDITIVE) {
00669                 doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00670             } else if (blendMode == BLEND_SUBTRACTIVE) {
00671                 doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00672             } else if (blendMode == BLEND_MULTIPLY) {
00673                 doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00674             } else {
00675                 assert(blendMode == BLEND_NORMAL);
00676                 doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
00677             }
00678         }
00679 
00680     }
00681 
00682     retSize.setWidth(img->w);
00683     retSize.setHeight(img->h);
00684 
00685     if (imgScaled) {
00686         imgScaled->setPixels(savedPixels);
00687         imgScaled->free();
00688         delete imgScaled;
00689     }
00690 
00691     return retSize;
00692 }
00693 
00701 void TransparentSurface::applyColorKey(uint8 rKey, uint8 gKey, uint8 bKey, bool overwriteAlpha) {
00702     assert(format.bytesPerPixel == 4);
00703     for (int i = 0; i < h; i++) {
00704         for (int j = 0; j < w; j++) {
00705             uint32 pix = ((uint32 *)pixels)[i * w + j];
00706             uint8 r, g, b, a;
00707             format.colorToARGB(pix, a, r, g, b);
00708             if (r == rKey && g == gKey && b == bKey) {
00709                 a = 0;
00710                 ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b);
00711             } else if (overwriteAlpha) {
00712                 a = 255;
00713                 ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b);
00714             }
00715         }
00716     }
00717 }
00718 
00724 void TransparentSurface::setAlpha(uint8 alpha, bool skipTransparent) {
00725     assert(format.bytesPerPixel == 4);
00726     for (int i = 0; i < h; i++) {
00727         for (int j = 0; j < w; j++) {
00728             uint32 pix = ((uint32 *)pixels)[i * w + j];
00729             uint8 r, g, b, a;
00730             format.colorToARGB(pix, a, r, g, b);
00731             if (!skipTransparent || a)
00732                 a = alpha;
00733             ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b);
00734         }
00735     }
00736 }
00737 
00738 AlphaType TransparentSurface::getAlphaMode() const {
00739     return _alphaMode;
00740 }
00741 
00742 void TransparentSurface::setAlphaMode(AlphaType mode) {
00743     _alphaMode = mode;
00744 }
00745 
00746 
00747 
00748 
00749 
00750 
00751 /*
00752 
00753 The below two functions are adapted from SDL_rotozoom.c,
00754 taken from SDL_gfx-2.0.18.
00755 
00756 Its copyright notice:
00757 
00758 =============================================================================
00759 SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces
00760 
00761 Copyright (C) 2001-2012  Andreas Schiffler
00762 
00763 This software is provided 'as-is', without any express or implied
00764 warranty. In no event will the authors be held liable for any damages
00765 arising from the use of this software.
00766 
00767 Permission is granted to anyone to use this software for any purpose,
00768 including commercial applications, and to alter it and redistribute it
00769 freely, subject to the following restrictions:
00770 
00771 1. The origin of this software must not be misrepresented; you must not
00772 claim that you wrote the original software. If you use this software
00773 in a product, an acknowledgment in the product documentation would be
00774 appreciated but is not required.
00775 
00776 2. Altered source versions must be plainly marked as such, and must not be
00777 misrepresented as being the original software.
00778 
00779 3. This notice may not be removed or altered from any source
00780 distribution.
00781 
00782 Andreas Schiffler -- aschiffler at ferzkopp dot net
00783 =============================================================================
00784 
00785 
00786 The functions have been adapted for different structures and coordinate
00787 systems.
00788 
00789 */
00790 
00791 
00792 
00793 
00794 struct tColorRGBA { byte r; byte g; byte b; byte a; };
00795 
00796 template <TFilteringMode filteringMode>
00797 TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const {
00798 
00799     assert(transform._angle != 0); // This would not be ideal; rotoscale() should never be called in conditional branches where angle = 0 anyway.
00800 
00801     Common::Point newHotspot;
00802     Common::Rect srcRect(0, 0, (int16)w, (int16)h);
00803     Common::Rect rect = TransformTools::newRect(Common::Rect(srcRect), transform, &newHotspot);
00804     Common::Rect dstRect(0, 0, (int16)(rect.right - rect.left), (int16)(rect.bottom - rect.top));
00805 
00806     TransparentSurface *target = new TransparentSurface();
00807     assert(format.bytesPerPixel == 4);
00808 
00809     int srcW = w;
00810     int srcH = h;
00811     int dstW = dstRect.width();
00812     int dstH = dstRect.height();
00813 
00814     target->create((uint16)dstW, (uint16)dstH, this->format);
00815 
00816     if (transform._zoom.x == 0 || transform._zoom.y == 0) {
00817         return target;
00818     }
00819 
00820     uint32 invAngle = 360 - (transform._angle % 360);
00821     float invAngleRad = Common::deg2rad<uint32,float>(invAngle);
00822     float invCos = cos(invAngleRad);
00823     float invSin = sin(invAngleRad);
00824 
00825     int icosx = (int)(invCos * (65536.0f * kDefaultZoomX / transform._zoom.x));
00826     int isinx = (int)(invSin * (65536.0f * kDefaultZoomX / transform._zoom.x));
00827     int icosy = (int)(invCos * (65536.0f * kDefaultZoomY / transform._zoom.y));
00828     int isiny = (int)(invSin * (65536.0f * kDefaultZoomY / transform._zoom.y));
00829 
00830 
00831     bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor
00832 
00833     int xd = (srcRect.left + transform._hotspot.x) << 16;
00834     int yd = (srcRect.top + transform._hotspot.y) << 16;
00835     int cx = newHotspot.x;
00836     int cy = newHotspot.y;
00837 
00838     int ax = -icosx * cx;
00839     int ay = -isiny * cx;
00840     int sw = srcW - 1;
00841     int sh = srcH - 1;
00842 
00843     tColorRGBA *pc = (tColorRGBA*)target->getBasePtr(0, 0);
00844 
00845     for (int y = 0; y < dstH; y++) {
00846         int t = cy - y;
00847         int sdx = ax + (isinx * t) + xd;
00848         int sdy = ay - (icosy * t) + yd;
00849         for (int x = 0; x < dstW; x++) {
00850             int dx = (sdx >> 16);
00851             int dy = (sdy >> 16);
00852             if (flipx) {
00853                 dx = sw - dx;
00854             }
00855             if (flipy) {
00856                 dy = sh - dy;
00857             }
00858 
00859             if (filteringMode == FILTER_BILINEAR) {
00860                 if ((dx > -1) && (dy > -1) && (dx < sw) && (dy < sh)) {
00861                     const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
00862                     tColorRGBA c00, c01, c10, c11, cswap;
00863                     c00 = *sp;
00864                     sp += 1;
00865                     c01 = *sp;
00866                     sp += (this->pitch / 4);
00867                     c11 = *sp;
00868                     sp -= 1;
00869                     c10 = *sp;
00870                     if (flipx) {
00871                         cswap = c00; c00=c01; c01=cswap;
00872                         cswap = c10; c10=c11; c11=cswap;
00873                     }
00874                     if (flipy) {
00875                         cswap = c00; c00=c10; c10=cswap;
00876                         cswap = c01; c01=c11; c11=cswap;
00877                     }
00878                     /*
00879                     * Interpolate colors
00880                     */
00881                     int ex = (sdx & 0xffff);
00882                     int ey = (sdy & 0xffff);
00883                     int t1, t2;
00884                     t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
00885                     t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
00886                     pc->r = (((t2 - t1) * ey) >> 16) + t1;
00887                     t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
00888                     t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
00889                     pc->g = (((t2 - t1) * ey) >> 16) + t1;
00890                     t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
00891                     t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
00892                     pc->b = (((t2 - t1) * ey) >> 16) + t1;
00893                     t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
00894                     t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
00895                     pc->a = (((t2 - t1) * ey) >> 16) + t1;
00896                 }
00897             } else {
00898                 if ((dx >= 0) && (dy >= 0) && (dx < srcW) && (dy < srcH)) {
00899                     const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
00900                     *pc = *sp;
00901                 }
00902             }
00903             sdx += icosx;
00904             sdy += isiny;
00905             pc++;
00906         }
00907     }
00908     return target;
00909 }
00910 
00911 template <TFilteringMode filteringMode>
00912 TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const {
00913 
00914     TransparentSurface *target = new TransparentSurface();
00915 
00916     int srcW = w;
00917     int srcH = h;
00918     int dstW = newWidth;
00919     int dstH = newHeight;
00920 
00921     target->create((uint16)dstW, (uint16)dstH, format);
00922 
00923     if (filteringMode == FILTER_BILINEAR) {
00924         assert(format.bytesPerPixel == 4);
00925 
00926         bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor
00927 
00928 
00929         int *sax = new int[dstW + 1];
00930         int *say = new int[dstH + 1];
00931         assert(sax && say);
00932 
00933         /*
00934         * Precalculate row increments
00935         */
00936         int spixelw = (srcW - 1);
00937         int spixelh = (srcH - 1);
00938         int sx = (int)(65536.0f * (float) spixelw / (float) (dstW - 1));
00939         int sy = (int)(65536.0f * (float) spixelh / (float) (dstH - 1));
00940 
00941         /* Maximum scaled source size */
00942         int ssx = (srcW << 16) - 1;
00943         int ssy = (srcH << 16) - 1;
00944 
00945         /* Precalculate horizontal row increments */
00946         int csx = 0;
00947         int *csax = sax;
00948         for (int x = 0; x <= dstW; x++) {
00949             *csax = csx;
00950             csax++;
00951             csx += sx;
00952 
00953             /* Guard from overflows */
00954             if (csx > ssx) {
00955                 csx = ssx;
00956             }
00957         }
00958 
00959         /* Precalculate vertical row increments */
00960         int csy = 0;
00961         int *csay = say;
00962         for (int y = 0; y <= dstH; y++) {
00963             *csay = csy;
00964             csay++;
00965             csy += sy;
00966 
00967             /* Guard from overflows */
00968             if (csy > ssy) {
00969                 csy = ssy;
00970             }
00971         }
00972 
00973         const tColorRGBA *sp = (const tColorRGBA *) getBasePtr(0, 0);
00974         tColorRGBA *dp = (tColorRGBA *) target->getBasePtr(0, 0);
00975         int spixelgap = srcW;
00976 
00977         if (flipx) {
00978             sp += spixelw;
00979         }
00980         if (flipy) {
00981             sp += spixelgap * spixelh;
00982         }
00983 
00984         csay = say;
00985         for (int y = 0; y < dstH; y++) {
00986             const tColorRGBA *csp = sp;
00987             csax = sax;
00988             for (int x = 0; x < dstW; x++) {
00989                 /*
00990                 * Setup color source pointers
00991                 */
00992                 int ex = (*csax & 0xffff);
00993                 int ey = (*csay & 0xffff);
00994                 int cx = (*csax >> 16);
00995                 int cy = (*csay >> 16);
00996 
00997                 const tColorRGBA *c00, *c01, *c10, *c11;
00998                 c00 = sp;
00999                 c01 = sp;
01000                 c10 = sp;
01001                 if (cy < spixelh) {
01002                     if (flipy) {
01003                         c10 -= spixelgap;
01004                     } else {
01005                         c10 += spixelgap;
01006                     }
01007                 }
01008                 c11 = c10;
01009                 if (cx < spixelw) {
01010                     if (flipx) {
01011                         c01--;
01012                         c11--;
01013                     } else {
01014                         c01++;
01015                         c11++;
01016                     }
01017                 }
01018 
01019                 /*
01020                 * Draw and interpolate colors
01021                 */
01022                 int t1, t2;
01023                 t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
01024                 t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
01025                 dp->r = (((t2 - t1) * ey) >> 16) + t1;
01026                 t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
01027                 t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
01028                 dp->g = (((t2 - t1) * ey) >> 16) + t1;
01029                 t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
01030                 t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
01031                 dp->b = (((t2 - t1) * ey) >> 16) + t1;
01032                 t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
01033                 t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
01034                 dp->a = (((t2 - t1) * ey) >> 16) + t1;
01035 
01036                 /*
01037                 * Advance source pointer x
01038                 */
01039                 int *salastx = csax;
01040                 csax++;
01041                 int sstepx = (*csax >> 16) - (*salastx >> 16);
01042                 if (flipx) {
01043                     sp -= sstepx;
01044                 } else {
01045                     sp += sstepx;
01046                 }
01047 
01048                 /*
01049                 * Advance destination pointer x
01050                 */
01051                 dp++;
01052             }
01053             /*
01054             * Advance source pointer y
01055             */
01056             int *salasty = csay;
01057             csay++;
01058             int sstepy = (*csay >> 16) - (*salasty >> 16);
01059             sstepy *= spixelgap;
01060             if (flipy) {
01061                 sp = csp - sstepy;
01062             } else {
01063                 sp = csp + sstepy;
01064             }
01065         }
01066 
01067         delete[] sax;
01068         delete[] say;
01069 
01070     } else {
01071         int *scaleCacheX = new int[dstW];
01072         for (int x = 0; x < dstW; x++) {
01073             scaleCacheX[x] = (x * srcW) / dstW;
01074         }
01075 
01076         switch (format.bytesPerPixel) {
01077         case 1:
01078             scaleNN<uint8>(scaleCacheX, target);
01079             break;
01080         case 2:
01081             scaleNN<uint16>(scaleCacheX, target);
01082             break;
01083         case 4:
01084             scaleNN<uint32>(scaleCacheX, target);
01085             break;
01086         default:
01087             error("Can only scale 8bpp, 16bpp, and 32bpp");
01088         }
01089 
01090         delete[] scaleCacheX;
01091     }
01092 
01093     return target;
01094 }
01095 
01096 TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
01097     assert(pixels);
01098 
01099     TransparentSurface *surface = new TransparentSurface();
01100 
01101     // If the target format is the same, just copy
01102     if (format == dstFormat) {
01103         surface->copyFrom(*this);
01104         return surface;
01105     }
01106 
01107     if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
01108         error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
01109 
01110     if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
01111         error("Surface::convertTo(): Can only convert to 2Bpp and 4Bpp");
01112 
01113     surface->create(w, h, dstFormat);
01114 
01115     if (format.bytesPerPixel == 1) {
01116         // Converting from paletted to high color
01117         assert(palette);
01118 
01119         for (int y = 0; y < h; y++) {
01120             const byte *srcRow = (const byte *)getBasePtr(0, y);
01121             byte *dstRow = (byte *)surface->getBasePtr(0, y);
01122 
01123             for (int x = 0; x < w; x++) {
01124                 byte index = *srcRow++;
01125                 byte r = palette[index * 3];
01126                 byte g = palette[index * 3 + 1];
01127                 byte b = palette[index * 3 + 2];
01128 
01129                 uint32 color = dstFormat.RGBToColor(r, g, b);
01130 
01131                 if (dstFormat.bytesPerPixel == 2)
01132                     *((uint16 *)dstRow) = color;
01133                 else
01134                     *((uint32 *)dstRow) = color;
01135 
01136                 dstRow += dstFormat.bytesPerPixel;
01137             }
01138         }
01139     } else {
01140         // Converting from high color to high color
01141         for (int y = 0; y < h; y++) {
01142             const byte *srcRow = (const byte *)getBasePtr(0, y);
01143             byte *dstRow = (byte *)surface->getBasePtr(0, y);
01144 
01145             for (int x = 0; x < w; x++) {
01146                 uint32 srcColor;
01147                 if (format.bytesPerPixel == 2)
01148                     srcColor = READ_UINT16(srcRow);
01149                 else if (format.bytesPerPixel == 3)
01150                     srcColor = READ_UINT24(srcRow);
01151                 else
01152                     srcColor = READ_UINT32(srcRow);
01153 
01154                 srcRow += format.bytesPerPixel;
01155 
01156                 // Convert that color to the new format
01157                 byte r, g, b, a;
01158                 format.colorToARGB(srcColor, a, r, g, b);
01159                 uint32 color = dstFormat.ARGBToColor(a, r, g, b);
01160 
01161                 if (dstFormat.bytesPerPixel == 2)
01162                     *((uint16 *)dstRow) = color;
01163                 else
01164                     *((uint32 *)dstRow) = color;
01165 
01166                 dstRow += dstFormat.bytesPerPixel;
01167             }
01168         }
01169     }
01170 
01171     return surface;
01172 }
01173 
01174 template <typename Size>
01175 void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const {
01176     for (int y = 0; y < target->h; y++) {
01177         Size *destP = (Size *)target->getBasePtr(0, y);
01178         const Size *srcP = (const Size *)getBasePtr(0, (y * h) / target->h);
01179         for (int x = 0; x < target->w; x++) {
01180             *destP++ = srcP[scaleCacheX[x]];
01181         }
01182     }
01183 }
01184 
01185 template TransparentSurface *TransparentSurface::rotoscaleT<FILTER_NEAREST>(const TransformStruct &transform) const;
01186 template TransparentSurface *TransparentSurface::rotoscaleT<FILTER_BILINEAR>(const TransformStruct &transform) const;
01187 template TransparentSurface *TransparentSurface::scaleT<FILTER_NEAREST>(uint16 newWidth, uint16 newHeight) const;
01188 template TransparentSurface *TransparentSurface::scaleT<FILTER_BILINEAR>(uint16 newWidth, uint16 newHeight) const;
01189 
01190 template void TransparentSurface::scaleNN<uint8>(int *scaleCacheX, TransparentSurface *target) const;
01191 template void TransparentSurface::scaleNN<uint16>(int *scaleCacheX, TransparentSurface *target) const;
01192 template void TransparentSurface::scaleNN<uint32>(int *scaleCacheX, TransparentSurface *target) const;
01193 
01194 TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transform) const {
01195     return rotoscaleT<FILTER_BILINEAR>(transform);
01196 }
01197 
01198 TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const {
01199     return scaleT<FILTER_NEAREST>(newWidth, newHeight);
01200 }
01201 
01202 } // End of namespace Graphics


Generated on Sat Aug 8 2020 05:01:24 for ResidualVM by doxygen 1.7.1
curved edge   curved edge