Files
scummvm-cursorfix/engines/ags/lib/allegro/surface.h
2026-02-02 04:50:13 +01:00

357 lines
12 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_SURFACE_H
#define AGS_LIB_ALLEGRO_SURFACE_H
#include "graphics/managed_surface.h"
#include "ags/lib/allegro/base.h"
#include "ags/lib/allegro/color.h"
#include "common/array.h"
namespace AGS3 {
class BITMAP {
private:
Graphics::ManagedSurface *_owner;
public:
int16 &w, &h;
int32 &pitch;
Graphics::PixelFormat &format;
bool clip;
int ct, cb, cl, cr;
Common::Array<byte *> line;
public:
BITMAP(Graphics::ManagedSurface *owner);
virtual ~BITMAP() {
}
Graphics::ManagedSurface &operator*() const {
return *_owner;
}
Graphics::ManagedSurface &getSurface() {
return *_owner;
}
const Graphics::ManagedSurface &getSurface() const {
return *_owner;
}
unsigned char *getPixels() const {
return (unsigned char *)_owner->getPixels();
}
unsigned char *getBasePtr(uint16 x, uint16 y) const {
return (unsigned char *)_owner->getBasePtr(x, y);
}
uint getTransparentColor() const {
// See allegro bitmap_mask_color
// For paletted sprites this is 0.
// For other color depths this is bright pink (RGB 255, 0, 255) with alpha set to 0.
if (format.bytesPerPixel == 1)
return 0;
return format.ARGBToColor(0, 255, 0, 255);
}
inline const Common::Point getOffsetFromOwner() const {
return _owner->getOffsetFromOwner();
}
int getpixel(int x, int y) const;
void clear() {
_owner->clear();
}
void makeOpaque();
/**
* Draws a solid filled in circle
*/
void circlefill(int x, int y, int radius, int color);
/**
* Fills an enclosed area starting at a given point
*/
void floodfill(int x, int y, int color);
/**
* Draw a horizontal line
*/
void hLine(int x, int y, int x2, uint32 color) {
_owner->hLine(x, y, x2, color);
}
/**
* Draw a vertical line.
*/
void vLine(int x, int y, int y2, uint32 color) {
_owner->vLine(x, y, y2, color);
}
/**
* Draws the passed surface onto this one
*/
void draw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
int dstX, int dstY, bool horizFlip, bool vertFlip,
bool skipTrans, int srcAlpha, int tintRed = -1, int tintGreen = -1,
int tintBlue = -1);
/**
* Stretches and draws the passed surface onto this one
*/
void stretchDraw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
const Common::Rect &destRect, bool skipTrans, int srcAlpha);
inline bool isSubBitmap() const {
return _owner->disposeAfterUse() == DisposeAfterUse::NO;
}
private:
// True color blender functions
// In Allegro all the blender functions are of the form
// unsigned int blender_func(unsigned long x, unsigned long y, unsigned long n)
// when x is the sprite color, y the destination color, and n an alpha value
void blendPixel(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool useTint, byte *destVal) const;
inline void rgbBlend(uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Note: the original's handling varies slightly for R & B vs G.
// We need to exactly replicate it to ensure Lamplight City's
// calendar puzzle works correctly
if (alpha)
alpha++;
uint32 x = ((uint32)rSrc << 16) | ((uint32)gSrc << 8) | (uint32)bSrc;
uint32 y = ((uint32)rDest << 16) | ((uint32)gDest << 8) | (uint32)bDest;
uint32 res = ((x & 0xFF00FF) - (y & 0xFF00FF)) * alpha / 256 + y;
y &= 0xFF00;
x &= 0xFF00;
uint32 g = (x - y) * alpha / 256 + y;
rDest = (res >> 16) & 0xff;
gDest = (g >> 8) & 0xff;
bDest = res & 0xff;
}
inline void argbBlend(uint32 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest) const {
// Original logic has uint32 src and dst colors as ARGB8888
// ++src_alpha;
// uint32 dst_alpha = geta32(dst);
// if (dst_alpha)
// ++dst_alpha;
// uint32 dst_g = (dst & 0x00FF00) * dst_alpha / 256;
// dst = (dst & 0xFF00FF) * dst_alpha / 256;
// dst_g = (((src & 0x00FF00) - (dst_g & 0x00FF00)) * src_alpha / 256 + dst_g) & 0x00FF00;
// dst = (((src & 0xFF00FF) - (dst & 0xFF00FF)) * src_alpha / 256 + dst) & 0xFF00FF;
// dst_alpha = 256 - (256 - src_alpha) * (256 - dst_alpha) / 256;
// src_alpha = /* 256 * 256 == */ 0x10000 / dst_alpha;
// dst_g = (dst_g * src_alpha / 256) & 0x00FF00;
// dst = (dst * src_alpha / 256) & 0xFF00FF;
// return dst | dst_g | (--dst_alpha << 24);
double sAlpha = (double)(aSrc & 0xff) / 255.0;
double dAlpha = (double)aDest / 255.0;
dAlpha *= (1.0 - sAlpha);
rDest = static_cast<uint8>((rSrc * sAlpha + rDest * dAlpha) / (sAlpha + dAlpha));
gDest = static_cast<uint8>((gSrc * sAlpha + gDest * dAlpha) / (sAlpha + dAlpha));
bDest = static_cast<uint8>((bSrc * sAlpha + bDest * dAlpha) / (sAlpha + dAlpha));
aDest = static_cast<uint8>(255. * (sAlpha + dAlpha));
}
// kRgbToRgbBlender
inline void blendRgbToRgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Default mode for set_trans_blender
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
// Original doesn't set alpha (so it is 0), but the function is not meant to be used
// on bitmap with transparency. Should we set alpha to 0xff?
aDest = 0;
}
// kAlphaPreservedBlenderMode
inline void blendPreserveAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _myblender_alpha_trans24
// Like blendRgbToRgb, but result as the same alpha as destColor
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
// Preserve value in aDest
}
// kArgbToArgbBlender
inline void blendArgbToArgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender functions: _argb2argb_blender
if (alpha == 0)
alpha = aSrc;
else
alpha = aSrc * ((alpha & 0xff) + 1) / 256;
if (alpha != 0)
argbBlend(alpha, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest);
}
// kRgbToArgbBlender
inline void blendRgbToArgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _rgb2argb_blenders
if (alpha == 0 || alpha == 0xff) {
aDest = 0xff;
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
} else
argbBlend(alpha, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest);
}
// kArgbToRgbBlender
inline void blendArgbToRgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _argb2rgb_blender
if (alpha == 0)
alpha = aSrc;
else
alpha = aSrc * ((alpha & 0xff) + 1) / 256;
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
// Original doesn't set alpha (so it is 0), but the function is not meant to be used
// on bitmap with transparency. Should we set alpha to 0xff?
aDest = 0;
}
// kOpaqueBlenderMode
inline void blendOpaque(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _opaque_alpha_blender
aDest = 0xff;
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
}
// kSourceAlphaBlender
inline void blendSourceAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Used after set_alpha_blender
// Uses alpha from source. Result is fully opaque
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, aSrc);
// Original doesn't set alpha (so it is 0), but the function is not meant to be used
// on bitmap with transparency. Should we set alpha to 0xff?
aDest = 0;
}
// kAdditiveBlenderMode
inline void blendAdditiveAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _additive_alpha_copysrc_blender
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
uint32 a = (uint32)aSrc + (uint32)aDest;
if (a > 0xff)
aDest = 0xff;
else
aDest = static_cast<uint8>(a);
}
// kTintBlenderMode and kTintLightBlenderMode
void blendTintSprite(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool light) const;
friend class DrawInnerImpl_AVX2;
friend class DrawInnerImpl_SSE2;
friend class DrawInnerImpl_NEON;
constexpr static int SCALE_THRESHOLD_BITS = 8;
constexpr static int SCALE_THRESHOLD = 1 << SCALE_THRESHOLD_BITS;
struct DrawInnerArgs {
const bool useTint, horizFlip, vertFlip, skipTrans;
bool sameFormat, shouldDraw;
int xStart, yStart, srcAlpha, tintRed, tintGreen, tintBlue, scaleX, scaleY;
uint32 transColor, alphaMask;
PALETTE palette;
BlenderMode blenderMode;
Common::Rect dstRect, srcArea;
BITMAP &dstBitmap;
const ::Graphics::ManagedSurface &src;
::Graphics::Surface destArea;
DrawInnerArgs(BITMAP *dstBitmap, const BITMAP *srcBitmap,
const Common::Rect &srcRect, const Common::Rect &dstRect,
bool skipTrans, int srcAlpha, bool horizFlip,
bool vertFlip, int tintRed, int tintGreen, int tintBlue,
bool doScale);
};
template<bool Scale>
void drawGeneric(DrawInnerArgs &args);
#ifdef SCUMMVM_NEON
template<bool Scale>
void drawNEON(DrawInnerArgs &args);
#endif
#ifdef SCUMMVM_SSE2
template<bool Scale>
void drawSSE2(DrawInnerArgs &args);
#endif
#ifdef SCUMMVM_AVX2
template<bool Scale>
void drawAVX2(DrawInnerArgs &args);
#endif
template<int DestBytesPerPixel, int SrcBytesPerPixel, bool Scale>
void drawInnerGeneric(DrawInnerArgs &args);
inline uint32 getColor(const byte *data, byte bpp) const {
switch (bpp) {
case 1:
return *data;
case 2:
return *(const uint16 *)data;
case 4:
return *(const uint32 *)data;
default:
error("Unsupported format in BITMAP::getColor");
}
}
};
/**
* Derived surface class
*/
class Surface : public Graphics::ManagedSurface, public BITMAP {
public:
Surface(int width, int height, const Graphics::PixelFormat &pixelFormat) :
Graphics::ManagedSurface(width, height, pixelFormat), BITMAP(this) {
// Allegro uses 255, 0, 255 RGB as the transparent color
if (pixelFormat.bytesPerPixel == 2 || pixelFormat.bytesPerPixel == 4)
setTransparentColor(pixelFormat.RGBToColor(255, 0, 255));
}
Surface(Graphics::ManagedSurface &surf, const Common::Rect &bounds) :
Graphics::ManagedSurface(surf, bounds), BITMAP(this) {
// Allegro uses 255, 0, 255 RGB as the transparent color
if (surf.format.bytesPerPixel == 2 || surf.format.bytesPerPixel == 4)
setTransparentColor(surf.format.RGBToColor(255, 0, 255));
}
~Surface() override {
}
};
BITMAP *create_bitmap(int width, int height);
BITMAP *create_bitmap_ex(int color_depth, int width, int height);
BITMAP *create_sub_bitmap(BITMAP *parent, int x, int y, int width, int height);
BITMAP *create_video_bitmap(int width, int height);
BITMAP *create_system_bitmap(int width, int height);
void destroy_bitmap(BITMAP *bitmap);
} // namespace AGS3
#endif