From bc74b2860dda300e90825449266d9cc06e23e8de Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 2 Jun 2020 14:47:06 +0200 Subject: [PATCH] Add gamma correct pixel blender --- src/font_renderer.cpp | 52 +++++++++++++++++++++++++++++++++ src/font_renderer.h | 34 +++++++++++++++------- src/renderer.c | 68 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 132 insertions(+), 22 deletions(-) diff --git a/src/font_renderer.cpp b/src/font_renderer.cpp index a1325af7..d62800b9 100644 --- a/src/font_renderer.cpp +++ b/src/font_renderer.cpp @@ -1,5 +1,9 @@ #include "font_renderer.h" +#include "agg_pixfmt_rgb.h" +#include "agg_pixfmt_rgba.h" +#include "agg_gamma_lut.h" + #include "font_renderer_alpha.h" FontRenderer *FontRendererNew(unsigned int flags) { @@ -95,3 +99,51 @@ int FontRendererBakeFontBitmap(FontRenderer *fr_, int font_height, } return res; } + +template +void blend_solid_hspan(agg::rendering_buffer& rbuf, Blender& blender, + int x, int y, unsigned len, + const typename Blender::color_type& c, const agg::int8u* covers) +{ + typedef Blender blender_type; + typedef typename blender_type::color_type color_type; + typedef typename blender_type::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + + if (c.a) + { + value_type* p = (value_type*)rbuf.row_ptr(x, y, len) + (x << 2); + do + { + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + if(alpha == color_type::base_mask) + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + else + { + blender.blend_pix(p, c.r, c.g, c.b, alpha, *covers); + } + p += 4; + ++covers; + } + while(--len); + } +} + +// destination implicitly BGRA32. Source implictly single-byte alpha coverage. +void FontRendererBlendGamma(uint8_t *dst, int dst_stride, uint8_t *src, int src_stride, int region_width, int region_height, FontRendererColor color) { + typedef agg::blender_rgb_gamma > blender_type; + agg::rendering_buffer dst_ren_buf(dst, region_width, region_height, dst_stride); + const agg::rgba8 color_a(color.r, color.g, color.b); + // FIXME: move blender object inside FontRenderer. + blender_type blender; + blender.gamma(1.8); + for (int x = 0, y = 0; y < region_height; y++) { + agg::int8u *covers = src + y * src_stride; + blend_solid_hspan(dst_ren_buf, blender, x, y, region_width, color_a, covers); + } +} diff --git a/src/font_renderer.h b/src/font_renderer.h index 5905078e..904d6844 100644 --- a/src/font_renderer.h +++ b/src/font_renderer.h @@ -1,16 +1,17 @@ #ifndef FONT_RENDERER_H #define FONT_RENDERER_H +#include + #ifdef __cplusplus extern "C" { #endif // Mirrors stbtt_bakedchar. -struct GlyphBitmapInfo_ { +typedef struct { unsigned short x0, y0, x1, y1; float xoff, yoff, xadvance; -}; -typedef struct GlyphBitmapInfo_ GlyphBitmapInfo; +} GlyphBitmapInfo; struct FontRenderer_; typedef struct FontRenderer_ FontRenderer; @@ -20,13 +21,26 @@ enum { FONT_RENDERER_KERNING = 1 << 1, }; -FontRenderer *FontRendererNew(unsigned int flags); -void FontRendererFree(FontRenderer *); -int FontRendererLoadFont(FontRenderer *, const char *filename); -int FontRendererGetFontHeight(FontRenderer *, float size); -int FontRendererBakeFontBitmap(FontRenderer *, int font_height, - void *pixels, int pixels_width, int pixels_height, - int first_char, int num_chars, GlyphBitmapInfo *glyph_info); +typedef struct { + uint8_t r, g, b; +} FontRendererColor; + +FontRenderer * FontRendererNew(unsigned int flags); + +void FontRendererFree(FontRenderer *); + +int FontRendererLoadFont(FontRenderer *, const char *filename); + +int FontRendererGetFontHeight(FontRenderer *, float size); + +int FontRendererBakeFontBitmap(FontRenderer *, int font_height, + void *pixels, int pixels_width, int pixels_height, + int first_char, int num_chars, GlyphBitmapInfo *glyph_info); + +void FontRendererBlendGamma(uint8_t *dst, int dst_stride, + uint8_t *src, int src_stride, + int region_width, int region_height, + FontRendererColor color); #ifdef __cplusplus } diff --git a/src/renderer.c b/src/renderer.c index 6fc00754..aa053667 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -12,8 +12,13 @@ struct RenImage { int width, height; }; +typedef struct { + uint8_t *pixels; + int width, height; +} RenCoverageImage; + struct GlyphSet { - RenImage *image; + RenCoverageImage *coverage; GlyphBitmapInfo glyphs[256]; }; typedef struct GlyphSet GlyphSet; @@ -100,11 +105,23 @@ RenImage* ren_new_image(int width, int height) { return image; } +RenCoverageImage* ren_new_coverage(int width, int height) { + assert(width > 0 && height > 0); + RenCoverageImage *image = malloc(sizeof(RenCoverageImage) + width * height * sizeof(uint8_t)); + check_alloc(image); + image->pixels = (void*) (image + 1); + image->width = width; + image->height = height; + return image; +} void ren_free_image(RenImage *image) { free(image); } +void ren_free_coverage(RenCoverageImage *coverage) { + free(coverage); +} static GlyphSet* load_glyphset(RenFont *font, int idx) { GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); @@ -113,17 +130,17 @@ static GlyphSet* load_glyphset(RenFont *font, int idx) { int width = 128; int height = 128; retry: - set->image = ren_new_image(width, height); + set->coverage = ren_new_coverage(width, height); int res = FontRendererBakeFontBitmap(font->renderer, font->height, - (void *) set->image->pixels, width, height, + (void *) set->coverage->pixels, width, height, idx << 8, 256, set->glyphs); /* retry with a larger image buffer if the buffer wasn't large enough */ if (res < 0) { width *= 2; height *= 2; - ren_free_image(set->image); + ren_free_coverage(set->coverage); goto retry; } @@ -132,11 +149,13 @@ retry: set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance); } +#if 0 /* convert 8bit data to 32bit */ for (int i = width * height - 1; i >= 0; i--) { uint8_t n = *((uint8_t*) set->image->pixels + i); set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n }; } +#endif return set; } @@ -178,7 +197,7 @@ void ren_free_font(RenFont *font) { for (int i = 0; i < MAX_GLYPHSET; i++) { GlyphSet *set = font->sets[i]; if (set) { - ren_free_image(set->image); + ren_free_coverage(set->coverage); free(set); } } @@ -262,16 +281,19 @@ void ren_draw_rect(RenRect rect, RenColor color) { } } +static void clip_point_inside_rect(int *x, int *y, RenRect *sub) { + /* clip */ + int n; + if ((n = clip.left - (*x)) > 0) { sub->width -= n; sub->x += n; (*x) += n; } + if ((n = clip.top - (*y)) > 0) { sub->height -= n; sub->y += n; (*y) += n; } + if ((n = (*x) + sub->width - clip.right ) > 0) { sub->width -= n; } + if ((n = (*y) + sub->height - clip.bottom) > 0) { sub->height -= n; } +} void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) { if (color.a == 0) { return; } - /* clip */ - int n; - if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; } - if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; } - if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; } - if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; } + clip_point_inside_rect(&x, &y, sub); if (sub->width <= 0 || sub->height <= 0) { return; @@ -297,6 +319,28 @@ void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) } } +static void ren_draw_coverage_with_color(RenCoverageImage *image, RenRect *sub, int x, int y, RenColor color) { + if (color.a == 0) { return; } + + clip_point_inside_rect(&x, &y, sub); + + if (sub->width <= 0 || sub->height <= 0) { + return; + } + + /* draw */ + SDL_Surface *surf = SDL_GetWindowSurface(window); + uint8_t *s = image->pixels; + RenColor *d = (RenColor*) surf->pixels; + s += sub->x + sub->y * image->width; + d += x + y * surf->w; + const int surf_pixel_size = 4; + FontRendererBlendGamma( + (uint8_t *) d, surf->w * surf_pixel_size, + s, image->width, + sub->width, sub->height, + (FontRendererColor) { .r = color.r, .g = color.g, .b = color.b }); +} int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { RenRect rect; @@ -310,7 +354,7 @@ int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) rect.y = g->y0; rect.width = g->x1 - g->x0; rect.height = g->y1 - g->y0; - ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color); + ren_draw_coverage_with_color(set->coverage, &rect, x + g->xoff, y + g->yoff, color); x += g->xadvance; } return x;