Decompile fn_01842 (CDCBitmap::ApplyPixelFilter)

Batch #10783 by kyoshi: 0 byte-identical, 1 confirmed by peer review.

- fn_01842 CDCBitmap::ApplyPixelFilter: logic confirmed by peer review

Peer-reviewed-by: Tanke, cremane, StrawberrysherbetIX
Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes).
This commit is contained in:
ragnarok-clanker 2026-11-04 17:20:51 +00:00
commit 457f9670ac
4 changed files with 138 additions and 61 deletions

View file

@ -99,13 +99,6 @@ void CDCBitmap::DrawRleSprite(
this->m_bitmap.DrawRleSprite(x, y, cells, images, palette, cellCount);
}
// OFFSET: 0x005460B0
void CDCBitmap::DrawSurface(
int x, int y, CSurface* source, int sourceX, int sourceY, int sourceWidth, int sourceHeight, bool mirrored, int scaleX, int scaleY, int filter, int format
) {
this->m_bitmap.DrawSurface(x, y, source, sourceX, sourceY, sourceWidth, sourceHeight, mirrored, scaleX, scaleY, filter, format);
}
// OFFSET: 0x00547460
void CDCBitmap::Fill(const RECT* rectangle, std::uint32_t color) {
this->m_bitmap.Fill(rectangle, color);

View file

@ -40,6 +40,7 @@ public:
void Dim(int x, int y, int width, int height, int percent) override;
void UploadTexture(CRenderSystemBase& system, CTexture*& texture);
HGDIOBJ Create(int width, int height);
void ApplyPixelFilter(int filter, unsigned int* red, unsigned int* green, unsigned int* blue);
void SetAlpha(unsigned char maxLuminance);
// INLINED
void SetAlpha(bool alpha) {

View file

@ -332,12 +332,23 @@ void BitmapPixels::CopyRegion(
}
}
// INLINED
static std::uint32_t PackPixel(unsigned alpha, unsigned red, unsigned green, unsigned blue) {
return ((((alpha << 8) + static_cast<unsigned char>(red)) << 8) + static_cast<unsigned char>(green) << 8) + static_cast<unsigned char>(blue);
}
// INLINED
static float SurfacePixelAlpha(int value) {
const float alpha = static_cast<float>(static_cast<unsigned>(value) >> 12) * 0.06666667f;
return alpha > 0.97f ? 1.0f : alpha;
}
// OFFSET: 0x005460B0
// FUNCTION: fn_01839
void BitmapPixels::DrawSurface(
int x, int y, CSurface* source, int sourceX, int sourceY, int sourceWidth, int sourceHeight, bool mirrored, int scaleX, int scaleY, int filter, int format
void CDCBitmap::DrawSurface(
int x, int y, CSurface* source, int sourceX, int sourceY, int sourceWidth, int sourceHeight, bool mirrored, int scaleX, int scaleY, int filter, int
) {
if (!source || !this->pixels) {
if (!source || !this->m_bitmap.pixels) {
return;
}
@ -346,64 +357,139 @@ void BitmapPixels::DrawSurface(
return;
}
this->dirty = true;
const auto* input = static_cast<const unsigned char*>(locked.pixels);
const auto* input = static_cast<const std::uint16_t*>(locked.pixels);
const int stride = locked.pitch / 2;
this->m_bitmap.dirty = true;
const int format = static_cast<CTexture*>(source)->Format();
const int width = this->m_bitmap.width;
std::uint32_t* pixels = this->m_bitmap.pixels;
unsigned red, green, blue;
for (int row = 0; row < sourceHeight; ++row) {
for (int column = 0; column < sourceWidth; ++column) {
const int inputX = sourceX + (mirrored ? sourceWidth - column - 1 : column);
std::uint16_t value;
std::memcpy(&value, input + 2 * (inputX + (sourceY + row) * stride), sizeof(value));
if (format == TextureFormat::A4R4G4B4) {
if (mirrored) {
for (int row = 0; row < sourceHeight; ++row) {
for (int column = sourceWidth - 1; column >= 0; --column) {
const int value = input[(sourceY + row) * stride + sourceX + column];
if (!(value & 0xF000)) {
continue;
}
if ((format == TextureFormat::A1R5G5B5 && !(value & 0x8000)) || (format == TextureFormat::A4R4G4B4 && !(value & 0xF000)) ||
(format != TextureFormat::A1R5G5B5 && format != TextureFormat::A4R4G4B4)) {
continue;
}
const float alpha = SurfacePixelAlpha(value);
red = (value >> 4) & 0xF0;
green = value & 0xF0;
blue = (value & 0xF) << 4;
this->ApplyPixelFilter(filter, &red, &green, &blue);
unsigned red = format != TextureFormat::A1R5G5B5 ? (value >> 4) & 0xF0 : (value >> 7) & 0xF8;
unsigned green = format != TextureFormat::A1R5G5B5 ? value & 0xF0 : (value >> 2) & 0xF8;
unsigned blue = format != TextureFormat::A1R5G5B5 ? (value & 15) << 4 : (value & 31) << 3;
if (filter) {
const unsigned average = std::max((red + green + blue) / 3, filter == 1 ? 120u : 100u);
red = green = blue = average;
if (filter == 2) {
red = 200;
} else if (filter == 3) {
green = 200;
} else if (filter == 4) {
blue = 200;
for (int dy = 0; dy < scaleY; ++dy) {
for (int dx = 0; dx < scaleX; ++dx) {
const int destinationX = x + (sourceWidth - column - 1) * scaleX + dx;
const int destinationY = y + row * scaleY + dy;
if (destinationX >= 0 && destinationY >= 0 && static_cast<unsigned>(destinationX) < this->m_bitmap.width &&
static_cast<unsigned>(destinationY) < this->m_bitmap.height) {
const float inverse = 1.0f - alpha;
const int index = destinationX + destinationY * width;
const unsigned outputRed =
static_cast<unsigned>(static_cast<float>(static_cast<unsigned char>(pixels[index] >> 16)) * inverse + static_cast<float>(red) * alpha);
const unsigned outputGreen =
static_cast<unsigned>(static_cast<float>(static_cast<unsigned char>(pixels[index] >> 8)) * inverse + static_cast<float>(green) * alpha);
const unsigned outputBlue =
static_cast<unsigned>(static_cast<float>(pixels[index] & 0xFF) * inverse + static_cast<float>(blue) * alpha);
pixels[index] = PackPixel(0, outputRed, outputGreen, outputBlue);
}
}
}
}
}
} else {
for (int row = 0; row < sourceHeight; ++row) {
for (int column = 0; column < sourceWidth; ++column) {
const int value = input[(sourceY + row) * stride + sourceX + column];
if (!(value & 0xF000)) {
continue;
}
float alpha = 1;
if (format == TextureFormat::A4R4G4B4) {
alpha = static_cast<float>(value >> 12) * 0.06666667f;
if (alpha > 0.97f) {
alpha = 1;
const float alpha = SurfacePixelAlpha(value);
red = (value >> 4) & 0xF0;
green = value & 0xF0;
blue = (value & 0xF) << 4;
this->ApplyPixelFilter(filter, &red, &green, &blue);
if (!&pixels[(y + (row + 1) * scaleY - 1) * width + x + column * scaleX + scaleX - 1]) {
continue;
}
for (int dy = 0; dy < scaleY; ++dy) {
for (int dx = 0; dx < scaleX; ++dx) {
const int destinationX = x + column * scaleX + dx;
const int destinationY = y + row * scaleY + dy;
if (destinationX >= 0 && destinationY >= 0 && static_cast<unsigned>(destinationX) < this->m_bitmap.width &&
static_cast<unsigned>(destinationY) < this->m_bitmap.height) {
const float inverse = 1.0f - alpha;
const int index = destinationX + destinationY * width;
const unsigned outputRed =
static_cast<unsigned>(static_cast<float>(static_cast<unsigned char>(pixels[index] >> 16)) * inverse + static_cast<float>(red) * alpha);
const unsigned outputGreen =
static_cast<unsigned>(static_cast<float>(static_cast<unsigned char>(pixels[index] >> 8)) * inverse + static_cast<float>(green) * alpha);
const unsigned outputBlue =
static_cast<unsigned>(static_cast<float>(pixels[index] & 0xFF) * inverse + static_cast<float>(blue) * alpha);
pixels[index] = PackPixel(0, outputRed, outputGreen, outputBlue);
}
}
}
}
}
}
} else if (format == TextureFormat::A1R5G5B5) {
if (mirrored) {
for (int row = 0; row < sourceHeight; ++row) {
for (int column = sourceWidth - 1; column >= 0; --column) {
const int value = input[(sourceY + row) * stride + sourceX + column];
if (!(value & 0x8000)) {
continue;
}
for (int dy = 0; dy < scaleY; ++dy) {
const int destinationY = y + row * scaleY + dy;
red = (value >> 7) & 0xF8;
green = (value >> 2) & 0xF8;
blue = (value & 0x1F) << 3;
this->ApplyPixelFilter(filter, &red, &green, &blue);
for (int dx = 0; dx < scaleX; ++dx) {
const int destinationX = x + column * scaleX + dx;
if (destinationX >= 0 && destinationY >= 0 && static_cast<unsigned>(destinationX) < this->width &&
static_cast<unsigned>(destinationY) < this->height) {
std::uint32_t& destination = this->pixels[destinationX + destinationY * this->width];
if (format == TextureFormat::A1R5G5B5) {
destination = 0xFF000000 | (red << 16) | (green << 8) | blue;
} else {
const float inverse = 1 - alpha;
const auto b =
static_cast<unsigned char>(static_cast<unsigned>(static_cast<float>(destination & 255) * inverse + static_cast<float>(blue) * alpha));
const auto g =
static_cast<unsigned char>(static_cast<unsigned>(static_cast<float>((destination >> 8) & 255) * inverse + static_cast<float>(green) * alpha));
const auto r =
static_cast<unsigned char>(static_cast<unsigned>(static_cast<float>((destination >> 16) & 255) * inverse + static_cast<float>(red) * alpha));
destination = b | (g << 8) | (r << 16);
for (int dy = 0; dy < scaleY; ++dy) {
for (int dx = 0; dx < scaleX; ++dx) {
const int destinationX = x + (sourceWidth - column - 1) * scaleX + dx;
const int destinationY = y + row * scaleY + dy;
if (destinationX >= 0 && destinationY >= 0 && static_cast<unsigned>(destinationX) < this->m_bitmap.width &&
static_cast<unsigned>(destinationY) < this->m_bitmap.height) {
pixels[destinationX + destinationY * width] = PackPixel(0xFF, red, green, blue);
}
}
}
}
}
} else {
for (int row = 0; row < sourceHeight; ++row) {
for (int column = 0; column < sourceWidth; ++column) {
const int value = input[(sourceY + row) * stride + sourceX + column];
if (!(value & 0x8000)) {
continue;
}
red = (value >> 7) & 0xF8;
green = (value >> 2) & 0xF8;
blue = (value & 0x1F) << 3;
this->ApplyPixelFilter(filter, &red, &green, &blue);
if (!&pixels[(y + (row + 1) * scaleY - 1) * width + x + column * scaleX + scaleX - 1]) {
continue;
}
for (int dy = 0; dy < scaleY; ++dy) {
for (int dx = 0; dx < scaleX; ++dx) {
const int destinationX = x + column * scaleX + dx;
const int destinationY = y + row * scaleY + dy;
if (destinationX >= 0 && destinationY >= 0 && static_cast<unsigned>(destinationX) < this->m_bitmap.width &&
static_cast<unsigned>(destinationY) < this->m_bitmap.height) {
pixels[destinationX + destinationY * width] = PackPixel(0xFF, red, green, blue);
}
}
}
}
@ -704,7 +790,7 @@ void BitmapPixels::ScaleCopy(int x, int y, int sourceWidth, int sourceHeight, in
// OFFSET: 0x00547670
// FUNCTION: fn_01842
void ApplyPixelFilter(int filter, unsigned int* red, unsigned int* green, unsigned int* blue) {
void CDCBitmap::ApplyPixelFilter(int filter, unsigned int* red, unsigned int* green, unsigned int* blue) {
if (!filter) {
return;
}

View file

@ -35,9 +35,6 @@ struct BitmapPixels {
int x, int y, int sourceLeft, int sourceTop, int sourceRight, int sourceBottom, const std::uint32_t* source, int sourceWidth, int sourceHeight,
bool transparent, bool alpha
);
void DrawSurface(
int x, int y, CSurface* source, int sourceX, int sourceY, int sourceWidth, int sourceHeight, bool mirrored, int scaleX, int scaleY, int filter, int format
);
void DrawScaledSurface(
int x, int y, int width, int height, CSurface* source, int sourceWidth, int sourceHeight, float scaleX, float scaleY, std::uint32_t color, bool mirrored,
int offsetX, int offsetY, int sourceX, int sourceY