Files
2026-02-02 04:50:13 +01:00

230 lines
6.4 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/>.
*
*/
#include "common/textconsole.h"
#include "bagel/mfc/gfx/blitter.h"
#include "bagel/mfc/afxwin.h"
namespace Bagel {
namespace MFC {
namespace Gfx {
static inline void copyPixel(const byte *srcP, byte *destP, int mode, const byte &WHITE,
bool isDestMonochrome, uint bgColor, const uint32 *paletteMap) {
byte src = *srcP;
if (paletteMap)
src = paletteMap[src];
switch (mode) {
case SRCCOPY:
*destP = src;
break;
case SRCAND:
*destP &= src;
break;
case SRCINVERT:
*destP ^= src;
break;
case SRCPAINT:
*destP |= src;
break;
case NOTSRCCOPY:
if (isDestMonochrome) {
*destP = src == bgColor ? 0 : 0xff;
return;
}
*destP = ~src;
break;
case DSTINVERT:
*destP = ~*destP;
return;
case BLACKNESS:
*destP = 0;
return;
case WHITENESS:
*destP = WHITE;
return;
default:
error("Unsupported blit mode");
break;
}
if (isDestMonochrome)
*destP = *destP == bgColor ? 0xff : 0;
}
static void blitInner(Gfx::Surface *srcSurface,
Gfx::Surface *destSurface,
const Common::Rect &srcRect, const Common::Point &destPos,
uint bgColor, int mode, const uint32 *paletteMap) {
const bool isDestMonochrome = destSurface->format.bytesPerPixel == 1 &&
destSurface->format.aLoss == 255;
const byte WHITE = 255;
Surface::YIterator ySrc(srcSurface);
Surface::XIterator xSrc(&ySrc);
Surface::YIterator yDest(destSurface);
Surface::XIterator xDest(&yDest);
byte dummy = 0;
byte *srcP, *destP;
for (ySrc = srcRect.top, yDest = destPos.y; ySrc < srcRect.bottom; ++ySrc, ++yDest) {
for (xSrc = srcRect.left, xDest = destPos.x; xSrc < srcRect.right; ++xSrc, ++xDest) {
srcP = xSrc;
destP = xDest;
if (!srcP)
srcP = &dummy;
if (!destP)
destP = &dummy;
copyPixel(srcP, destP, mode, WHITE, isDestMonochrome, bgColor, paletteMap);
}
}
}
static void stretchBlitInner(Gfx::Surface *srcSurface,
Gfx::Surface *destSurface,
const Common::Rect &srcRect, const Common::Rect &dstRect,
uint bgColor, int mode, const uint32 *paletteMap) {
const bool isDestMonochrome = destSurface->format.bytesPerPixel == 1 &&
destSurface->format.aLoss == 255;
const byte WHITE = 255;
const int srcWidth = srcRect.right - srcRect.left;
const int srcHeight = srcRect.bottom - srcRect.top;
const int dstWidth = dstRect.right - dstRect.left;
const int dstHeight = dstRect.bottom - dstRect.top;
Surface::YIterator ySrc(srcSurface);
Surface::XIterator xSrc(&ySrc);
Surface::YIterator yDest(destSurface);
Surface::XIterator xDest(&yDest);
if (srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0)
return; // Invalid rectangles
for (int y = 0; y < dstHeight; ++y) {
// Map destination y to source y using fixed-point arithmetic
int srcY = srcRect.top + (y * srcHeight) / dstHeight;
if (srcY >= srcSurface->h)
continue;
int dstY = dstRect.top + y;
if (dstY >= destSurface->h)
continue;
for (int x = 0; x < dstWidth; ++x) {
int srcX = srcRect.left + (x * srcWidth) / dstWidth;
if (srcX >= srcSurface->w)
continue;
int dstX = dstRect.left + x;
if (dstX >= destSurface->w)
continue;
xSrc = srcX;
ySrc = srcY;
byte *srcP = xSrc;
xDest = dstX;
yDest = dstY;
byte *destP = xDest;
copyPixel(srcP, destP, mode, WHITE, isDestMonochrome, bgColor, paletteMap);
}
}
}
void blit(Gfx::Surface *src, Gfx::Surface *dest,
const Common::Rect &srcRect, const Common::Point &destPos,
uint bgColor, int mode, const uint32 *paletteMap) {
// For normal copying modes, the formats must match.
// Other modes like DSTINVERT don't need a source,
// so in that case the source can remain uninitialized
assert(src->format.bytesPerPixel == dest->format.bytesPerPixel ||
src->format.bytesPerPixel == 0);
assert(dest->format.bytesPerPixel == dest->format.bytesPerPixel ||
dest->format.bytesPerPixel == 0);
blitInner(src, dest, srcRect, destPos, bgColor, mode, paletteMap);
Common::Rect dirtyRect(destPos.x, destPos.y,
destPos.x + srcRect.width(), destPos.y + srcRect.height());
dest->addDirtyRect(dirtyRect);
}
void stretchBlit(Gfx::Surface *src, Gfx::Surface *dest,
const Common::Rect &srcRect, const Common::Rect &destRect,
uint bgColor, int mode, const uint32 *paletteMap) {
assert(src->format.bytesPerPixel == dest->format.bytesPerPixel ||
src->format.bytesPerPixel == 0);
assert(dest->format.bytesPerPixel == dest->format.bytesPerPixel ||
dest->format.bytesPerPixel == 0);
stretchBlitInner(src, dest, srcRect, destRect, bgColor, mode, paletteMap);
}
static inline void rasterPixel(byte *pixel, byte) {
// Currently only R2_NOT
*pixel = ~*pixel;
}
void frameRect(Gfx::Surface *dest,
const Common::Rect &r, byte color, int drawMode) {
assert(dest->format.bytesPerPixel == 1);
if (drawMode == R2_COPYPEN) {
dest->frameRect(r, color);
return;
} else if (drawMode == R2_NOP) {
return;
}
assert(drawMode == R2_NOT);
const int w = r.right - r.left;
const int h = r.bottom - r.top - 2;
byte *pixel;
// Top line
pixel = (byte *)dest->getBasePtr(r.left, r.top);
for (int x = 0; x < w; ++x, ++pixel)
rasterPixel(pixel, color);
// Bottom line
pixel = (byte *)dest->getBasePtr(r.left, r.bottom - 1);
for (int x = 0; x < w; ++x, ++pixel)
rasterPixel(pixel, color);
// Left edge
pixel = (byte *)dest->getBasePtr(r.left, r.top + 1);
for (int y = 0; y < h; ++y, pixel += dest->pitch)
rasterPixel(pixel, color);
// Right edge
pixel = (byte *)dest->getBasePtr(r.right - 1, r.top + 1);
for (int y = 0; y < h; ++y, pixel += dest->pitch)
rasterPixel(pixel, color);
// Mark the rectangle area as dirty
dest->addDirtyRect(r);
}
} // namespace Gfx
} // namespace MFC
} // namespace Bagel