changeset 28789:a0ce88ba2557

Combine adjacent overlapping, translucent glyph borders and shadows to avoid luminance build-up, which looks ugly. The resulting, modified bitmaps are stored in separate bitmap cache.
author greg
date Thu, 05 Mar 2009 20:47:33 +0000
parents 09348bd171e9
children b747efa600d8
files libass/ass_cache.c libass/ass_cache.h libass/ass_render.c
diffstat 3 files changed, 177 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/libass/ass_cache.c	Thu Mar 05 20:36:59 2009 +0000
+++ b/libass/ass_cache.c	Thu Mar 05 20:47:33 2009 +0000
@@ -324,3 +324,53 @@
 	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();
+}
+
--- a/libass/ass_cache.h	Thu Mar 05 20:36:59 2009 +0000
+++ b/libass/ass_cache.h	Thu Mar 05 20:47:33 2009 +0000
@@ -65,6 +65,27 @@
 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;
--- a/libass/ass_render.c	Thu Mar 05 20:36:59 2009 +0000
+++ b/libass/ass_render.c	Thu Mar 05 20:47:33 2009 +0000
@@ -279,6 +279,7 @@
 	
 	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));
@@ -294,6 +295,7 @@
 {
 	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);
@@ -406,6 +408,93 @@
 }
 
 /**
+ * \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<h; y++)
+		for (x=0; x<w; x++) {
+			opos = (old_top+y)*(aw) + (old_left+x);
+			cpos = (cur_top+y)*(bw) + (cur_left+x);
+			m = (a[opos] > 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).
  */
@@ -416,6 +505,9 @@
 	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;
@@ -426,9 +518,15 @@
 		pen_y = dst_y + info->pos.y + ROUND(info->shadow * frame_context.border_scale);
 		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)
@@ -440,8 +538,14 @@
 		
 		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;
@@ -2121,6 +2225,7 @@
 	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;
 }