diff options
-rw-r--r-- | engines/wintermute/graphics/transparent_surface.cpp | 475 | ||||
-rw-r--r-- | engines/wintermute/graphics/transparent_surface.h | 28 |
2 files changed, 315 insertions, 188 deletions
diff --git a/engines/wintermute/graphics/transparent_surface.cpp b/engines/wintermute/graphics/transparent_surface.cpp index f5528b1155..43deb62db6 100644 --- a/engines/wintermute/graphics/transparent_surface.cpp +++ b/engines/wintermute/graphics/transparent_surface.cpp @@ -17,8 +17,16 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + * The bottom part of this is file is adapted from SDL_rotozoom.c. The + * relevant copyright notice for those specific functions can be found at the + * top of that section. + * */ + + #include "common/algorithm.h" #include "common/endian.h" #include "common/util.h" @@ -29,6 +37,8 @@ #include "engines/wintermute/graphics/transparent_surface.h" #include "engines/wintermute/graphics/transform_tools.h" +//#define ENABLE_BILINEAR + namespace Wintermute { void doBlitOpaqueFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep); @@ -269,119 +279,6 @@ void BlenderAdditive::blendPixel(byte ina, byte inr, byte ing, byte inb, byte *o } } -#if ENABLE_BILINEAR -void TransparentSurface::copyPixelBilinear(float projX, float projY, int dstX, int dstY, const Common::Rect &srcRect, const Common::Rect &dstRect, const TransparentSurface *src, TransparentSurface *dst) { - - // TODO: Do some optimization on this. This is completely naive. - - int srcW = srcRect.width(); - int srcH = srcRect.height(); - int dstW = dstRect.width(); - int dstH = dstRect.height(); - - assert(dstX >= 0 && dstX < dstW); - assert(dstY >= 0 && dstY < dstH); - - float x1 = floor(projX); - float x2 = ceil(projX); - float y1 = floor(projY); - float y2 = ceil(projY); - - uint32 Q11, Q12, Q21, Q22; - - if (x1 >= srcW || x1 < 0 || y1 >= srcH || y1 < 0) { - Q11 = 0; - } else { - Q11 = READ_UINT32((const byte *)src->getBasePtr((int)(x1 + srcRect.left), (int)(y1 + srcRect.top))); - } - - if (x1 >= srcW || x1 < 0 || y2 >= srcH || y2 < 0) { - Q12 = 0; - } else { - Q12 = READ_UINT32((const byte *)src->getBasePtr((int)(x1 + srcRect.left), (int)(y2 + srcRect.top))); - } - - if (x2 >= srcW || x2 < 0 || y1 >= srcH || y1 < 0) { - Q21 = 0; - } else { - Q21 = READ_UINT32((const byte *)src->getBasePtr((int)(x2 + srcRect.left), (int)(y1 + srcRect.top))); - } - - if (x2 >= srcW || x2 < 0 || y2 >= srcH || y2 < 0) { - Q22 = 0; - } else { - Q22 = READ_UINT32((const byte *)src->getBasePtr((int)(x2 + srcRect.left), (int)(y2 + srcRect.top))); - } - - byte *Q11s = (byte *)&Q11; - byte *Q12s = (byte *)&Q12; - byte *Q21s = (byte *)&Q21; - byte *Q22s = (byte *)&Q22; - - uint32 color; - byte *dest = (byte *)&color; - - float q11x = (x2 - projX); - float q11y = (y2 - projY); - float q21x = (projX - x1); - float q21y = (y2 - projY); - float q12x = (x2 - projX); - float q12y = (projY - y1); - - if (x1 == x2 && y1 == y2) { - for (int c = 0; c < 4; c++) { - dest[c] = ((float)Q11s[c]); - } - } else { - - if (x1 == x2) { - q11x = 0.5; - q12x = 0.5; - q21x = 0.5; - } else if (y1 == y2) { - q11y = 0.5; - q12y = 0.5; - q21y = 0.5; - } - - for (int c = 0; c < 4; c++) { - dest[c] = (byte)( - ((float)Q11s[c]) * q11x * q11y + - ((float)Q21s[c]) * q21x * q21y + - ((float)Q12s[c]) * q12x * q12y + - ((float)Q22s[c]) * (1.0 - - q11x * q11y - - q21x * q21y - - q12x * q12y) - ); - } - } - WRITE_UINT32((byte *)dst->getBasePtr(dstX + dstRect.left, dstY + dstRect.top), color); -} -#else -void TransparentSurface::copyPixelNearestNeighbor(float projX, float projY, int dstX, int dstY, const Common::Rect &srcRect, const Common::Rect &dstRect, const TransparentSurface *src, TransparentSurface *dst) { - - // TODO: Have the Rect arguments become completely useless at this point? - - int srcW = srcRect.width(); - int srcH = srcRect.height(); - int dstW = dstRect.width(); - int dstH = dstRect.height(); - - assert(dstX >= 0 && dstX < dstW); - assert(dstY >= 0 && dstY < dstH); - - uint32 color; - - if (projX >= srcW || projX < 0 || projY >= srcH || projY < 0) { - color = 0; - } else { - color = READ_UINT32((const byte *)src->getBasePtr((int)projX, (int)projY)); - } - - WRITE_UINT32((byte *)dst->getBasePtr(dstX, dstY), color); -} -#endif TransparentSurface::TransparentSurface() : Surface(), _alphaMode(ALPHA_FULL) {} @@ -655,6 +552,88 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p return retSize; } +/** + * Writes a color key to the alpha channel of the surface + * @param rKey the red component of the color key + * @param gKey the green component of the color key + * @param bKey the blue component of the color key + * @param overwriteAlpha if true, all other alpha will be set fully opaque + */ +void TransparentSurface::applyColorKey(uint8 rKey, uint8 gKey, uint8 bKey, bool overwriteAlpha) { + assert(format.bytesPerPixel == 4); + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + uint32 pix = ((uint32 *)pixels)[i * w + j]; + uint8 r, g, b, a; + format.colorToARGB(pix, a, r, g, b); + if (r == rKey && g == gKey && b == bKey) { + a = 0; + ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); + } else if (overwriteAlpha) { + a = 255; + ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); + } + } + } +} + +TransparentSurface::AlphaType TransparentSurface::getAlphaMode() const { + return _alphaMode; +} + +void TransparentSurface::setAlphaMode(TransparentSurface::AlphaType mode) { + _alphaMode = mode; +} + + + + + + +/* + +The below two functions are adapted from SDL_rotozoom.c, +taken from SDL_gfx-2.0.18. + +Its copyright notice: + +============================================================================= +SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces + +Copyright (C) 2001-2012 Andreas Schiffler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +Andreas Schiffler -- aschiffler at ferzkopp dot net +============================================================================= + + +The functions have been adapted for different structures and coordinate +systems. + +*/ + + + + + TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transform) const { assert(transform._angle != 0); // This would not be ideal; rotoscale() should never be called in conditional branches where angle = 0 anyway. @@ -667,33 +646,98 @@ TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transfo TransparentSurface *target = new TransparentSurface(); assert(format.bytesPerPixel == 4); + int srcW = w; + int srcH = h; int dstW = dstRect.width(); int dstH = dstRect.height(); target->create((uint16)dstW, (uint16)dstH, this->format); + if (transform._zoom.x == 0 || transform._zoom.y == 0) + return target; + uint32 invAngle = 360 - (transform._angle % 360); float invCos = cos(invAngle * M_PI / 180.0); float invSin = sin(invAngle * M_PI / 180.0); - float targX; - float targY; - for (int y = 0; y < dstH; y++) { - for (int x = 0; x < dstW; x++) { - int x1 = x - newHotspot.x; - int y1 = y - newHotspot.y; + struct tColorRGBA { byte r; byte g; byte b; byte a; }; + int icosx = (int)(invCos * (65536.0f * kDefaultZoomX / transform._zoom.x)); + int isinx = (int)(invSin * (65536.0f * kDefaultZoomX / transform._zoom.x)); + int icosy = (int)(invCos * (65536.0f * kDefaultZoomY / transform._zoom.y)); + int isiny = (int)(invSin * (65536.0f * kDefaultZoomY / transform._zoom.y)); + - targX = ((x1 * invCos - y1 * invSin)) * kDefaultZoomX / transform._zoom.x + srcRect.left; - targY = ((x1 * invSin + y1 * invCos)) * kDefaultZoomY / transform._zoom.y + srcRect.top; + bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor - targX += transform._hotspot.x; - targY += transform._hotspot.y; + int xd = (srcRect.left + transform._hotspot.x) << 16; + int yd = (srcRect.top + transform._hotspot.y) << 16; + int cx = newHotspot.x; + int cy = newHotspot.y; -#if ENABLE_BILINEAR - copyPixelBilinear(targX, targY, x, y, srcRect, dstRect, this, target); + int ax = -icosx * cx; + int ay = -isiny * cx; + int sw = srcW - 1; + int sh = srcH - 1; + + tColorRGBA *pc = (tColorRGBA*)target->getBasePtr(0, 0); + + for (int y = 0; y < dstH; y++) { + int t = cy - y; + int sdx = ax + (isinx * t) + xd; + int sdy = ay - (icosy * t) + yd; + for (int x = 0; x < dstW; x++) { + int dx = (sdx >> 16); + int dy = (sdy >> 16); + if (flipx) dx = sw - dx; + if (flipy) dy = sh - dy; + +#ifdef ENABLE_BILINEAR + if ((dx > -1) && (dy > -1) && (dx < sw) && (dy < sh)) { + const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy); + tColorRGBA c00, c01, c10, c11, cswap; + c00 = *sp; + sp += 1; + c01 = *sp; + sp += (this->pitch/4); + c11 = *sp; + sp -= 1; + c10 = *sp; + if (flipx) { + cswap = c00; c00=c01; c01=cswap; + cswap = c10; c10=c11; c11=cswap; + } + if (flipy) { + cswap = c00; c00=c10; c10=cswap; + cswap = c01; c01=c11; c11=cswap; + } + /* + * Interpolate colors + */ + int ex = (sdx & 0xffff); + int ey = (sdy & 0xffff); + int t1, t2; + t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; + t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; + pc->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; + t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; + pc->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; + t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; + pc->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; + t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; + pc->a = (((t2 - t1) * ey) >> 16) + t1; + } #else - copyPixelNearestNeighbor(targX, targY, x, y, srcRect, dstRect, this, target); + if ((dx >= 0) && (dy >= 0) && (dx < srcW) && (dy < srcH)) { + const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy); + *pc = *sp; + } #endif + sdx += icosx; + sdy += isiny; + pc++; } } return target; @@ -715,15 +759,153 @@ TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) target->create((uint16)dstW, (uint16)dstH, this->format); -#if ENABLE_BILINEAR +#ifdef ENABLE_BILINEAR + + // NB: The actual order of these bytes may not be correct, but + // since all values are treated equal, that does not matter. + struct tColorRGBA { byte r; byte g; byte b; byte a; }; + + bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor + + + int *sax = new int[dstW+1]; + int *say = new int[dstH+1]; + assert(sax && say); + + /* + * Precalculate row increments + */ + int spixelw = (srcW - 1); + int spixelh = (srcH - 1); + int sx = (int) (65536.0f * (float) spixelw / (float) (dstW - 1)); + int sy = (int) (65536.0f * (float) spixelh / (float) (dstH - 1)); + + /* Maximum scaled source size */ + int ssx = (srcW << 16) - 1; + int ssy = (srcH << 16) - 1; + + /* Precalculate horizontal row increments */ + int csx = 0; + int *csax = sax; + for (int x = 0; x <= dstW; x++) { + *csax = csx; + csax++; + csx += sx; + + /* Guard from overflows */ + if (csx > ssx) { + csx = ssx; + } + } + + /* Precalculate vertical row increments */ + int csy = 0; + int *csay = say; + for (int y = 0; y <= dstH; y++) { + *csay = csy; + csay++; + csy += sy; + + /* Guard from overflows */ + if (csy > ssy) { + csy = ssy; + } + } + + const tColorRGBA *sp = (const tColorRGBA *) getBasePtr(0,0); + tColorRGBA *dp = (tColorRGBA *) target->getBasePtr(0,0); + int spixelgap = srcW; + + if (flipx) + sp += spixelw; + if (flipy) + sp += spixelgap * spixelh; + + csay = say; for (int y = 0; y < dstH; y++) { - float projY = y / (float)dstH * srcH; + const tColorRGBA *csp = sp; + csax = sax; for (int x = 0; x < dstW; x++) { - float projX = x / (float)dstW * srcW; - copyPixelBilinear(projX, projY, x, y, srcRect, dstRect, this, target); + /* + * Setup color source pointers + */ + int ex = (*csax & 0xffff); + int ey = (*csay & 0xffff); + int cx = (*csax >> 16); + int cy = (*csay >> 16); + + const tColorRGBA *c00, *c01, *c10, *c11; + c00 = sp; + c01 = sp; + c10 = sp; + if (cy < spixelh) { + if (flipy) + c10 -= spixelgap; + else + c10 += spixelgap; + } + c11 = c10; + if (cx < spixelw) { + if (flipx) { + c01--; + c11--; + } else { + c01++; + c11++; + } + } + + /* + * Draw and interpolate colors + */ + int t1, t2; + t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; + t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; + dp->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; + t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; + dp->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; + t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; + dp->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; + t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; + dp->a = (((t2 - t1) * ey) >> 16) + t1; + + /* + * Advance source pointer x + */ + int *salastx = csax; + csax++; + int sstepx = (*csax >> 16) - (*salastx >> 16); + if (flipx) + sp -= sstepx; + else + sp += sstepx; + + /* + * Advance destination pointer x + */ + dp++; } + /* + * Advance source pointer y + */ + int *salasty = csay; + csay++; + int sstepy = (*csay >> 16) - (*salasty >> 16); + sstepy *= spixelgap; + if (flipy) + sp = csp - sstepy; + else + sp = csp + sstepy; } + + delete[] sax; + delete[] say; + #else + int *scaleCacheX = new int[dstW]; for (int x = 0; x < dstW; x++) scaleCacheX[x] = (x * srcW) / dstW; @@ -735,42 +917,15 @@ TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) *destP++ = srcP[scaleCacheX[x]]; } delete[] scaleCacheX; + #endif return target; } -/** - * Writes a color key to the alpha channel of the surface - * @param rKey the red component of the color key - * @param gKey the green component of the color key - * @param bKey the blue component of the color key - * @param overwriteAlpha if true, all other alpha will be set fully opaque - */ -void TransparentSurface::applyColorKey(uint8 rKey, uint8 gKey, uint8 bKey, bool overwriteAlpha) { - assert(format.bytesPerPixel == 4); - for (int i = 0; i < h; i++) { - for (int j = 0; j < w; j++) { - uint32 pix = ((uint32 *)pixels)[i * w + j]; - uint8 r, g, b, a; - format.colorToARGB(pix, a, r, g, b); - if (r == rKey && g == gKey && b == bKey) { - a = 0; - ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); - } else if (overwriteAlpha) { - a = 255; - ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); - } - } - } -} -TransparentSurface::AlphaType TransparentSurface::getAlphaMode() const { - return _alphaMode; -} -void TransparentSurface::setAlphaMode(TransparentSurface::AlphaType mode) { - _alphaMode = mode; -} + + } // End of namespace Wintermute diff --git a/engines/wintermute/graphics/transparent_surface.h b/engines/wintermute/graphics/transparent_surface.h index 5f44cf0c4c..b887c05fa8 100644 --- a/engines/wintermute/graphics/transparent_surface.h +++ b/engines/wintermute/graphics/transparent_surface.h @@ -25,9 +25,6 @@ #include "graphics/surface.h" #include "engines/wintermute/graphics/transform_struct.h" -#define ENABLE_BILINEAR 0 - - /* * This code is based on Broken Sword 2.5 engine * @@ -53,31 +50,6 @@ struct TransparentSurface : public Graphics::Surface { void setColorKey(char r, char g, char b); void disableColorKey(); -#if ENABLE_BILINEAR - /* - * Pick color from a point in source and copy it to a pixel in target. - * The point in the source can be a float - we have subpixel accuracy in the arguments. - * We do bilinear interpolation to estimate the color of the point even if the - * point is specuified w/subpixel accuracy. - * - * @param projX, projY, point in the source to pick color from. - * @param dstX, dstY destionation pixel - * @param *src, *dst pointer to the source and dest surfaces - */ - static void copyPixelBilinear(float projX, float projY, int dstX, int dstY, const Common::Rect &srcRect, const Common::Rect &dstRect, const TransparentSurface *src, TransparentSurface *dst); -#else - /* - * Pick color from a point in source and copy it to a pixel in target. - * The point in the source can be a float - we have subpixel accuracy in the arguments. - * HOWEVER, this particular function just does nearest neighbor. - * Use copyPixelBilinear if you interpolation. - * - * @param projX, projY, point in the source to pick color from. - * @param dstX, dstY destionation pixel - * @param *src, *dst pointer to the source and dest surfaces - */ - static void copyPixelNearestNeighbor(float projX, float projY, int dstX, int dstY, const Common::Rect &srcRect, const Common::Rect &dstRect, const TransparentSurface *src, TransparentSurface *dst); -#endif // Enums /** @brief The possible flipping parameters for the blit methode. |