Files
scummvm-cursorfix/engines/m4/graphics/gr_surface.cpp
2026-02-02 04:50:13 +01:00

195 lines
5.3 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/algorithm.h"
#include "m4/graphics/gr_surface.h"
#include "m4/graphics/gr_sprite.h"
namespace M4 {
M4Surface::M4Surface(int sw, int sh) : Buffer() {
this->w = sw;
this->h = sh;
this->stride = sw;
this->encoding = NO_COMPRESS;
this->data = new byte[sw * sh];
Common::fill(this->data, this->data + sw * sh, 0);
_disposeAfterUse = DisposeAfterUse::YES;
}
M4Surface::M4Surface(const byte *src, int sw, int sh) {
this->w = sw;
this->h = sh;
this->stride = sw;
this->encoding = NO_COMPRESS;
this->data = new byte[sw * sh];
Common::fill(this->data, this->data + sw * sh, 0);
_disposeAfterUse = DisposeAfterUse::YES;
rleDraw(src);
}
M4Surface::~M4Surface() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete[] data;
}
void M4Surface::rleDraw(const byte *src, int x, int y) {
const byte *srcP = src;
byte *destData = data + y * w + x;
byte *destP = destData;
int destWidth = w;
byte count, val;
int line = 0;
assert(x >= 0 && y >= 0 && x < w && y < h);
for (;;) {
count = *srcP++;
if (count) {
// Basic run length
val = *srcP++;
// 0 pixels are transparent, and are skipped. Otherwise, draw pixels
if (val != 0)
Common::fill(destP, destP + count, val);
destP += count;
} else {
count = *srcP++;
if (count >= 3) {
// Block of uncompressed pixels to copy
for (; count > 0; --count, ++destP) {
val = *srcP++;
if (val != 0)
*destP = val;
}
} else if (!(count & 3)) {
// End of line code
++line;
destP = destData + line * destWidth;
} else {
// Stop drawing image. Seems weird that it doesn't handle the X/Y offset
// form for count & 2, but the original explicitly doesn't implement it
break;
}
}
}
assert(destP <= (data + h * stride));
}
void M4Surface::draw(const Buffer &src, int x, int y, bool forwards,
const byte *depthCodes, int srcDepth, const byte *inverseColorTable,
const byte *palette) {
if ((src.encoding & 0x7f) == RLE8) {
// The standard case of RLE sprite drawing onto screen can directly
// use RLE decompression for performance
if (forwards && !depthCodes && !inverseColorTable && x >= 0 && y >= 0 &&
(x + src.w) <= this->w && (y + src.h) <= this->h) {
rleDraw(src.data, x, y);
} else {
// All other RLE drawing first decompresses the sprite, and then does
// the various clipping, reverse, etc. on that
M4Surface tmp(src.data, src.w, src.h);
drawInner(tmp, depthCodes, x, y, forwards, srcDepth, palette, inverseColorTable);
}
} else {
// Uncompressed images get passed to inner drawing
drawInner(src, depthCodes, x, y, forwards, srcDepth, palette, inverseColorTable);
}
}
void M4Surface::drawInner(const Buffer &src, const byte *depthCodes, int x, int y,
bool forwards, int srcDepth, const byte *palette, const byte *inverseColorTable) {
assert((src.encoding & 0x7f) == NO_COMPRESS);
for (int srcY = 0; srcY < src.h; ++srcY, ++y) {
if (y >= h)
// Below bottom of screen
break;
else if (y < 0)
// Above top of screen
continue;
const byte *srcP = forwards ? src.getBasePtr(0, srcY) : src.getBasePtr(src.w - 1, srcY);
byte *destP = getBasePtr(x, y);
const byte *depthP = depthCodes ? depthCodes + y * w + x : nullptr;
int deltaX = forwards ? 1 : -1;
int destX = x;
uint32 adjusted, total;
for (int srcX = 0; srcX < src.w; ++srcX, srcP += deltaX, ++destX) {
if (destX >= w)
// Beyond right of screen
break;
byte v = *srcP;
byte depth = depthP ? *depthP & 0xf : 0;
if (destX >= 0 && v != 0 && (!depthP || depth == 0 || srcDepth < depth)) {
if (inverseColorTable) {
// Handling for shadows
if (v != 128) {
const byte *palP = palette + *destP * 3;
uint rgb = (uint32)palP[0] | ((uint32)palP[1] << 8) |
((uint32)palP[2] << 16);
rgb >>= 2;
// Red component
adjusted = (rgb & 0xff) * v;
adjusted = MIN((uint)(adjusted >> 8), 31U);
total = adjusted << 10;
// Green component
rgb >>= 8;
adjusted = (rgb & 0xff) * v;
adjusted = MIN((uint)(adjusted >> 8), 31U);
total |= (adjusted << 5);
// Blue component
rgb >>= 8;
adjusted = (rgb & 0xff) * v;
adjusted = MIN((uint)(adjusted >> 8), 31U);
total |= adjusted;
// Write out pixel from inverse table
*destP = inverseColorTable[total];
}
} else {
// Normal pixel
*destP = v;
}
}
++destP;
if (depthP)
++depthP;
}
}
}
} // namespace M4