GRAPHICS: Support varying bit depths in ManagedSurface transBlitFrom

This commit is contained in:
Paul Gilbert 2016-11-26 10:53:14 -05:00
parent d7caa0e6f4
commit 4e06799122

View file

@ -242,49 +242,85 @@ void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRe
destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor); destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
} }
template<typename T> template<typename TSRC, typename TDEST>
void transBlit(const Surface &src, const Common::Rect &srcRect, Surface *dest, const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { void transBlit(const Surface &src, const Common::Rect &srcRect, Surface &dest, const Common::Rect &destRect, TSRC transColor, bool flipped, uint overrideColor) {
int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width(); int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width();
int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height(); int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height();
const Graphics::PixelFormat &srcFormat = src.format;
const Graphics::PixelFormat &destFormat = dest.format;
byte aSrc, rSrc, gSrc, bSrc;
byte rDest, gDest, bDest;
double alpha;
// Loop through drawing output lines // Loop through drawing output lines
for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) { for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) {
if (destY < 0 || destY >= dest->h) if (destY < 0 || destY >= dest.h)
continue; continue;
const T *srcLine = (const T *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD); const TSRC *srcLine = (const TSRC *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD);
T *destLine = (T *)dest->getBasePtr(destRect.left, destY); TDEST *destLine = (TDEST *)dest.getBasePtr(destRect.left, destY);
// Loop through drawing the pixels of the row // Loop through drawing the pixels of the row
for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) { for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) {
if (destX < 0 || destX >= dest->w) if (destX < 0 || destX >= dest.w)
continue; continue;
T srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD]; TSRC srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD];
if (srcVal != transColor) { if (srcVal == transColor)
continue;
if (srcFormat == destFormat) {
// Matching formats, so we can do a straight copy
destLine[xCtr] = overrideColor ? overrideColor : srcVal; destLine[xCtr] = overrideColor ? overrideColor : srcVal;
} else {
// Otherwise we have to manually decode and re-encode each pixel
srcFormat.colorToARGB(*srcLine, aSrc, rSrc, gSrc, bSrc);
destFormat.colorToRGB(destLine[xCtr], rDest, gDest, bDest);
if (aSrc == 0) {
// Completely transparent, so skip
continue;
} else if (aSrc == 0xff) {
// Completely opaque, so copy RGB values over
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
} else {
// Partially transparent, so calculate new pixel colors
alpha = (double)aSrc / 255.0;
rDest = (rSrc * alpha) + (rDest * (1.0 - alpha));
gDest = (gSrc * alpha) + (gDest * (1.0 - alpha));
bDest = (bSrc * alpha) + (bDest * (1.0 - alpha));
}
destLine[xCtr] = destFormat.ARGBToColor(0xff, rDest, gDest, bDest);
} }
} }
} }
} }
#define HANDLE_BLIT(SRC_BYTES, DEST_BYTES, SRC_TYPE, DEST_TYPE) \
if (src.format.bytesPerPixel == SRC_BYTES && format.bytesPerPixel == DEST_BYTES) \
transBlit<SRC_TYPE, DEST_TYPE>(src, srcRect, _innerSurface, destRect, transColor, flipped, overrideColor); \
else
void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect, void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) {
if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0) if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0)
return; return;
if (format.bytesPerPixel == 1) HANDLE_BLIT(1, 1, byte, byte)
transBlit<byte>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); HANDLE_BLIT(2, 2, uint16, uint16)
else if (format.bytesPerPixel == 2) HANDLE_BLIT(4, 4, uint32, uint32)
transBlit<uint16>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); HANDLE_BLIT(2, 4, uint16, uint32)
else if (format.bytesPerPixel == 4) HANDLE_BLIT(4, 2, uint32, uint16)
transBlit<uint32>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor);
else
error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4"); error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4");
// Mark the affected area // Mark the affected area
addDirtyRect(destRect); addDirtyRect(destRect);
} }
#undef HANDLE_BLIT
void ManagedSurface::markAllDirty() { void ManagedSurface::markAllDirty() {
addDirtyRect(Common::Rect(0, 0, this->w, this->h)); addDirtyRect(Common::Rect(0, 0, this->w, this->h));
} }