>From 79c60e678c09e862166e53159c46756f9b34202a Mon Sep 17 00:00:00 2001 From: greg Date: Sat, 28 Feb 2009 05:52:48 +0100 Subject: [PATCH 2/2] Composite/combine overlapping glyph borders and shadows. --- libass/ass_cache.c | 50 ++++++++++++++++++++++++ libass/ass_cache.h | 21 ++++++++++ libass/ass_render.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 177 insertions(+), 1 deletions(-) diff --git a/libass/ass_cache.c b/libass/ass_cache.c index 41645c4..0561874 100644 --- a/libass/ass_cache.c +++ b/libass/ass_cache.c @@ -324,3 +324,53 @@ void ass_glyph_cache_reset(void) ass_glyph_cache_done(); ass_glyph_cache_init(); } + + +//--------------------------------- +// composite cache + +hashmap_t* composite_cache; + +static void composite_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) +{ + composite_hash_val_t* v = value; + free(v->a); + free(v->b); + free(key); + free(value); +} + +void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val) +{ + return hashmap_insert(composite_cache, key, val); +} + +/** + * \brief Get a composite bitmap from composite cache. + * \param key hash key + * \return requested hash val or 0 if not found +*/ +composite_hash_val_t* cache_find_composite(composite_hash_key_t* key) +{ + return hashmap_find(composite_cache, key); +} + +void ass_composite_cache_init(void) +{ + composite_cache = hashmap_init(sizeof(composite_hash_key_t), + sizeof(composite_hash_val_t), + 0xFFFF + 13, + composite_hash_dtor, NULL, NULL); +} + +void ass_composite_cache_done(void) +{ + hashmap_done(composite_cache); +} + +void ass_composite_cache_reset(void) +{ + ass_composite_cache_done(); + ass_composite_cache_init(); +} + diff --git a/libass/ass_cache.h b/libass/ass_cache.h index fdb8a64..f7042be 100644 --- a/libass/ass_cache.h +++ b/libass/ass_cache.h @@ -65,6 +65,27 @@ bitmap_hash_val_t* cache_find_bitmap(bitmap_hash_key_t* key); void ass_bitmap_cache_reset(void); void ass_bitmap_cache_done(void); + +// Cache for composited bitmaps +typedef struct composite_hash_key_s { + int aw, ah, bw, bh; + int ax, ay, bx, by; + bitmap_hash_key_t a; + bitmap_hash_key_t b; +} composite_hash_key_t; + +typedef struct composite_hash_val_s { + unsigned char* a; + unsigned char* b; +} composite_hash_val_t; + +void ass_composite_cache_init(void); +void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val); +composite_hash_val_t* cache_find_composite(composite_hash_key_t* key); +void ass_composite_cache_reset(void); +void ass_composite_cache_done(void); + + // describes an outline glyph typedef struct glyph_hash_key_s { ass_font_t* font; diff --git a/libass/ass_render.c b/libass/ass_render.c index a40adec..0cce3f7 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -272,6 +272,7 @@ ass_renderer_t* ass_renderer_init(ass_library_t* library) ass_font_cache_init(); ass_bitmap_cache_init(); + ass_composite_cache_init(); ass_glyph_cache_init(); text_info.glyphs = calloc(MAX_GLYPHS, sizeof(glyph_info_t)); @@ -287,6 +288,7 @@ void ass_renderer_done(ass_renderer_t* priv) { ass_font_cache_done(); ass_bitmap_cache_done(); + ass_composite_cache_done(); ass_glyph_cache_done(); if (render_context.stroker) { FT_Stroker_Done(render_context.stroker); @@ -399,6 +401,93 @@ static ass_image_t** render_glyph(bitmap_t* bm, int dst_x, int dst_y, uint32_t c } /** + * \brief Calculate overlapping area of two consecutive bitmaps and in case they + * overlap, composite them together + * Mainly useful for translucent glyphs and especially borders, to avoid the + * luminance adding up where they overlap (which looks ugly) + */ +static void render_overlap(ass_image_t** last_tail, ass_image_t** tail, bitmap_hash_key_t *last_hash, bitmap_hash_key_t* hash) { + int left, top, bottom, right; + int old_left, old_top, w, h, cur_left, cur_top; + int x, y, opos, cpos; + char m; + composite_hash_key_t hk; + composite_hash_val_t *hv; + composite_hash_key_t *nhk; + int ax = (*last_tail)->dst_x; + int ay = (*last_tail)->dst_y; + int aw = (*last_tail)->w; + int ah = (*last_tail)->h; + int bx = (*tail)->dst_x; + int by = (*tail)->dst_y; + int bw = (*tail)->w; + int bh = (*tail)->h; + unsigned char* a; + unsigned char* b; + + if ((*last_tail)->bitmap == (*tail)->bitmap) + return; + + // Calculate overlap coordinates + left = (ax > bx) ? ax : bx; + top = (ay > by) ? ay : by; + right = ((ax+aw) < (bx+bw)) ? (ax+aw) : (bx+bw); + bottom = ((ay+ah) < (by+bh)) ? (ay+ah) : (by+bh); + if ((right <= left) || (bottom <= top)) + return; + old_left = left-(ax); + old_top = top-(ay); + w = right-left; + h = bottom-top; + cur_left = left-(bx); + cur_top = top-(by); + + // Query cache + memcpy(&hk.a, last_hash, sizeof(*last_hash)); + memcpy(&hk.b, hash, sizeof(*hash)); + hk.aw = aw; + hk.ah = ah; + hk.bw = bw; + hk.bh = bh; + hk.ax = ax; + hk.ay = ay; + hk.bx = bx; + hk.by = by; + hv = cache_find_composite(&hk); + if (hv) { + (*last_tail)->bitmap = hv->a; + (*tail)->bitmap = hv->b; + return; + } + + // Allocate new bitmaps and copy over data + a = (*last_tail)->bitmap; + b = (*tail)->bitmap; + (*last_tail)->bitmap = malloc(aw*ah); + (*tail)->bitmap = malloc(bw*bh); + memcpy((*last_tail)->bitmap, a, aw*ah); + memcpy((*tail)->bitmap, b, bw*bh); + + // Composite overlapping area + for (y=0; y b[cpos]) ? a[opos] : b[cpos]; + (*last_tail)->bitmap[opos] = 0; + (*tail)->bitmap[cpos] = m; + } + + // Insert bitmaps into the cache + nhk = calloc(1, sizeof(*nhk)); + memcpy(nhk, &hk, sizeof(*nhk)); + hv = calloc(1, sizeof(*hv)); + hv->a = (*last_tail)->bitmap; + hv->b = (*tail)->bitmap; + cache_add_composite(nhk, hv); +} + +/** * \brief Convert text_info_t struct to ass_image_t list * Splits glyphs in halves when needed (for \kf karaoke). */ @@ -409,6 +498,9 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y) bitmap_t* bm; ass_image_t* head; ass_image_t** tail = &head; + ass_image_t** last_tail = 0; + ass_image_t** here_tail = 0; + bitmap_hash_key_t* last_hash = 0; for (i = 0; i < text_info->length; ++i) { glyph_info_t* info = text_info->glyphs + i; @@ -419,9 +511,15 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y) pen_y = dst_y + info->pos.y + info->shadow; bm = info->bm_s; + here_tail = tail; tail = render_glyph(bm, pen_x, pen_y, info->c[3], 0, 1000000, tail); + if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) + render_overlap(last_tail, here_tail, last_hash, &info->hash_key); + last_tail = here_tail; + last_hash = &info->hash_key; } + last_tail = 0; for (i = 0; i < text_info->length; ++i) { glyph_info_t* info = text_info->glyphs + i; if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o) @@ -433,8 +531,14 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y) if ((info->effect_type == EF_KARAOKE_KO) && (info->effect_timing <= info->bbox.xMax)) { // do nothing - } else + } else { + here_tail = tail; tail = render_glyph(bm, pen_x, pen_y, info->c[2], 0, 1000000, tail); + if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) + render_overlap(last_tail, here_tail, last_hash, &info->hash_key); + last_tail = here_tail; + last_hash = &info->hash_key; + } } for (i = 0; i < text_info->length; ++i) { glyph_info_t* info = text_info->glyphs + i; @@ -2104,6 +2208,7 @@ static void ass_reconfigure(ass_renderer_t* priv) priv->render_id = ++last_render_id; ass_glyph_cache_reset(); ass_bitmap_cache_reset(); + ass_composite_cache_reset(); ass_free_images(priv->prev_images_root); priv->prev_images_root = 0; } -- 1.6.1.2