renderer: fix color glyph rendering under OpenGL

Also changes color atlas to always use an sRGB internal format so that
the texture reads automatically linearize the colors.

Renames the misleading `rgba` atlas format to `bgra`, since both
FreeType and CoreText are set up to draw color glyphs in bgra.
This commit is contained in:
Qwerasd
2025-06-23 17:31:49 -06:00
parent f5439c860a
commit 41ae32814f
8 changed files with 25 additions and 22 deletions

View File

@@ -50,15 +50,18 @@ modified: std.atomic.Value(usize) = .{ .raw = 0 },
resized: std.atomic.Value(usize) = .{ .raw = 0 },
pub const Format = enum(u8) {
/// 1 byte per pixel grayscale.
grayscale = 0,
rgb = 1,
rgba = 2,
/// 3 bytes per pixel BGR.
bgr = 1,
/// 4 bytes per pixel BGRA.
bgra = 2,
pub fn depth(self: Format) u8 {
return switch (self) {
.grayscale => 1,
.rgb => 3,
.rgba => 4,
.bgr => 3,
.bgra => 4,
};
}
};

View File

@@ -79,7 +79,7 @@ pub fn init(
var atlas_grayscale = try Atlas.init(alloc, 512, .grayscale);
errdefer atlas_grayscale.deinit(alloc);
var atlas_color = try Atlas.init(alloc, 512, .rgba);
var atlas_color = try Atlas.init(alloc, 512, .bgra);
errdefer atlas_color.deinit(alloc);
var result: SharedGrid = .{

View File

@@ -391,7 +391,7 @@ pub const Face = struct {
const format: ?font.Atlas.Format = switch (bitmap_ft.pixel_mode) {
freetype.c.FT_PIXEL_MODE_MONO => null,
freetype.c.FT_PIXEL_MODE_GRAY => .grayscale,
freetype.c.FT_PIXEL_MODE_BGRA => .rgba,
freetype.c.FT_PIXEL_MODE_BGRA => .bgra,
else => {
log.warn("glyph={} pixel mode={}", .{ glyph_index, bitmap_ft.pixel_mode });
@panic("unsupported pixel mode");

View File

@@ -347,7 +347,7 @@ pub fn initAtlasTexture(
) Texture.Error!Texture {
const pixel_format: mtl.MTLPixelFormat = switch (atlas.format) {
.grayscale => .r8unorm,
.rgba => .bgra8unorm,
.bgra => .bgra8unorm_srgb,
else => @panic("unsupported atlas format for Metal texture"),
};

View File

@@ -440,7 +440,7 @@ pub fn initAtlasTexture(
const format: gl.Texture.Format, const internal_format: gl.Texture.InternalFormat =
switch (atlas.format) {
.grayscale => .{ .red, .red },
.rgba => .{ .rgba, .srgba },
.bgra => .{ .bgra, .srgba },
else => @panic("unsupported atlas format for OpenGL texture"),
};

View File

@@ -336,7 +336,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const color = try api.initAtlasTexture(&.{
.data = undefined,
.size = 1,
.format = .rgba,
.format = .bgra,
});
errdefer color.deinit();

View File

@@ -87,19 +87,19 @@ void main() {
case MODE_TEXT_COLOR:
{
// For now, we assume that color glyphs
// are already premultiplied sRGB colors.
// are already premultiplied linear colors.
vec4 color = texture(atlas_color, in_data.tex_coord);
// If we aren't doing linear blending, we can return this right away.
if (!use_linear_blending) {
// If we are doing linear blending, we can return this right away.
if (use_linear_blending) {
out_FragColor = color;
return;
}
// Otherwise we need to linearize the color. Since the alpha is
// premultiplied, we need to divide it out before linearizing.
// Otherwise we need to unlinearize the color. Since the alpha is
// premultiplied, we need to divide it out before unlinearizing.
color.rgb /= vec3(color.a);
color = linearize(color);
color = unlinearize(color);
color.rgb *= vec3(color.a);
out_FragColor = color;

View File

@@ -553,19 +553,19 @@ fragment float4 cell_text_fragment(
}
case MODE_TEXT_COLOR: {
// For now, we assume that color glyphs are
// already premultiplied Display P3 colors.
// For now, we assume that color glyphs
// are already premultiplied linear colors.
float4 color = textureColor.sample(textureSampler, in.tex_coord);
// If we aren't doing linear blending, we can return this right away.
if (!uniforms.use_linear_blending) {
// If we're doing linear blending, we can return this right away.
if (uniforms.use_linear_blending) {
return color;
}
// Otherwise we need to linearize the color. Since the alpha is
// premultiplied, we need to divide it out before linearizing.
// Otherwise we need to unlinearize the color. Since the alpha is
// premultiplied, we need to divide it out before unlinearizing.
color.rgb /= color.a;
color = linearize(color);
color = unlinearize(color);
color.rgb *= color.a;
return color;