diff options
Diffstat (limited to 'engines/wintermute/graphics/transparent_surface.cpp')
-rw-r--r-- | engines/wintermute/graphics/transparent_surface.cpp | 181 |
1 files changed, 169 insertions, 12 deletions
diff --git a/engines/wintermute/graphics/transparent_surface.cpp b/engines/wintermute/graphics/transparent_surface.cpp index dcdcbf247e..5b7c416ee9 100644 --- a/engines/wintermute/graphics/transparent_surface.cpp +++ b/engines/wintermute/graphics/transparent_surface.cpp @@ -23,12 +23,124 @@ #include "common/endian.h" #include "common/util.h" #include "common/rect.h" +#include "common/math.h" #include "common/textconsole.h" #include "graphics/primitives.h" #include "engines/wintermute/graphics/transparent_surface.h" +#include "engines/wintermute/graphics/transform_tools.h" namespace Wintermute { + +#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) { + + 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) { + 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 + byte *TransparentSurface::_lookup = nullptr; void TransparentSurface::destroyLookup() { @@ -250,12 +362,12 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p int inStep = 4; int inoStep = img->pitch; - if (flipping & TransparentSurface::FLIP_V) { + if (flipping & TransparentSurface::FLIP_H) { inStep = -inStep; xp = img->w - 1; } - if (flipping & TransparentSurface::FLIP_H) { + if (flipping & TransparentSurface::FLIP_V) { inoStep = -inoStep; yp = img->h - 1; } @@ -383,16 +495,54 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p return retSize; } -TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const { +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. + + Point32 newHotspot; Common::Rect srcRect(0, 0, (int16)w, (int16)h); - Common::Rect dstRect(0, 0, (int16)newWidth, (int16)newHeight); - return scale(srcRect, dstRect); + Rect32 rect = TransformTools::newRect(Rect32 (srcRect), transform, &newHotspot); + Common::Rect dstRect(0, 0, (int16)(rect.right - rect.left), (int16)(rect.bottom - rect.top)); + + TransparentSurface *target = new TransparentSurface(); + assert(format.bytesPerPixel == 4); + + int dstW = dstRect.width(); + int dstH = dstRect.height(); + + target->create((uint16)dstW, (uint16)dstH, this->format); + + 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; + + targX = ((x1 * invCos - y1 * invSin)) * kDefaultZoomX / transform._zoom.x + srcRect.left; + targY = ((x1 * invSin + y1 * invCos)) * kDefaultZoomY / transform._zoom.y + srcRect.top; + + targX += transform._hotspot.x; + targY += transform._hotspot.y; + +#if ENABLE_BILINEAR + copyPixelBilinear(targX, targY, x, y, srcRect, dstRect, this, target); +#else + copyPixelNearestNeighbor(targX, targY, x, y, srcRect, dstRect, this, target); +#endif + } + } + return target; } -// Copied from clone2727's https://github.com/clone2727/scummvm/blob/pegasus/engines/pegasus/surface.cpp#L247 -TransparentSurface *TransparentSurface::scale(const Common::Rect &srcRect, const Common::Rect &dstRect) const { - // I'm doing simple linear scaling here - // dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH); +TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const { + Common::Rect srcRect(0, 0, (int16)w, (int16)h); + Common::Rect dstRect(0, 0, (int16)newWidth, (int16)newHeight); + TransparentSurface *target = new TransparentSurface(); assert(format.bytesPerPixel == 4); @@ -404,11 +554,18 @@ TransparentSurface *TransparentSurface::scale(const Common::Rect &srcRect, const target->create((uint16)dstW, (uint16)dstH, this->format); + + float projX; + float projY; for (int y = 0; y < dstH; y++) { for (int x = 0; x < dstW; x++) { - uint32 color = READ_UINT32((const byte *)getBasePtr(x * srcW / dstW + srcRect.left, - y * srcH / dstH + srcRect.top)); - WRITE_UINT32((byte *)target->getBasePtr(x + dstRect.left, y + dstRect.top), color); + projX = x / (float)dstW * srcW; + projY = y / (float)dstH * srcH; +#if ENABLE_BILINEAR + copyPixelBilinear(projX, projY, x, y, srcRect, dstRect, this, target); +#else + copyPixelNearestNeighbor(projX, projY, x, y, srcRect, dstRect, this, target); +#endif } } return target; |