COMMON: Copy TransparentSurface from Wintermute engine to common code

This commit is contained in:
Eugene Sandulenko 2014-06-03 17:25:18 +03:00
parent 215f5f9a49
commit 7322d905b3
7 changed files with 1387 additions and 0 deletions

View file

@ -17,6 +17,9 @@ MODULE_OBJS := \
scaler/thumbnail_intern.o \
sjis.o \
surface.o \
transform_struct.o \
transform_tools.o \
transparent_surface.o \
thumbnail.o \
VectorRenderer.o \
VectorRendererSpec.o \

View file

@ -0,0 +1,120 @@
/* 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.
*
*/
#include "graphics/transform_struct.h"
#include "graphics/transparent_surface.h"
namespace Graphics {
void TransformStruct::init(Common::Point zoom, uint32 angle, Common::Point hotspot, bool alphaDisable, TSpriteBlendMode blendMode, uint32 rgbaMod, bool mirrorX, bool mirrorY, Common::Point offset) {
_zoom = zoom;
_angle = angle;
_hotspot = hotspot;
_blendMode = blendMode;
_rgbaMod = rgbaMod;
_alphaDisable = alphaDisable;
_flip = 0;
_flip += FLIP_H * mirrorX;
_flip += FLIP_V * mirrorY;
_offset = offset;
_numTimesX = 1;
_numTimesY = 1;
}
TransformStruct::TransformStruct(int32 zoomX, int32 zoomY, uint32 angle, int32 hotspotX, int32 hotspotY, TSpriteBlendMode blendMode, uint32 rgbaMod, bool mirrorX, bool mirrorY, int32 offsetX, int32 offsetY) {
init(Common::Point(zoomX, zoomY),
angle,
Common::Point(hotspotX, hotspotY),
false,
blendMode,
rgbaMod,
mirrorX, mirrorY,
Common::Point(offsetX, offsetY));
}
TransformStruct::TransformStruct(float zoomX, float zoomY, uint32 angle, int32 hotspotX, int32 hotspotY, TSpriteBlendMode blendMode, uint32 rgbaMod, bool mirrorX, bool mirrorY, int32 offsetX, int32 offsetY) {
init(Common::Point((int)(zoomX / 100.0 * kDefaultZoomX),
(int)(zoomY / 100.0 * kDefaultZoomY)),
angle,
Common::Point(hotspotX, hotspotY),
false,
blendMode,
rgbaMod,
mirrorX, mirrorY,
Common::Point(offsetX, offsetY));
}
TransformStruct::TransformStruct(int32 zoomX, int32 zoomY, TSpriteBlendMode blendMode, uint32 rgbaMod, bool mirrorX, bool mirrorY) {
init(Common::Point(zoomX, zoomY),
kDefaultAngle,
Common::Point(kDefaultHotspotX, kDefaultHotspotY),
false,
blendMode,
rgbaMod,
mirrorX,
mirrorY,
Common::Point(kDefaultOffsetX, kDefaultOffsetY));
}
TransformStruct::TransformStruct(int32 zoomX, int32 zoomY, uint32 angle, int32 hotspotX, int32 hotspotY) {
init(Common::Point(zoomX, zoomY),
angle,
Common::Point(hotspotX, hotspotY),
true,
BLEND_NORMAL,
kDefaultRgbaMod,
false, false,
Common::Point(kDefaultOffsetX, kDefaultOffsetY));
}
TransformStruct::TransformStruct(int32 numTimesX, int32 numTimesY) {
init(Common::Point(kDefaultZoomX, kDefaultZoomY),
kDefaultAngle,
Common::Point(kDefaultHotspotX, kDefaultHotspotY),
false,
BLEND_NORMAL,
kDefaultRgbaMod,
false, false,
Common::Point(kDefaultOffsetX, kDefaultOffsetY));
_numTimesX = numTimesX;
_numTimesY = numTimesY;
}
TransformStruct::TransformStruct() {
init(Common::Point(kDefaultZoomX, kDefaultZoomY),
kDefaultAngle,
Common::Point(kDefaultHotspotX, kDefaultHotspotY),
true,
BLEND_NORMAL,
kDefaultRgbaMod,
false, false,
Common::Point(kDefaultOffsetX, kDefaultOffsetY));
}
bool TransformStruct::getMirrorX() const {
return (bool)(_flip & FLIP_H);
}
bool TransformStruct::getMirrorY() const {
return (bool)(_flip & FLIP_V);
}
} // End of namespace Graphics

View file

@ -0,0 +1,97 @@
/* 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.
*
*/
#ifndef GRAPHICS_TRANSFORM_STRUCT_H
#define GRAPHICS_TRANSFORM_STRUCT_H
#include "common/rect.h"
namespace Graphics {
enum TSpriteBlendMode {
BLEND_UNKNOWN = -1,
BLEND_NORMAL = 0,
BLEND_ADDITIVE = 1,
BLEND_SUBTRACTIVE = 2,
NUM_BLEND_MODES
};
/**
* Contains all the required information that define a transform.
* Same source sprite + same TransformStruct = Same resulting sprite.
* Has a number of overloaded constructors to accomodate various argument lists.
*/
const int32 kDefaultZoomX = 100;
const int32 kDefaultZoomY = 100;
const uint32 kDefaultRgbaMod = 0xFFFFFFFF;
const int32 kDefaultHotspotX = 0;
const int32 kDefaultHotspotY = 0;
const int32 kDefaultOffsetX = 0;
const int32 kDefaultOffsetY = 0;
const int32 kDefaultAngle = 0;
struct TransformStruct {
private:
void init(Common::Point zoom, uint32 angle, Common::Point hotspot, bool alphaDisable, TSpriteBlendMode blendMode, uint32 alpha, bool mirrorX, bool mirrorY, Common::Point offset);
public:
TransformStruct(int32 zoomX, int32 zoomY, uint32 angle, int32 hotspotX, int32 hotspotY, TSpriteBlendMode blendMode, uint32 alpha, bool mirrorX = false, bool mirrorY = false, int32 offsetX = 0, int32 offsetY = 0);
TransformStruct(float zoomX, float zoomY, uint32 angle, int32 hotspotX, int32 hotspotY, TSpriteBlendMode blendMode, uint32 alpha, bool mirrorX = false, bool mirrorY = false, int32 offsetX = 0, int32 offsetY = 0);
TransformStruct(int32 zoomX, int32 zoomY, TSpriteBlendMode blendMode, uint32 alpha, bool mirrorX = false, bool mirrorY = false);
TransformStruct(int32 zoomX, int32 zoomY, uint32 angle, int32 hotspotX = 0, int32 hotspotY = 0);
TransformStruct(int32 numTimesX, int32 numTimesY);
TransformStruct();
Common::Point _zoom; ///< Zoom; 100 = no zoom
Common::Point _hotspot; ///< Position of the hotspot
int32 _angle; ///< Rotation angle, in degrees
byte _flip; ///< Bitflag: see TransparentSurface::FLIP_XXX
bool _alphaDisable;
TSpriteBlendMode _blendMode;
uint32 _rgbaMod; ///< RGBa
Common::Point _offset;
int32 _numTimesX;
int32 _numTimesY;
bool getMirrorX() const;
bool getMirrorY() const;
bool operator==(const TransformStruct &compare) const {
return (compare._angle == _angle &&
compare._flip == _flip &&
compare._zoom == _zoom &&
compare._offset == _offset &&
compare._alphaDisable == _alphaDisable &&
compare._rgbaMod == _rgbaMod &&
compare._blendMode == _blendMode &&
compare._numTimesX == _numTimesX &&
compare._numTimesY == _numTimesY
);
}
bool operator!=(const TransformStruct &compare) const {
return !(compare == *this);
}
};
} // End of namespace Graphics
#endif

View file

@ -0,0 +1,87 @@
/* 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.
*
*/
#include "graphics/transform_tools.h"
#include <math.h>
namespace Graphics {
FloatPoint TransformTools::transformPoint(FloatPoint point, const float rotate, const Common::Point &zoom, const bool mirrorX, const bool mirrorY) {
float rotateRad = rotate * M_PI / 180.0f;
float x = point.x;
float y = point.y;
x = (x * zoom.x) / kDefaultZoomX;
y = (y * zoom.y) / kDefaultZoomY;
#if 0
// TODO: Mirroring should be done before rotation, but the blitting
// code does the inverse, so we match that for now.
if (mirrorX)
x *= -1;
if (mirrorY)
y *= -1;
#endif
FloatPoint newPoint;
newPoint.x = x * cos(rotateRad) - y * sin(rotateRad);
newPoint.y = x * sin(rotateRad) + y * cos(rotateRad);
if (mirrorX) {
newPoint.x *= -1;
}
if (mirrorY) {
newPoint.y *= -1;
}
return newPoint;
}
Common::Rect TransformTools::newRect(const Common::Rect &oldRect, const TransformStruct &transform, Common::Point *newHotspot) {
Common::Point nw(oldRect.left, oldRect.top);
Common::Point ne(oldRect.right, oldRect.top);
Common::Point sw(oldRect.left, oldRect.bottom);
Common::Point se(oldRect.right, oldRect.bottom);
FloatPoint nw1, ne1, sw1, se1;
nw1 = transformPoint(nw - transform._hotspot, transform._angle, transform._zoom);
ne1 = transformPoint(ne - transform._hotspot, transform._angle, transform._zoom);
sw1 = transformPoint(sw - transform._hotspot, transform._angle, transform._zoom);
se1 = transformPoint(se - transform._hotspot, transform._angle, transform._zoom);
float top = MIN(nw1.y, MIN(ne1.y, MIN(sw1.y, se1.y)));
float bottom = MAX(nw1.y, MAX(ne1.y, MAX(sw1.y, se1.y)));
float left = MIN(nw1.x, MIN(ne1.x, MIN(sw1.x, se1.x)));
float right = MAX(nw1.x, MAX(ne1.x, MAX(sw1.x, se1.x)));
if (newHotspot) {
newHotspot->y = (uint32)(-floor(top));
newHotspot->x = (uint32)(-floor(left));
}
Common::Rect res;
res.top = (int32)(floor(top)) + transform._hotspot.y;
res.bottom = (int32)(ceil(bottom)) + transform._hotspot.y;
res.left = (int32)(floor(left)) + transform._hotspot.x;
res.right = (int32)(ceil(right)) + transform._hotspot.x;
return res;
}
} // End of namespace Graphics

View file

@ -0,0 +1,77 @@
/* 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.
*
*/
#ifndef GRAPHICS_TRANSFORM_TOOLS_H
#define GRAPHICS_TRANSFORM_TOOLS_H
#include "common/rect.h"
#include "graphics/transform_struct.h"
namespace Graphics {
static const float kEpsilon = 0.00001; // arbitrarily taken number
struct FloatPoint {
float x;
float y;
FloatPoint() : x(0), y(0) {}
FloatPoint(float x1, float y1) : x(x1), y(y1) {}
FloatPoint(const Common::Point p) : x(p.x), y(p.y) {}
bool operator==(const FloatPoint &p) const { return fabs(x - p.x) < kEpsilon && fabs(y - p.y) < kEpsilon; }
bool operator!=(const FloatPoint &p) const { return fabs(x - p.x) > kEpsilon || fabs(y - p.y) > kEpsilon; }
FloatPoint operator+(const FloatPoint &delta) const { return FloatPoint (x + delta.x, y + delta.y); }
FloatPoint operator-(const FloatPoint &delta) const { return FloatPoint (x - delta.x, y - delta.y); }
FloatPoint& operator+=(const FloatPoint &delta) {
x += delta.x;
y += delta.y;
return *this;
}
FloatPoint& operator-=(const FloatPoint &delta) {
x -= delta.x;
y -= delta.y;
return *this;
}
};
class TransformTools {
public:
/**
* Basic transform (scale + rotate) for a single point
*/
static FloatPoint transformPoint(FloatPoint point, const float rotate, const Common::Point &zoom, const bool mirrorX = false, const bool mirrorY = false);
/**
* @param &point the point on which the transform is to be applied
* @param rotate the angle in degrees
* @param &zoom zoom x,y in percent
* @param mirrorX flip along the vertical axis?
* @param mirrorY flip along the horizontal axis?
* @return the smallest rect that can contain the transformed sprite
* and, as a side-effect, "newHotspot" will tell you where the hotspot will
* have ended up in the new rect, for centering.
*/
static Common::Rect newRect(const Common::Rect &oldRect, const TransformStruct &transform, Common::Point *newHotspot);
};
} // End of namespace Wintermute
#endif

View file

@ -0,0 +1,851 @@
/* 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 "graphics/transparent_surface.h"
#include "graphics/transform_tools.h"
//#define ENABLE_BILINEAR
namespace Graphics {
static const int kAShift = 0;//img->format.aShift;
static const int kBModShift = 0;//img->format.bShift;
static const int kGModShift = 8;//img->format.gShift;
static const int kRModShift = 16;//img->format.rShift;
static const int kAModShift = 24;//img->format.aShift;
#ifdef SCUMM_LITTLE_ENDIAN
static const int kAIndex = 0;
static const int kBIndex = 1;
static const int kGIndex = 2;
static const int kRIndex = 3;
#else
static const int kAIndex = 3;
static const int kBIndex = 2;
static const int kGIndex = 1;
static const int kRIndex = 0;
#endif
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);
void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
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[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 >> kAShift) & 0xff;
if (a != 0) { // Full opacity (Any value not exactly 0 is Opaque here)
*(uint32 *)out = pix;
out[kAIndex] = 0xFF;
}
out += 4;
in += inStep;
}
outo += pitch;
ino += inoStep;
}
}
/**
* Optimized version of doBlit to be used with alpha blended blitting
* @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
*/
void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
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++) {
if (in[kAIndex] != 0) {
out[kAIndex] = 255;
out[kRIndex] = ((in[kRIndex] * in[kAIndex]) + out[kRIndex] * (255 - in[kAIndex])) >> 8;
out[kGIndex] = ((in[kGIndex] * in[kAIndex]) + out[kGIndex] * (255 - in[kAIndex])) >> 8;
out[kBIndex] = ((in[kBIndex] * in[kAIndex]) + out[kBIndex] * (255 - in[kAIndex])) >> 8;
}
in += inStep;
out += 4;
}
outo += pitch;
ino += inoStep;
}
} else {
byte ca = (color >> kAModShift) & 0xFF;
byte cr = (color >> kRModShift) & 0xFF;
byte cg = (color >> kGModShift) & 0xFF;
byte cb = (color >> kBModShift) & 0xFF;
for (uint32 i = 0; i < height; i++) {
out = outo;
in = ino;
for (uint32 j = 0; j < width; j++) {
uint32 ina = in[kAIndex] * ca >> 8;
out[kAIndex] = 255;
out[kBIndex] = (out[kBIndex] * (255 - ina) >> 8);
out[kGIndex] = (out[kGIndex] * (255 - ina) >> 8);
out[kRIndex] = (out[kRIndex] * (255 - ina) >> 8);
out[kBIndex] = out[kBIndex] + (in[kBIndex] * ina * cb >> 16);
out[kGIndex] = out[kGIndex] + (in[kGIndex] * ina * cg >> 16);
out[kRIndex] = out[kRIndex] + (in[kRIndex] * ina * cr >> 16);
in += inStep;
out += 4;
}
outo += pitch;
ino += inoStep;
}
}
}
/**
* Optimized version of doBlit to be used with additive blended blitting
*/
void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
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++) {
if (in[kAIndex] != 0) {
out[kRIndex] = MIN((in[kRIndex] * in[kAIndex] >> 8) + out[kRIndex], 255);
out[kGIndex] = MIN((in[kGIndex] * in[kAIndex] >> 8) + out[kGIndex], 255);
out[kBIndex] = MIN((in[kBIndex] * in[kAIndex] >> 8) + out[kBIndex], 255);
}
in += inStep;
out += 4;
}
outo += pitch;
ino += inoStep;
}
} else {
byte ca = (color >> kAModShift) & 0xFF;
byte cr = (color >> kRModShift) & 0xFF;
byte cg = (color >> kGModShift) & 0xFF;
byte cb = (color >> kBModShift) & 0xFF;
for (uint32 i = 0; i < height; i++) {
out = outo;
in = ino;
for (uint32 j = 0; j < width; j++) {
uint32 ina = in[kAIndex] * ca >> 8;
if (cb != 255) {
out[kBIndex] = MIN(out[kBIndex] + ((in[kBIndex] * cb * ina) >> 16), 255u);
} else {
out[kBIndex] = MIN(out[kBIndex] + (in[kBIndex] * ina >> 8), 255u);
}
if (cg != 255) {
out[kGIndex] = MIN(out[kGIndex] + ((in[kGIndex] * cg * ina) >> 16), 255u);
} else {
out[kGIndex] = MIN(out[kGIndex] + (in[kGIndex] * ina >> 8), 255u);
}
if (cr != 255) {
out[kRIndex] = MIN(out[kRIndex] + ((in[kRIndex] * cr * ina) >> 16), 255u);
} else {
out[kRIndex] = MIN(out[kRIndex] + (in[kRIndex] * ina >> 8), 255u);
}
in += inStep;
out += 4;
}
outo += pitch;
ino += inoStep;
}
}
}
/**
* Optimized version of doBlit to be used with subtractive blended blitting
*/
void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
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++) {
if (in[kAIndex] != 0) {
out[kRIndex] = MAX(out[kRIndex] - ((in[kRIndex] * out[kRIndex]) * in[kAIndex] >> 16), 0);
out[kGIndex] = MAX(out[kGIndex] - ((in[kGIndex] * out[kGIndex]) * in[kAIndex] >> 16), 0);
out[kBIndex] = MAX(out[kBIndex] - ((in[kBIndex] * out[kBIndex]) * in[kAIndex] >> 16), 0);
}
in += inStep;
out += 4;
}
outo += pitch;
ino += inoStep;
}
} else {
byte cr = (color >> kRModShift) & 0xFF;
byte cg = (color >> kGModShift) & 0xFF;
byte cb = (color >> kBModShift) & 0xFF;
for (uint32 i = 0; i < height; i++) {
out = outo;
in = ino;
for (uint32 j = 0; j < width; j++) {
out[kAIndex] = 255;
if (cb != 255) {
out[kBIndex] = MAX(out[kBIndex] - ((in[kBIndex] * cb * (out[kBIndex]) * in[kAIndex]) >> 24), 0);
} else {
out[kBIndex] = MAX(out[kBIndex] - (in[kBIndex] * (out[kBIndex]) * in[kAIndex] >> 16), 0);
}
if (cg != 255) {
out[kGIndex] = MAX(out[kGIndex] - ((in[kGIndex] * cg * (out[kGIndex]) * in[kAIndex]) >> 24), 0);
} else {
out[kGIndex] = MAX(out[kGIndex] - (in[kGIndex] * (out[kGIndex]) * in[kAIndex] >> 16), 0);
}
if (cr != 255) {
out[kRIndex] = MAX(out[kRIndex] - ((in[kRIndex] * cr * (out[kRIndex]) * in[kAIndex]) >> 24), 0);
} else {
out[kRIndex] = MAX(out[kRIndex] - (in[kRIndex] * (out[kRIndex]) * in[kAIndex] >> 16), 0);
}
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 32bpp images, but got %d", format.bytesPerPixel * 8);
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 & FLIP_H) {
inStep = -inStep;
xp = img->w - 1;
}
if (flipping & 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) {
doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
} else if (blendMode == BLEND_SUBTRACTIVE) {
doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
} else {
assert(blendMode == BLEND_NORMAL);
doBlitAlphaBlend(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);
}
}
}
}
AlphaType TransparentSurface::getAlphaMode() const {
return _alphaMode;
}
void TransparentSurface::setAlphaMode(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.
Common::Point newHotspot;
Common::Rect srcRect(0, 0, (int16)w, (int16)h);
Common::Rect rect = TransformTools::newRect(Common::Rect(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 Graphics

View file

@ -0,0 +1,152 @@
/* 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.
*
*/
#ifndef GRAPHICS_TRANSPARENTSURFACE_H
#define GRAPHICS_TRANSPARENTSURFACE_H
#include "graphics/surface.h"
#include "graphics/transform_struct.h"
/*
* This code is based on Broken Sword 2.5 engine
*
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
*
* Licensed under GNU GPL v2
*
*/
// TODO: Find a better solution for this.
#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B))
#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))
namespace Graphics {
// Enums
/**
@brief The possible flipping parameters for the blit method.
*/
enum FLIP_FLAGS {
/// The image will not be flipped.
FLIP_NONE = 0,
/// The image will be flipped at the horizontal axis.
FLIP_H = 1,
/// The image will be flipped at the vertical axis.
FLIP_V = 2,
/// The image will be flipped at the horizontal and vertical axis.
FLIP_HV = FLIP_H | FLIP_V,
/// The image will be flipped at the horizontal and vertical axis.
FLIP_VH = FLIP_H | FLIP_V
};
enum AlphaType {
ALPHA_OPAQUE = 0,
ALPHA_BINARY = 1,
ALPHA_FULL = 2
};
/**
* A transparent graphics surface, which implements alpha blitting.
*/
struct TransparentSurface : public Graphics::Surface {
TransparentSurface();
TransparentSurface(const Graphics::Surface &surf, bool copyData = false);
void setColorKey(char r, char g, char b);
void disableColorKey();
/**
@brief renders the surface to another surface
@param target a pointer to the target surface. In most cases this is the framebuffer.
@param posX the position on the X-axis in the target image in pixels where the image is supposed to be rendered.<br>
The default value is 0.
@param posY the position on the Y-axis in the target image in pixels where the image is supposed to be rendered.<br>
The default value is 0.
@param flipping how the the image should be flipped.<br>
The default value is BS_Image::FLIP_NONE (no flipping)
@param pPartRect Pointer on Common::Rect which specifies the section to be rendered. If the whole image has to be rendered the Pointer is NULL.<br>
This referes to the unflipped and unscaled image.<br>
The default value is NULL.
@param color an ARGB color value, which determines the parameters for the color modulation und alpha blending.<br>
The alpha component of the color determines the alpha blending parameter (0 = no covering, 255 = full covering).<br>
The color components determines the color for color modulation.<br>
The default value is BS_ARGB(255, 255, 255, 255) (full covering, no color modulation).
The macros BS_RGB and BS_ARGB can be used for the creation of the color value.
@param width the output width of the screen section.
The images will be scaled if the output width of the screen section differs from the image section.<br>
The value -1 determines that the image should not be scaled.<br>
The default value is -1.
@param height the output height of the screen section.
The images will be scaled if the output width of the screen section differs from the image section.<br>
The value -1 determines that the image should not be scaled.<br>
The default value is -1.
@return returns false if the rendering failed.
*/
Common::Rect blit(Graphics::Surface &target, int posX = 0, int posY = 0,
int flipping = FLIP_NONE,
Common::Rect *pPartRect = nullptr,
uint color = BS_ARGB(255, 255, 255, 255),
int width = -1, int height = -1,
TSpriteBlendMode blend = BLEND_NORMAL);
void applyColorKey(uint8 r, uint8 g, uint8 b, bool overwriteAlpha = false);
/**
* @brief Scale function; this returns a transformed version of this surface after rotation and
* scaling. Please do not use this if angle != 0, use rotoscale.
*
* @param newWidth the resulting width.
* @param newHeight the resulting height.
* @see TransformStruct
*/
TransparentSurface *scale(uint16 newWidth, uint16 newHeight) const;
/**
* @brief Rotoscale function; this returns a transformed version of this surface after rotation and
* scaling. Please do not use this if angle == 0, use plain old scaling function.
*
* @param transform a TransformStruct wrapping the required info. @see TransformStruct
*
*/
TransparentSurface *rotoscale(const TransformStruct &transform) const;
AlphaType getAlphaMode() const;
void setAlphaMode(AlphaType);
private:
AlphaType _alphaMode;
};
/**
* A deleter for Surface objects which can be used with SharedPtr.
*
* This deleter assures Surface::free is called on deletion.
*/
/*struct SharedPtrTransparentSurfaceDeleter {
void operator()(TransparentSurface *ptr) {
ptr->free();
delete ptr;
}
};*/
} // End of namespace Graphics
#endif