Add gamma correct pixel blender
This commit is contained in:
parent
c41d6a82e1
commit
bc74b2860d
|
@ -1,5 +1,9 @@
|
||||||
#include "font_renderer.h"
|
#include "font_renderer.h"
|
||||||
|
|
||||||
|
#include "agg_pixfmt_rgb.h"
|
||||||
|
#include "agg_pixfmt_rgba.h"
|
||||||
|
#include "agg_gamma_lut.h"
|
||||||
|
|
||||||
#include "font_renderer_alpha.h"
|
#include "font_renderer_alpha.h"
|
||||||
|
|
||||||
FontRenderer *FontRendererNew(unsigned int flags) {
|
FontRenderer *FontRendererNew(unsigned int flags) {
|
||||||
|
@ -95,3 +99,51 @@ int FontRendererBakeFontBitmap(FontRenderer *fr_, int font_height,
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Blender>
|
||||||
|
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<agg::rgba8, agg::order_bgra, agg::gamma_lut<> > 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<blender_type>(dst_ren_buf, blender, x, y, region_width, color_a, covers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
#ifndef FONT_RENDERER_H
|
#ifndef FONT_RENDERER_H
|
||||||
#define FONT_RENDERER_H
|
#define FONT_RENDERER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Mirrors stbtt_bakedchar.
|
// Mirrors stbtt_bakedchar.
|
||||||
struct GlyphBitmapInfo_ {
|
typedef struct {
|
||||||
unsigned short x0, y0, x1, y1;
|
unsigned short x0, y0, x1, y1;
|
||||||
float xoff, yoff, xadvance;
|
float xoff, yoff, xadvance;
|
||||||
};
|
} GlyphBitmapInfo;
|
||||||
typedef struct GlyphBitmapInfo_ GlyphBitmapInfo;
|
|
||||||
|
|
||||||
struct FontRenderer_;
|
struct FontRenderer_;
|
||||||
typedef struct FontRenderer_ FontRenderer;
|
typedef struct FontRenderer_ FontRenderer;
|
||||||
|
@ -20,14 +21,27 @@ enum {
|
||||||
FONT_RENDERER_KERNING = 1 << 1,
|
FONT_RENDERER_KERNING = 1 << 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
FontRenderer *FontRendererNew(unsigned int flags);
|
typedef struct {
|
||||||
|
uint8_t r, g, b;
|
||||||
|
} FontRendererColor;
|
||||||
|
|
||||||
|
FontRenderer * FontRendererNew(unsigned int flags);
|
||||||
|
|
||||||
void FontRendererFree(FontRenderer *);
|
void FontRendererFree(FontRenderer *);
|
||||||
|
|
||||||
int FontRendererLoadFont(FontRenderer *, const char *filename);
|
int FontRendererLoadFont(FontRenderer *, const char *filename);
|
||||||
|
|
||||||
int FontRendererGetFontHeight(FontRenderer *, float size);
|
int FontRendererGetFontHeight(FontRenderer *, float size);
|
||||||
|
|
||||||
int FontRendererBakeFontBitmap(FontRenderer *, int font_height,
|
int FontRendererBakeFontBitmap(FontRenderer *, int font_height,
|
||||||
void *pixels, int pixels_width, int pixels_height,
|
void *pixels, int pixels_width, int pixels_height,
|
||||||
int first_char, int num_chars, GlyphBitmapInfo *glyph_info);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,8 +12,13 @@ struct RenImage {
|
||||||
int width, height;
|
int width, height;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *pixels;
|
||||||
|
int width, height;
|
||||||
|
} RenCoverageImage;
|
||||||
|
|
||||||
struct GlyphSet {
|
struct GlyphSet {
|
||||||
RenImage *image;
|
RenCoverageImage *coverage;
|
||||||
GlyphBitmapInfo glyphs[256];
|
GlyphBitmapInfo glyphs[256];
|
||||||
};
|
};
|
||||||
typedef struct GlyphSet GlyphSet;
|
typedef struct GlyphSet GlyphSet;
|
||||||
|
@ -100,11 +105,23 @@ RenImage* ren_new_image(int width, int height) {
|
||||||
return image;
|
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) {
|
void ren_free_image(RenImage *image) {
|
||||||
free(image);
|
free(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ren_free_coverage(RenCoverageImage *coverage) {
|
||||||
|
free(coverage);
|
||||||
|
}
|
||||||
|
|
||||||
static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
||||||
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
||||||
|
@ -113,17 +130,17 @@ static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
||||||
int width = 128;
|
int width = 128;
|
||||||
int height = 128;
|
int height = 128;
|
||||||
retry:
|
retry:
|
||||||
set->image = ren_new_image(width, height);
|
set->coverage = ren_new_coverage(width, height);
|
||||||
|
|
||||||
int res = FontRendererBakeFontBitmap(font->renderer, font->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);
|
idx << 8, 256, set->glyphs);
|
||||||
|
|
||||||
/* retry with a larger image buffer if the buffer wasn't large enough */
|
/* retry with a larger image buffer if the buffer wasn't large enough */
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
width *= 2;
|
width *= 2;
|
||||||
height *= 2;
|
height *= 2;
|
||||||
ren_free_image(set->image);
|
ren_free_coverage(set->coverage);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,11 +149,13 @@ retry:
|
||||||
set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance);
|
set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* convert 8bit data to 32bit */
|
/* convert 8bit data to 32bit */
|
||||||
for (int i = width * height - 1; i >= 0; i--) {
|
for (int i = width * height - 1; i >= 0; i--) {
|
||||||
uint8_t n = *((uint8_t*) set->image->pixels + i);
|
uint8_t n = *((uint8_t*) set->image->pixels + i);
|
||||||
set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n };
|
set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n };
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +197,7 @@ void ren_free_font(RenFont *font) {
|
||||||
for (int i = 0; i < MAX_GLYPHSET; i++) {
|
for (int i = 0; i < MAX_GLYPHSET; i++) {
|
||||||
GlyphSet *set = font->sets[i];
|
GlyphSet *set = font->sets[i];
|
||||||
if (set) {
|
if (set) {
|
||||||
ren_free_image(set->image);
|
ren_free_coverage(set->coverage);
|
||||||
free(set);
|
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) {
|
void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) {
|
||||||
if (color.a == 0) { return; }
|
if (color.a == 0) { return; }
|
||||||
|
|
||||||
/* clip */
|
clip_point_inside_rect(&x, &y, sub);
|
||||||
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; }
|
|
||||||
|
|
||||||
if (sub->width <= 0 || sub->height <= 0) {
|
if (sub->width <= 0 || sub->height <= 0) {
|
||||||
return;
|
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) {
|
int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) {
|
||||||
RenRect rect;
|
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.y = g->y0;
|
||||||
rect.width = g->x1 - g->x0;
|
rect.width = g->x1 - g->x0;
|
||||||
rect.height = g->y1 - g->y0;
|
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;
|
x += g->xadvance;
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
|
|
Loading…
Reference in New Issue