aboutsummaryrefslogtreecommitdiff
path: root/engines/wintermute/graphics/transparent_surface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/wintermute/graphics/transparent_surface.cpp')
-rw-r--r--engines/wintermute/graphics/transparent_surface.cpp181
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;