1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:58:12 +00:00

LibGL: Implement color blending

This implements different blend modes in the SoftwareRasterizer by
first setting up the blend factors then rendering the pixels into a
temporary buffer and finally mixing the contents of the temporary buffer
with the contents of the backbuffer based on the blend factors.
This commit is contained in:
Stephan Unverwerth 2021-05-15 23:40:04 +02:00 committed by Linus Groh
parent d6e9b433cf
commit f70a6ff712

View file

@ -38,6 +38,66 @@ static Gfx::RGBA32 to_rgba32(const FloatVector4& v)
return a << 24 | b << 16 | g << 8 | r;
}
static FloatVector4 to_vec4(Gfx::RGBA32 rgba)
{
return {
(rgba & 0xff) / 255.0f,
((rgba >> 8) & 0xff) / 255.0f,
((rgba >> 16) & 0xff) / 255.0f,
((rgba >> 24) & 0xff) / 255.0f
};
}
static constexpr void setup_blend_factors(GLenum mode, FloatVector4& constant, float& src_alpha, float& dst_alpha, float& src_color, float& dst_color)
{
constant = { 0.0f, 0.0f, 0.0f, 0.0f };
src_alpha = 0;
dst_alpha = 0;
src_color = 0;
dst_color = 0;
switch (mode) {
case GL_ZERO:
break;
case GL_ONE:
constant = { 1.0f, 1.0f, 1.0f, 1.0f };
break;
case GL_SRC_COLOR:
src_color = 1;
break;
case GL_ONE_MINUS_SRC_COLOR:
constant = { 1.0f, 1.0f, 1.0f, 1.0f };
src_color = -1;
break;
case GL_SRC_ALPHA:
src_alpha = 1;
break;
case GL_ONE_MINUS_SRC_ALPHA:
constant = { 1.0f, 1.0f, 1.0f, 1.0f };
src_alpha = -1;
break;
case GL_DST_ALPHA:
dst_alpha = -1;
break;
case GL_ONE_MINUS_DST_ALPHA:
constant = { 1.0f, 1.0f, 1.0f, 1.0f };
dst_alpha = -1;
break;
case GL_DST_COLOR:
dst_color = 1;
break;
case GL_ONE_MINUS_DST_COLOR:
constant = { 1.0f, 1.0f, 1.0f, 1.0f };
dst_color = -1;
break;
case GL_SRC_ALPHA_SATURATE:
// FIXME: How do we implement this?
break;
default:
VERIFY_NOT_REACHED();
}
}
template<typename PS>
static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& render_target, DepthBuffer& depth_buffer, const GLTriangle& triangle, PS pixel_shader)
{
@ -57,6 +117,36 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
float one_over_area = 1.0f / area;
FloatVector4 src_constant {};
float src_factor_src_alpha = 0;
float src_factor_dst_alpha = 0;
float src_factor_src_color = 0;
float src_factor_dst_color = 0;
FloatVector4 dst_constant {};
float dst_factor_src_alpha = 0;
float dst_factor_dst_alpha = 0;
float dst_factor_src_color = 0;
float dst_factor_dst_color = 0;
if (options.enable_blending) {
setup_blend_factors(
options.blend_source_factor,
src_constant,
src_factor_src_alpha,
src_factor_dst_alpha,
src_factor_src_color,
src_factor_dst_color);
setup_blend_factors(
options.blend_source_factor,
dst_constant,
dst_factor_src_alpha,
dst_factor_dst_alpha,
dst_factor_src_color,
dst_factor_dst_color);
}
// Obey top-left rule:
// This sets up "zero" for later pixel coverage tests.
// Depending on where on the triangle the edge is located
@ -97,6 +187,8 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
static_assert(RASTERIZER_BLOCK_SIZE < sizeof(int) * 8, "RASTERIZER_BLOCK_SIZE must be smaller than the pixel_mask's width in bits");
int pixel_mask[RASTERIZER_BLOCK_SIZE];
FloatVector4 pixel_buffer[RASTERIZER_BLOCK_SIZE][RASTERIZER_BLOCK_SIZE];
// Iterate over all blocks within the bounds of the triangle
for (int by = by0; by < by1; by++) {
for (int bx = bx0; bx < bx1; bx++) {
@ -191,7 +283,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
continue;
}
auto* pixel = &render_target.scanline(y0 + y)[x0];
auto* pixel = pixel_buffer[y];
for (int x = 0; x < RASTERIZER_BLOCK_SIZE; x++, coords += dbdx, pixel++) {
if (~pixel_mask[y] & (1 << x))
continue;
@ -215,7 +307,47 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
FloatVector2(triangle.vertices[2].u, triangle.vertices[2].v),
barycentric);
*pixel = to_rgba32(pixel_shader(uv, rgba));
*pixel = pixel_shader(uv, rgba);
}
}
if (options.enable_blending) {
// Blend color values from pixel_buffer into render_target
for (int y = 0; y < RASTERIZER_BLOCK_SIZE; y++) {
auto src = pixel_buffer[y];
auto dst = &render_target.scanline(y + y0)[x0];
for (int x = 0; x < RASTERIZER_BLOCK_SIZE; x++, src++, dst++) {
if (~pixel_mask[y] & (1 << x))
continue;
auto float_dst = to_vec4(*dst);
auto src_factor = src_constant
+ *src * src_factor_src_color
+ FloatVector4(src->w(), src->w(), src->w(), src->w()) * src_factor_src_alpha
+ float_dst * src_factor_dst_color
+ FloatVector4(float_dst.w(), float_dst.w(), float_dst.w(), float_dst.w()) * src_factor_dst_alpha;
auto dst_factor = dst_constant
+ *src * dst_factor_src_color
+ FloatVector4(src->w(), src->w(), src->w(), src->w()) * dst_factor_src_alpha
+ float_dst * dst_factor_dst_color
+ FloatVector4(float_dst.w(), float_dst.w(), float_dst.w(), float_dst.w()) * dst_factor_dst_alpha;
*dst = to_rgba32(*src * src_factor + float_dst * dst_factor);
}
}
} else {
// Copy color values from pixel_buffer into render_target
for (int y = 0; y < RASTERIZER_BLOCK_SIZE; y++) {
auto src = pixel_buffer[y];
auto dst = &render_target.scanline(y + y0)[x0];
for (int x = 0; x < RASTERIZER_BLOCK_SIZE; x++, src++, dst++) {
if (~pixel_mask[y] & (1 << x))
continue;
*dst = to_rgba32(*src);
}
}
}
}