1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

using box blur & parrallelism to speed up

This commit is contained in:
Laserlicht
2025-07-13 14:43:06 +02:00
parent 448a97995c
commit 1cada2ba1e

View File

@@ -23,6 +23,7 @@
#include "../../lib/GameConstants.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_reduce.h>
#include <SDL_render.h>
#include <SDL_surface.h>
@@ -816,40 +817,44 @@ void applyAffineTransform(SDL_Surface* src, SDL_Surface* dst, double a, double b
int getLowestNonTransparentY(SDL_Surface* surface)
{
assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
assert(surface && surface->format->format == SDL_PIXELFORMAT_ARGB8888);
if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
if(SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
const int w = surface->w;
const int h = surface->h;
const int bpp = surface->format->BytesPerPixel;
auto pixels = static_cast<Uint8*>(surface->pixels);
int w = surface->w;
int h = surface->h;
int bpp = surface->format->BytesPerPixel;
auto pixels = (Uint8*)surface->pixels;
for(int y = h - 1; y >= 0; --y)
{
Uint8* row = pixels + y * surface->pitch;
for(int x = 0; x < w; ++x)
// Use parallel_reduce to find the max y with non-transparent pixel
int lowestY = tbb::parallel_reduce(
tbb::blocked_range<int>(0, h),
-1, // initial lowestY = -1 (fully transparent)
[&](const tbb::blocked_range<int>& r, int localMaxY) -> int
{
Uint32 pixel = *(Uint32*)(row + x * bpp);
Uint8 r;
Uint8 g;
Uint8 b;
Uint8 a;
SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a);
if (a > 0)
for (int y = r.begin(); y != r.end(); ++y)
{
if(SDL_MUSTLOCK(surface))
SDL_UnlockSurface(surface);
return y;
Uint8* row = pixels + y * surface->pitch;
for (int x = 0; x < w; ++x)
{
Uint32 pixel = *(Uint32*)(row + x * bpp);
Uint8 a = (pixel >> 24) & 0xFF; // Fast path for ARGB8888
if (a > 0)
{
localMaxY = std::max(localMaxY, y);
break; // no need to scan rest of row
}
}
}
return localMaxY;
},
[](int a, int b) -> int
{
return std::max(a, b);
}
}
);
if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
return -1; // fully transparent
return lowestY;
}
void fillAlphaPixelWithRGBA(SDL_Surface* surface, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
@@ -884,125 +889,54 @@ void fillAlphaPixelWithRGBA(SDL_Surface* surface, Uint8 r, Uint8 g, Uint8 b, Uin
if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
}
void gaussianBlur(SDL_Surface* surface, int amount)
void boxBlur(SDL_Surface* surface)
{
assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
if (!surface || amount <= 0) return;
assert(surface && surface->format->format == SDL_PIXELFORMAT_ARGB8888);
if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
int width = surface->w;
int height = surface->h;
int pixelCount = width * height;
auto pixels = static_cast<Uint32*>(surface->pixels);
Uint32* pixels = static_cast<Uint32*>(surface->pixels);
std::vector<Uint32> temp(pixelCount);
std::vector<Uint8> srcR(pixelCount);
std::vector<Uint8> srcG(pixelCount);
std::vector<Uint8> srcB(pixelCount);
std::vector<Uint8> srcA(pixelCount);
std::vector<Uint8> dstR(pixelCount);
std::vector<Uint8> dstG(pixelCount);
std::vector<Uint8> dstB(pixelCount);
std::vector<Uint8> dstA(pixelCount);
// Initialize src channels from surface pixels
tbb::parallel_for(tbb::blocked_range<size_t>(0, height), [&](const tbb::blocked_range<size_t>& r)
tbb::parallel_for(0, height, [&](int y)
{
for(int y = r.begin(); y != r.end(); ++y)
for (int x = 0; x < width; ++x)
{
for (int x = 0; x < width; ++x)
int sumR = 0;
int sumG = 0;
int sumB = 0;
int sumA = 0;
int count = 0;
for (int ky = -1; ky <= 1; ++ky)
{
Uint32 pixel = pixels[y * width + x];
Uint8 r;
Uint8 g;
Uint8 b;
Uint8 a;
SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a);
int idx = y * width + x;
srcR[idx] = r;
srcG[idx] = g;
srcB[idx] = b;
srcA[idx] = a;
}
}
});
// 3x3 Gaussian kernel
std::array<std::array<float, 3>, 3> kernel = {{
{{1.f/16, 2.f/16, 1.f/16}},
{{2.f/16, 4.f/16, 2.f/16}},
{{1.f/16, 2.f/16, 1.f/16}}
}};
// Apply the blur 'amount' times for stronger blur
for (int iteration = 0; iteration < amount; ++iteration)
{
tbb::parallel_for(tbb::blocked_range<size_t>(0, height), [&](const tbb::blocked_range<size_t>& r)
{
for(int y = r.begin(); y != r.end(); ++y)
{
for (int x = 0; x < width; ++x)
int ny = std::clamp(y + ky, 0, height - 1);
for (int kx = -1; kx <= 1; ++kx)
{
float sumR = 0.f;
float sumG = 0.f;
float sumB = 0.f;
float sumA = 0.f;
int nx = std::clamp(x + kx, 0, width - 1);
Uint32 pixel = pixels[ny * width + nx];
for (int ky = -1; ky <= 1; ++ky)
{
for (int kx = -1; kx <= 1; ++kx)
{
int nx = x + kx;
int ny = y + ky;
// Clamp edges
if (nx < 0) nx = 0;
else if (nx >= width) nx = width - 1;
if (ny < 0) ny = 0;
else if (ny >= height) ny = height - 1;
int nIdx = ny * width + nx;
float kval = kernel[ky + 1][kx + 1];
sumR += srcR[nIdx] * kval;
sumG += srcG[nIdx] * kval;
sumB += srcB[nIdx] * kval;
sumA += srcA[nIdx] * kval;
}
}
int idx = y * width + x;
dstR[idx] = static_cast<Uint8>(sumR);
dstG[idx] = static_cast<Uint8>(sumG);
dstB[idx] = static_cast<Uint8>(sumB);
dstA[idx] = static_cast<Uint8>(sumA);
sumA += (pixel >> 24) & 0xFF;
sumR += (pixel >> 16) & 0xFF;
sumG += (pixel >> 8) & 0xFF;
sumB += (pixel >> 0) & 0xFF;
++count;
}
}
});
// Swap src and dst for next iteration (blur chaining)
srcR.swap(dstR);
srcG.swap(dstG);
srcB.swap(dstB);
srcA.swap(dstA);
}
// After final iteration, write back to surface pixels
tbb::parallel_for(tbb::blocked_range<size_t>(0, height), [&](const tbb::blocked_range<size_t>& r)
{
for(int y = r.begin(); y != r.end(); ++y)
{
for (int x = 0; x < width; ++x)
{
int idx = y * width + x;
pixels[idx] = SDL_MapRGBA(surface->format, srcR[idx], srcG[idx], srcB[idx], srcA[idx]);
}
Uint8 a = sumA / count;
Uint8 r = sumR / count;
Uint8 g = sumG / count;
Uint8 b = sumB / count;
temp[y * width + x] = (a << 24) | (r << 16) | (g << 8) | b;
}
});
std::copy(temp.begin(), temp.end(), pixels);
if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
}
@@ -1029,7 +963,7 @@ SDL_Surface * CSDL_Ext::drawShadow(SDL_Surface * source, bool doSheer)
applyAffineTransform(sourceSurface, destSurface, a, b, c, d, tx, ty);
fillAlphaPixelWithRGBA(destSurface, 0, 0, 0, 128);
gaussianBlur(destSurface, 1);
boxBlur(destSurface);
SDL_FreeSurface(sourceSurface);