mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-26 04:35:16 +00:00
951 lines
26 KiB
C++
951 lines
26 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* 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"
|
|
#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"
|
|
|
|
//#define ENABLE_BILINEAR
|
|
|
|
namespace Wintermute {
|
|
|
|
void doBlitOpaqueFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep);
|
|
void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep);
|
|
|
|
// These gather together various blendPixel functions for use with templates.
|
|
|
|
class BlenderAdditive {
|
|
public:
|
|
inline void blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb);
|
|
inline void blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb, byte *ca, byte *cr, byte *cg, byte *cb);
|
|
inline void blendPixel(byte *in, byte *out);
|
|
inline void blendPixel(byte *in, byte *out, int colorMod);
|
|
};
|
|
|
|
class BlenderSubtractive {
|
|
public:
|
|
inline void blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb);
|
|
inline void blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb, byte *ca, byte *cr, byte *cg, byte *cb);
|
|
inline void blendPixel(byte *in, byte *out);
|
|
inline void blendPixel(byte *in, byte *out, int colorMod);
|
|
};
|
|
|
|
class BlenderNormal {
|
|
public:
|
|
inline void blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb);
|
|
inline void blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb, byte *ca, byte *cr, byte *cg, byte *cb);
|
|
inline void blendPixel(byte *in, byte *out);
|
|
inline void blendPixel(byte *in, byte *out, int colorMod);
|
|
};
|
|
|
|
/**
|
|
* Perform additive blending of a pixel, applying beforehand a given colormod.
|
|
* @param ina, inr, ing, inb: the input pixel, split into its components.
|
|
* @param *outa, *outr, *outg, *outb pointer to the output pixel.
|
|
* @param *outa, *outr, *outg, *outb pointer to the colormod components.
|
|
*/
|
|
void BlenderAdditive::blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb, byte *ca, byte *cr, byte *cg, byte *cb) {
|
|
if (*ca != 255) {
|
|
ina = (ina) * (*ca) >> 8;
|
|
}
|
|
|
|
if (ina == 0) {
|
|
return;
|
|
} else {
|
|
if (*cb != 255) {
|
|
*outb = MIN(*outb + ((inb * (*cb) * ina) >> 16), 255);
|
|
} else {
|
|
*outb = MIN(*outb + (inb * ina >> 8), 255);
|
|
}
|
|
|
|
if (*cg != 255) {
|
|
*outg = MIN(*outg + ((ing * (*cg) * ina) >> 16), 255);
|
|
} else {
|
|
*outg = MIN(*outg + (ing * ina >> 8), 255);
|
|
}
|
|
|
|
if (*cr != 255) {
|
|
*outr = MIN(*outr + ((inr * (*cr) * ina) >> 16), 255);
|
|
} else {
|
|
*outr = MIN(*outr + (inr * ina >> 8), 255);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform subtractive blending of a pixel, applying beforehand a given colormod.
|
|
* @param ina, inr, ing, inb: the input pixel, split into its components.
|
|
* @param *outa, *outr, *outg, *outb pointer to the output pixel.
|
|
* @param *outa, *outr, *outg, *outb pointer to the colormod components.
|
|
*/
|
|
void BlenderSubtractive::blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb, byte *ca, byte *cr, byte *cg, byte *cb) {
|
|
//if (*ca != 255) {
|
|
// ina = ina * (*ca) >> 8;
|
|
// }
|
|
|
|
// As weird as it is, evidence suggests that alphamod is ignored when doing
|
|
// subtractive...
|
|
|
|
// TODO if ina == 255 fast version
|
|
|
|
if (ina == 0) {
|
|
return;
|
|
} else {
|
|
if (*cb != 255) {
|
|
*outb = MAX(*outb - ((inb * (*cb) * (*outb) * ina) >> 24), 0);
|
|
} else {
|
|
*outb = MAX(*outb - (inb * (*outb) * ina >> 16), 0);
|
|
}
|
|
|
|
if (*cg != 255) {
|
|
*outg = MAX(*outg - ((ing * (*cg) * (*outg) * ina) >> 24), 0);
|
|
} else {
|
|
*outg = MAX(*outg - (ing * (*outg) * ina >> 16), 0);
|
|
}
|
|
|
|
if (*cr != 255) {
|
|
*outr = MAX(*outr - ((inr * (*cr) * (*outr) * ina) >> 24), 0);
|
|
} else {
|
|
*outr = MAX(*outr - (inr * (*outr) * ina >> 16), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform "regular" alphablending of a pixel, applying beforehand a given colormod.
|
|
* @param ina, inr, ing, inb: the input pixel, split into its components.
|
|
* @param *outa, *outr, *outg, *outb pointer to the output pixel.
|
|
* @param *outa, *outr, *outg, *outb pointer to the colormod components.
|
|
*/
|
|
|
|
void BlenderNormal::blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb, byte *ca, byte *cr, byte *cg, byte *cb) {
|
|
if (*ca != 255) {
|
|
ina = ina * (*ca) >> 8;
|
|
}
|
|
|
|
if (ina == 0) {
|
|
return;
|
|
} else if (ina == 255) {
|
|
if (*cb != 255) {
|
|
*outb = (inb * (*cb)) >> 8;
|
|
} else {
|
|
*outb = inb;
|
|
}
|
|
|
|
if (*cr != 255) {
|
|
*outr = (inr * (*cr)) >> 8;
|
|
} else {
|
|
*outr = inr;
|
|
}
|
|
|
|
if (*cg != 255) {
|
|
*outg = (ing * (*cg)) >> 8;
|
|
} else {
|
|
*outg = ing;
|
|
}
|
|
|
|
*outa = ina;
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
*outa = 255;
|
|
*outb = (*outb * (255 - ina) >> 8);
|
|
*outr = (*outr * (255 - ina) >> 8);
|
|
*outg = (*outg * (255 - ina) >> 8);
|
|
|
|
if (*cb == 0) {
|
|
*outb = *outb;
|
|
} else if (*cb != 255) {
|
|
*outb = *outb + (inb * ina * (*cb) >> 16);
|
|
} else {
|
|
*outb = *outb + (inb * ina >> 8);
|
|
}
|
|
|
|
if (*cr == 0) {
|
|
*outr = *outr;
|
|
} else if (*cr != 255) {
|
|
*outr = *outr + (inr * ina * (*cr) >> 16);
|
|
} else {
|
|
*outr = *outr + (inr * ina >> 8);
|
|
}
|
|
|
|
if (*cg == 0) {
|
|
*outg = *outg;
|
|
} else if (*cg != 255) {
|
|
*outg = *outg + (ing * ina * (*cg) >> 16);
|
|
} else {
|
|
*outg = *outg + (ing * ina >> 8);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform "regular" alphablending of a pixel.
|
|
* @param ina, inr, ing, inb: the input pixel, split into its components.
|
|
* @param *outa, *outr, *outg, *outb pointer to the output pixel.
|
|
*/
|
|
|
|
void BlenderNormal::blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb) {
|
|
|
|
if (ina == 0) {
|
|
return;
|
|
} else if (ina == 255) {
|
|
*outb = inb;
|
|
*outg = ing;
|
|
*outr = inr;
|
|
*outa = ina;
|
|
return;
|
|
} else {
|
|
*outa = 255;
|
|
*outb = ((inb * ina) + *outb * (255 - ina)) >> 8;
|
|
*outg = ((ing * ina) + *outg * (255 - ina)) >> 8;
|
|
*outr = ((inr * ina) + *outr * (255 - ina)) >> 8;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform subtractive blending of a pixel.
|
|
* @param ina, inr, ing, inb: the input pixel, split into its components.
|
|
* @param *outa, *outr, *outg, *outb pointer to the output pixel.
|
|
*/
|
|
void BlenderSubtractive::blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb) {
|
|
|
|
if (ina == 0) {
|
|
return;
|
|
} else if (ina == 255) {
|
|
*outa = *outa;
|
|
*outr = *outr - (inr * (*outr) >> 8);
|
|
*outg = *outg - (ing * (*outg) >> 8);
|
|
*outb = *outb - (inb * (*outb) >> 8);
|
|
return;
|
|
} else {
|
|
*outa = *outa;
|
|
*outb = MAX(*outb - ((inb * (*outb)) * ina >> 16), 0);
|
|
*outg = MAX(*outg - ((ing * (*outg)) * ina >> 16), 0);
|
|
*outr = MAX(*outr - ((inr * (*outr)) * ina >> 16), 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform additive blending of a pixel.
|
|
* @param ina, inr, ing, inb: the input pixel, split into its components.
|
|
* @param *outa, *outr, *outg, *outb pointer to the output pixel.
|
|
*/
|
|
void BlenderAdditive::blendPixel(byte ina, byte inr, byte ing, byte inb, byte *outa, byte *outr, byte *outg, byte *outb) {
|
|
|
|
if (ina == 0) {
|
|
return;
|
|
} else if (ina == 255) {
|
|
*outa = *outa;
|
|
*outr = MIN(*outr + inr, 255);
|
|
*outg = MIN(*outg + ing, 255);
|
|
*outb = MIN(*outb + inb, 255);
|
|
return;
|
|
} else {
|
|
*outa = *outa;
|
|
*outb = MIN((inb * ina >> 8) + *outb, 255);
|
|
*outg = MIN((ing * ina >> 8) + *outg, 255);
|
|
*outr = MIN((inr * ina >> 8) + *outr, 255);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
TransparentSurface::TransparentSurface() : Surface(), _alphaMode(ALPHA_FULL) {}
|
|
|
|
TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _alphaMode(ALPHA_FULL) {
|
|
if (copyData) {
|
|
copyFrom(surf);
|
|
} else {
|
|
w = surf.w;
|
|
h = surf.h;
|
|
pitch = surf.pitch;
|
|
format = surf.format;
|
|
// We need to cast the const qualifier away here because 'pixels'
|
|
// always needs to be writable. 'surf' however is a constant Surface,
|
|
// thus getPixels will always return const pixel data.
|
|
pixels = const_cast<void *>(surf.getPixels());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Optimized version of doBlit to be used w/opaque blitting (no alpha).
|
|
*/
|
|
void doBlitOpaqueFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
|
|
|
|
byte *in;
|
|
byte *out;
|
|
|
|
for (uint32 i = 0; i < height; i++) {
|
|
out = outo;
|
|
in = ino;
|
|
memcpy(out, in, width * 4);
|
|
for (uint32 j = 0; j < width; j++) {
|
|
out[TransparentSurface::kAIndex] = 0xFF;
|
|
out += 4;
|
|
}
|
|
outo += pitch;
|
|
ino += inoStep;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Optimized version of doBlit to be used w/binary blitting (blit or no-blit, no blending).
|
|
*/
|
|
void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
|
|
|
|
byte *in;
|
|
byte *out;
|
|
|
|
for (uint32 i = 0; i < height; i++) {
|
|
out = outo;
|
|
in = ino;
|
|
for (uint32 j = 0; j < width; j++) {
|
|
uint32 pix = *(uint32 *)in;
|
|
int a = (pix >> TransparentSurface::kAShift) & 0xff;
|
|
|
|
if (a == 0) { // Full transparency
|
|
} else { // Full opacity (Any value not exactly 0 is Opaque here)
|
|
*(uint32 *)out = pix;
|
|
out[TransparentSurface::kAIndex] = 0xFF;
|
|
}
|
|
out += 4;
|
|
in += inStep;
|
|
}
|
|
outo += pitch;
|
|
ino += inoStep;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* What we have here is a template method that calls blendPixel() from a different
|
|
* class - the one we call it with - thus performing a different type of blending.
|
|
*
|
|
* @param ino a pointer to the input surface
|
|
* @param outo a pointer to the output surface
|
|
* @param width width of the input surface
|
|
* @param height height of the input surface
|
|
* @param pitch pitch of the output surface - that is, width in bytes of every row, usually bpp * width of the TARGET surface (the area we are blitting to might be smaller, do the math)
|
|
* @inStep size in bytes to skip to address each pixel, usually bpp of the source surface
|
|
* @inoStep width in bytes of every row on the *input* surface / kind of like pitch
|
|
* @color colormod in 0xAARRGGBB format - 0xFFFFFFFF for no colormod
|
|
*/
|
|
|
|
template<class Blender>
|
|
void doBlit(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
|
|
Blender b;
|
|
byte *in;
|
|
byte *out;
|
|
|
|
if (color == 0xffffffff) {
|
|
|
|
for (uint32 i = 0; i < height; i++) {
|
|
out = outo;
|
|
in = ino;
|
|
for (uint32 j = 0; j < width; j++) {
|
|
|
|
byte *outa = &out[TransparentSurface::kAIndex];
|
|
byte *outr = &out[TransparentSurface::kRIndex];
|
|
byte *outg = &out[TransparentSurface::kGIndex];
|
|
byte *outb = &out[TransparentSurface::kBIndex];
|
|
|
|
b.blendPixel(in[TransparentSurface::kAIndex],
|
|
in[TransparentSurface::kRIndex],
|
|
in[TransparentSurface::kGIndex],
|
|
in[TransparentSurface::kBIndex],
|
|
outa, outr, outg, outb);
|
|
|
|
in += inStep;
|
|
out += 4;
|
|
}
|
|
outo += pitch;
|
|
ino += inoStep;
|
|
}
|
|
} else {
|
|
|
|
byte ca = (color >> TransparentSurface::kAModShift) & 0xFF;
|
|
byte cr = (color >> TransparentSurface::kRModShift) & 0xFF;
|
|
byte cg = (color >> TransparentSurface::kGModShift) & 0xFF;
|
|
byte cb = (color >> TransparentSurface::kBModShift) & 0xFF;
|
|
|
|
for (uint32 i = 0; i < height; i++) {
|
|
out = outo;
|
|
in = ino;
|
|
for (uint32 j = 0; j < width; j++) {
|
|
|
|
byte *outa = &out[TransparentSurface::kAIndex];
|
|
byte *outr = &out[TransparentSurface::kRIndex];
|
|
byte *outg = &out[TransparentSurface::kGIndex];
|
|
byte *outb = &out[TransparentSurface::kBIndex];
|
|
|
|
b.blendPixel(in[TransparentSurface::kAIndex],
|
|
in[TransparentSurface::kRIndex],
|
|
in[TransparentSurface::kGIndex],
|
|
in[TransparentSurface::kBIndex],
|
|
outa, outr, outg, outb, &ca, &cr, &cg, &cb);
|
|
in += inStep;
|
|
out += 4;
|
|
}
|
|
outo += pitch;
|
|
ino += inoStep;
|
|
}
|
|
}
|
|
}
|
|
|
|
Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
|
|
|
|
Common::Rect retSize;
|
|
retSize.top = 0;
|
|
retSize.left = 0;
|
|
retSize.setWidth(0);
|
|
retSize.setHeight(0);
|
|
// Check if we need to draw anything at all
|
|
int ca = (color >> 24) & 0xff;
|
|
|
|
if (ca == 0) {
|
|
return retSize;
|
|
}
|
|
|
|
// Create an encapsulating surface for the data
|
|
TransparentSurface srcImage(*this, false);
|
|
// TODO: Is the data really in the screen format?
|
|
if (format.bytesPerPixel != 4) {
|
|
warning("TransparentSurface can only blit 32 bpp images");
|
|
return retSize;
|
|
}
|
|
|
|
if (pPartRect) {
|
|
|
|
int xOffset = pPartRect->left;
|
|
int yOffset = pPartRect->top;
|
|
|
|
if (flipping & FLIP_V) {
|
|
yOffset = srcImage.h - pPartRect->bottom;
|
|
}
|
|
|
|
if (flipping & FLIP_H) {
|
|
xOffset = srcImage.w - pPartRect->right;
|
|
}
|
|
|
|
srcImage.pixels = getBasePtr(xOffset, yOffset);
|
|
srcImage.w = pPartRect->width();
|
|
srcImage.h = pPartRect->height();
|
|
|
|
debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
|
|
pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
|
|
} else {
|
|
|
|
debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
|
|
srcImage.w, srcImage.h, color, width, height);
|
|
}
|
|
|
|
if (width == -1) {
|
|
width = srcImage.w;
|
|
}
|
|
if (height == -1) {
|
|
height = srcImage.h;
|
|
}
|
|
|
|
#ifdef SCALING_TESTING
|
|
// Hardcode scaling to 66% to test scaling
|
|
width = width * 2 / 3;
|
|
height = height * 2 / 3;
|
|
#endif
|
|
|
|
Graphics::Surface *img = nullptr;
|
|
Graphics::Surface *imgScaled = nullptr;
|
|
byte *savedPixels = nullptr;
|
|
if ((width != srcImage.w) || (height != srcImage.h)) {
|
|
// Scale the image
|
|
img = imgScaled = srcImage.scale(width, height);
|
|
savedPixels = (byte *)img->getPixels();
|
|
} else {
|
|
img = &srcImage;
|
|
}
|
|
|
|
// Handle off-screen clipping
|
|
if (posY < 0) {
|
|
img->h = MAX(0, (int)img->h - -posY);
|
|
img->setPixels((byte *)img->getBasePtr(0, -posY));
|
|
posY = 0;
|
|
}
|
|
|
|
if (posX < 0) {
|
|
img->w = MAX(0, (int)img->w - -posX);
|
|
img->setPixels((byte *)img->getBasePtr(-posX, 0));
|
|
posX = 0;
|
|
}
|
|
|
|
img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0));
|
|
img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0));
|
|
|
|
if ((img->w > 0) && (img->h > 0)) {
|
|
int xp = 0, yp = 0;
|
|
|
|
int inStep = 4;
|
|
int inoStep = img->pitch;
|
|
if (flipping & TransparentSurface::FLIP_H) {
|
|
inStep = -inStep;
|
|
xp = img->w - 1;
|
|
}
|
|
|
|
if (flipping & TransparentSurface::FLIP_V) {
|
|
inoStep = -inoStep;
|
|
yp = img->h - 1;
|
|
}
|
|
|
|
byte *ino = (byte *)img->getBasePtr(xp, yp);
|
|
byte *outo = (byte *)target.getBasePtr(posX, posY);
|
|
|
|
if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_OPAQUE) {
|
|
doBlitOpaqueFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
|
|
} else if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_BINARY) {
|
|
doBlitBinaryFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
|
|
} else {
|
|
if (blendMode == BLEND_ADDITIVE) {
|
|
doBlit<BlenderAdditive>(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
|
|
} else if (blendMode == BLEND_SUBTRACTIVE) {
|
|
doBlit<BlenderSubtractive>(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
|
|
} else {
|
|
assert(blendMode == BLEND_NORMAL);
|
|
doBlit<BlenderNormal>(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
retSize.setWidth(img->w);
|
|
retSize.setHeight(img->h);
|
|
|
|
if (imgScaled) {
|
|
imgScaled->setPixels(savedPixels);
|
|
imgScaled->free();
|
|
delete imgScaled;
|
|
}
|
|
|
|
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.
|
|
|
|
Point32 newHotspot;
|
|
Common::Rect srcRect(0, 0, (int16)w, (int16)h);
|
|
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 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);
|
|
|
|
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));
|
|
|
|
|
|
bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor
|
|
|
|
int xd = (srcRect.left + transform._hotspot.x) << 16;
|
|
int yd = (srcRect.top + transform._hotspot.y) << 16;
|
|
int cx = newHotspot.x;
|
|
int cy = newHotspot.y;
|
|
|
|
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
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
int srcW = srcRect.width();
|
|
int srcH = srcRect.height();
|
|
int dstW = dstRect.width();
|
|
int dstH = dstRect.height();
|
|
|
|
target->create((uint16)dstW, (uint16)dstH, this->format);
|
|
|
|
#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++) {
|
|
const tColorRGBA *csp = sp;
|
|
csax = sax;
|
|
for (int x = 0; x < dstW; x++) {
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
for (int y = 0; y < dstH; y++) {
|
|
uint32 *destP = (uint32 *)target->getBasePtr(0, y);
|
|
const uint32 *srcP = (const uint32 *)getBasePtr(0, (y * srcH) / dstH);
|
|
for (int x = 0; x < dstW; x++) {
|
|
*destP++ = srcP[scaleCacheX[x]];
|
|
}
|
|
}
|
|
delete[] scaleCacheX;
|
|
|
|
#endif
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
} // End of namespace Wintermute
|