230 lines
6.4 KiB
C++
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
|