diff options
author | James Courtier-Dutton <jcdutton@users.sourceforge.net> | 2004-08-06 21:35:54 +0000 |
---|---|---|
committer | James Courtier-Dutton <jcdutton@users.sourceforge.net> | 2004-08-06 21:35:54 +0000 |
commit | dca9ed9d68e6dcbdaee9d86db041bd6765bcff25 (patch) | |
tree | 4c2f1906dd163cbae218dd7277976df8bb703207 | |
parent | f500378f0d746e6c0c56986eb4437700991f2d3f (diff) | |
download | xine-lib-dca9ed9d68e6dcbdaee9d86db041bd6765bcff25.tar.gz xine-lib-dca9ed9d68e6dcbdaee9d86db041bd6765bcff25.tar.bz2 |
Improved quality of overlays.
by Reinhard Nissl.
CVS patchset: 6875
CVS date: 2004/08/06 21:35:54
-rw-r--r-- | src/video_out/alphablend.c | 228 | ||||
-rw-r--r-- | src/xine-engine/osd.c | 106 |
2 files changed, 255 insertions, 79 deletions
diff --git a/src/video_out/alphablend.c b/src/video_out/alphablend.c index a0541304b..be5f01617 100644 --- a/src/video_out/alphablend.c +++ b/src/video_out/alphablend.c @@ -713,6 +713,81 @@ static void mem_blend8(uint8_t *mem, uint8_t val, uint8_t o, size_t sz) } } + +#define EXACT_BLENDING 1 + + +#ifdef EXACT_BLENDING + +#define BLEND_MAXWIDTH 2048 +uint8_t blend_yuv_data[ 3 ][ 2 ][ BLEND_MAXWIDTH ]; +uint8_t blend_yuv_data[ 3 ][ 2 ][ BLEND_MAXWIDTH ]; +uint8_t blend_yuv_data[ 3 ][ 2 ][ BLEND_MAXWIDTH ]; + +void blend_yuv_exact(uint8_t *dst_cr, uint8_t *dst_cb, int src_width, int x_odd); +void blend_yuv_exact(uint8_t *dst_cr, uint8_t *dst_cb, int src_width, int x_odd) +{ + int x; + + for (x = 0; x < src_width; x += 2) { + /* get opacity of the 4 pixels that share chroma */ + int o00 = blend_yuv_data[ 0 ][ 0 ][ x + 0 ]; + int o01 = blend_yuv_data[ 0 ][ 0 ][ x + 1 ]; + int o10 = blend_yuv_data[ 0 ][ 1 ][ x + 0 ]; + int o11 = blend_yuv_data[ 0 ][ 1 ][ x + 1 ]; + + /* are there any pixels a little bit opaque? */ + if (o00 || o01 || o10 || o11) + { + /* get the chroma componets of the 4 pixels */ + int cr00 = -128 + blend_yuv_data[ 1 ][ 0 ][ x + 0 ]; + int cr01 = -128 + blend_yuv_data[ 1 ][ 0 ][ x + 1 ]; + int cr10 = -128 + blend_yuv_data[ 1 ][ 1 ][ x + 0 ]; + int cr11 = -128 + blend_yuv_data[ 1 ][ 1 ][ x + 1 ]; + + int cb00 = -128 + blend_yuv_data[ 2 ][ 0 ][ x + 0 ]; + int cb01 = -128 + blend_yuv_data[ 2 ][ 0 ][ x + 1 ]; + int cb10 = -128 + blend_yuv_data[ 2 ][ 1 ][ x + 0 ]; + int cb11 = -128 + blend_yuv_data[ 2 ][ 1 ][ x + 1 ]; + + /* are all pixels completely opaque? */ + if (o00 >= 0xf && o01 >= 0xf && o10 >= 0xf && o11 >= 0xf) + { + /* set the output chroma to the average of the four pixels */ + *dst_cr = 128 + (cr00 + cr01 + cr10 + cr11) / 4; + *dst_cb = 128 + (cb00 + cb01 + cb10 + cb11) / 4; + } + else + { + int t4, cr, cb; + + /* blending required, so clamp opacity values to allowed range */ + if (o00 > 0xf) o00 = 0xf; + if (o01 > 0xf) o01 = 0xf; + if (o10 > 0xf) o10 = 0xf; + if (o11 > 0xf) o11 = 0xf; + + /* calculate transparency of background over the four pixels */ + t4 = (0xf - o00) + (0xf - o01) + (0xf - o10) + (0xf - o11); + + /* get background chroma */ + cr = -128 + *dst_cr; + cb = -128 + *dst_cb; + + /* blend the output chroma to the average of the four pixels */ + *dst_cr = 128 + (cr * t4 + cr00 * o00 + cr01 * o01 + cr10 * o10 + cr11 * o11) / (4 * 0xf); + *dst_cb = 128 + (cb * t4 + cb00 * o00 + cb01 * o01 + cb10 * o10 + cb11 * o11) / (4 * 0xf); + } + } + + /* next chroma sample */ + dst_cr++; + dst_cb++; + } +} + +#endif + void blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, int dst_width, int dst_height, int dst_pitches[3]) { @@ -725,6 +800,8 @@ void blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, rle_elem_t *rle_limit = rle + img_overl->num_rle; int x_off = img_overl->x; int y_off = img_overl->y; + int x_odd = x_off & 1; + int y_odd = y_off & 1; int ymask,xmask; int rle_this_bite; int rle_remainder; @@ -732,10 +809,14 @@ void blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, int x, y; int clip_right; uint8_t clr=0; - +#ifdef EXACT_BLENDING + int anyLineBufferd = 0; + int exactBlendWidth = ((src_width <= (dst_width - x_off)) ? src_width : (dst_width - x_off)); +#endif + uint8_t *dst_y = dst_base[0] + dst_pitches[0] * y_off + x_off; - uint8_t *dst_cr = dst_base[2] + (y_off / 2) * dst_pitches[1] + (x_off / 2) + 1; - uint8_t *dst_cb = dst_base[1] + (y_off / 2) * dst_pitches[2] + (x_off / 2) + 1; + uint8_t *dst_cr = dst_base[2] + (y_off / 2) * dst_pitches[1] + (x_off / 2); + uint8_t *dst_cb = dst_base[1] + (y_off / 2) * dst_pitches[2] + (x_off / 2); #ifdef LOG_BLEND_YUV printf("overlay_blend started x=%d, y=%d, w=%d h=%d\n",img_overl->x,img_overl->y,img_overl->width,img_overl->height); #endif @@ -743,18 +824,30 @@ void blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, my_trans = img_overl->clip_trans; /* avoid wraping overlay if drawing to small image */ - if( (x_off + img_overl->clip_right) < dst_width ) + if( (x_off + img_overl->clip_right) <= dst_width ) clip_right = img_overl->clip_right; else - clip_right = dst_width - 1 - x_off; + clip_right = dst_width - x_off; /* avoid buffer overflow */ - if( (src_height + y_off) >= dst_height ) - src_height = dst_height - 1 - y_off; + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; +#ifdef EXACT_BLENDING + /* make linebuffer transparent */ + memset(&blend_yuv_data[ 0 ][ 0 ][ 0 ], 0, 2 * BLEND_MAXWIDTH); +#endif + rlelen=rle_remainder=0; for (y = 0; y < src_height; y++) { - ymask = ((img_overl->clip_top > y) || (img_overl->clip_bottom < y)); + if (rle >= rle_limit) { +#ifdef LOG_BLEND_YUV + printf("y-rle_limit\n"); +#endif + break; + } + + ymask = ((y < img_overl->clip_top) || (y >= img_overl->clip_bottom)); xmask = 0; #ifdef LOG_BLEND_YUV printf("X started ymask=%d y=%d src_height=%d\n",ymask, y, src_height); @@ -762,6 +855,14 @@ void blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, for (x = 0; x < src_width;) { uint16_t o; + + if (rle >= rle_limit) { +#ifdef LOG_BLEND_YUV + printf("x-rle_limit\n"); +#endif + break; + } + #ifdef LOG_BLEND_YUV printf("1:rle_len=%d, remainder=%d, x=%d\n",rlelen, rle_remainder, x); #endif @@ -788,14 +889,14 @@ void blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, #endif if (ymask == 0) { - if (x <= img_overl->clip_left) { + if (x < img_overl->clip_left) { /* Starts outside clip area */ - if ((x + rle_remainder - 1) > img_overl->clip_left ) { + if ((x + rle_remainder) > img_overl->clip_left ) { #ifdef LOG_BLEND_YUV printf("Outside clip left %d, ending inside\n", img_overl->clip_left); #endif /* Cutting needed, starts outside, ends inside */ - rle_this_bite = (img_overl->clip_left - x + 1); + rle_this_bite = (img_overl->clip_left - x); rle_remainder -= rle_this_bite; rlelen -= rle_this_bite; my_clut = (clut_t*) img_overl->color; @@ -879,48 +980,97 @@ void blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, #ifdef LOG_BLEND_YUV printf("Trans=%d clr=%d xmask=%d my_clut[clr]=%d\n",o, clr, xmask, my_clut[clr].y); #endif - if (o) { - if(o >= 15) { - memset(dst_y + x, my_clut[clr].y, rle_this_bite); - if (y & 1) { - memset(dst_cr + (x >> 1), my_clut[clr].cr, (rle_this_bite+1) >> 1); - memset(dst_cb + (x >> 1), my_clut[clr].cb, (rle_this_bite+1) >> 1); - } - } else { - mem_blend8(dst_y + x, my_clut[clr].y, o, rle_this_bite); - if (y & 1) { - /* Blending cr and cb should use a different function, with pre -128 to each sample */ - mem_blend8(dst_cr + (x >> 1), my_clut[clr].cr, o, (rle_this_bite+1) >> 1); - mem_blend8(dst_cb + (x >> 1), my_clut[clr].cb, o, (rle_this_bite+1) >> 1); - } - } - } -#ifdef LOG_BLEND_YUV - printf("rle_this_bite=%d, remainder=%d, x=%d\n",rle_this_bite, rle_remainder, x); + if (x < (dst_width - x_off)) + { + /* clip against right edge of destination area */ + if ((x + rle_this_bite) > (dst_width - x_off)) + { + int toClip = (x + rle_this_bite) - (dst_width - x_off); + + rle_this_bite -= toClip; + rle_remainder += toClip; + rlelen += toClip; + } + +#ifdef EXACT_BLENDING + /* remember opacity of current line */ + memset(&blend_yuv_data[ 0 ][ (y + y_odd) & 1 ][ x + x_odd ], o, rle_this_bite); + anyLineBufferd |= ((y + y_odd) & 1) ? 2 : 1; #endif - x += rle_this_bite; - if (rle >= rle_limit) { -#ifdef LOG_BLEND_YUV - printf("x-rle_limit\n"); + + if (o) { + if(o >= 15) { + memset(dst_y + x, my_clut[clr].y, rle_this_bite); +#ifndef EXACT_BLENDING + if ((y + y_odd) & 1) { + memset(dst_cr + ((x + x_odd) >> 1), my_clut[clr].cr, (rle_this_bite+1) >> 1); + memset(dst_cb + ((x + x_odd) >> 1), my_clut[clr].cb, (rle_this_bite+1) >> 1); + } #endif - break; + } else { + mem_blend8(dst_y + x, my_clut[clr].y, o, rle_this_bite); +#ifndef EXACT_BLENDING + if ((y + y_odd) & 1) { + /* Blending cr and cb should use a different function, with pre -128 to each sample */ + mem_blend8(dst_cr + ((x + x_odd) >> 1), my_clut[clr].cr, o, (rle_this_bite+1) >> 1); + mem_blend8(dst_cb + ((x + x_odd) >> 1), my_clut[clr].cb, o, (rle_this_bite+1) >> 1); + } +#endif + } + +#ifdef EXACT_BLENDING + /* remember chroma of current line */ + memset(&blend_yuv_data[ 1 ][ (y + y_odd) & 1 ][ x + x_odd ], my_clut[ clr ].cr, rle_this_bite); + memset(&blend_yuv_data[ 2 ][ (y + y_odd) & 1 ][ x + x_odd ], my_clut[ clr ].cb, rle_this_bite); +#endif + } } - } - if (rle >= rle_limit) { #ifdef LOG_BLEND_YUV - printf("x-rle_limit\n"); + printf("rle_this_bite=%d, remainder=%d, x=%d\n",rle_this_bite, rle_remainder, x); #endif - break; + x += rle_this_bite; } dst_y += dst_pitches[0]; - if (y & 1) { + if ((y + y_odd) & 1) { + +#ifdef EXACT_BLENDING + /* blend bufferd chroma */ + if (anyLineBufferd) + { + if (!(anyLineBufferd & 2)) + { + /* make second line transparent */ + memset(&blend_yuv_data[ 0 ][ 1 ][ 0 ], 0, BLEND_MAXWIDTH); + } + + blend_yuv_exact(dst_cr, dst_cb, exactBlendWidth, x_odd); + + anyLineBufferd = 0; + } +#endif + dst_cr += dst_pitches[2]; dst_cb += dst_pitches[1]; } } + +#ifdef EXACT_BLENDING + /* blend bufferd chroma */ + if (anyLineBufferd) + { + if (!(anyLineBufferd & 2)) + { + /* make second line transparent */ + memset(&blend_yuv_data[ 0 ][ 1 ][ 0 ], 0, BLEND_MAXWIDTH); + } + + blend_yuv_exact(dst_cr, dst_cb, exactBlendWidth, x_odd); + } +#endif + #ifdef LOG_BLEND_YUV printf("overlay_blend ended\n"); #endif diff --git a/src/xine-engine/osd.c b/src/xine-engine/osd.c index a0488992f..6585a7bf2 100644 --- a/src/xine-engine/osd.c +++ b/src/xine-engine/osd.c @@ -165,6 +165,10 @@ static osd_object_t *osd_new_object (osd_renderer_t *this, int width, int height /* +#define DEBUG_RLE +*/ + +/* * send the osd to be displayed at given pts (0=now) * the object is not changed. there may be subsequent drawing on it. */ @@ -173,7 +177,7 @@ static int _osd_show (osd_object_t *osd, int64_t vpts, int unscaled ) { osd_renderer_t *this = osd->renderer; video_overlay_manager_t *ovl_manager; rle_elem_t rle, *rle_p=0; - int x, y, spare; + int x, y, required; uint8_t *c; lprintf("osd=%p vpts=%lld\n", osd, vpts); @@ -190,69 +194,91 @@ static int _osd_show (osd_object_t *osd, int64_t vpts, int unscaled ) { } pthread_mutex_lock (&this->osd_mutex); - + + /* clip update area to allowed range */ + if(osd->x1 > osd->width) + osd->x1 = osd->width; + if(osd->x2 > osd->width) + osd->x2 = osd->width; + if(osd->y1 > osd->height) + osd->y1 = osd->height; + if(osd->y2 > osd->height) + osd->y2 = osd->height; + +#ifdef DEBUG_RLE + lprintf("osd_show %p rle starts\n", osd); +#endif + /* check if osd is valid (something drawn on it) */ - if( osd->x2 >= osd->x1 ) { + if( osd->x2 > osd->x1 && osd->y2 > osd->y1 ) { this->event.object.handle = osd->handle; - if(osd->x1 > osd->width) - osd->x1 = osd->width; - if(osd->x2 > osd->width) - osd->x2 = osd->width; - if(osd->y1 > osd->height) - osd->y1 = osd->height; - if(osd->y2 > osd->height) - osd->y2 = osd->height; - memset( this->event.object.overlay, 0, sizeof(*this->event.object.overlay) ); this->event.object.overlay->unscaled = unscaled; this->event.object.overlay->x = osd->display_x + osd->x1; this->event.object.overlay->y = osd->display_y + osd->y1; - this->event.object.overlay->width = osd->x2 - osd->x1 + 1; - this->event.object.overlay->height = osd->y2 - osd->y1 + 1; + this->event.object.overlay->width = osd->x2 - osd->x1; + this->event.object.overlay->height = osd->y2 - osd->y1; - this->event.object.overlay->clip_top = -1; - this->event.object.overlay->clip_bottom = this->event.object.overlay->height + - osd->display_y; + this->event.object.overlay->clip_top = 0; + this->event.object.overlay->clip_bottom = this->event.object.overlay->height; this->event.object.overlay->clip_left = 0; - this->event.object.overlay->clip_right = this->event.object.overlay->width + - osd->display_x; + this->event.object.overlay->clip_right = this->event.object.overlay->width; - spare = osd->y2 - osd->y1; + /* there will be at least that many rle objects (one for each row) */ + required = osd->y2 - osd->y1; this->event.object.overlay->num_rle = 0; this->event.object.overlay->data_size = 1024; + while (required > this->event.object.overlay->data_size) + this->event.object.overlay->data_size += 1024; rle_p = this->event.object.overlay->rle = malloc(this->event.object.overlay->data_size * sizeof(rle_elem_t) ); - for( y = osd->y1; y <= osd->y2; y++ ) { - rle.len = 0; - rle.color = 0; - c = osd->area + y * osd->width + osd->x1; - for( x = osd->x1; x <= osd->x2; x++, c++ ) { + for( y = osd->y1; y < osd->y2; y++ ) { +#ifdef DEBUG_RLE + lprintf("osd_show %p y = %d: ", osd, y); +#endif + c = osd->area + y * osd->width + osd->x1; + + /* initialize a rle object with the first pixel's color */ + rle.len = 1; + rle.color = *c++; + + /* loop over the remaining pixels in the row */ + for( x = osd->x1 + rle.len; x < osd->x2; x++, c++ ) { if( rle.color != *c ) { - if( rle.len ) { - if( (this->event.object.overlay->num_rle + spare) > - this->event.object.overlay->data_size ) { - this->event.object.overlay->data_size += 1024; - rle_p = this->event.object.overlay->rle = - realloc( this->event.object.overlay->rle, - this->event.object.overlay->data_size * sizeof(rle_elem_t) ); - rle_p += this->event.object.overlay->num_rle; - } - *rle_p++ = rle; - this->event.object.overlay->num_rle++; + if( (this->event.object.overlay->num_rle + required) > + this->event.object.overlay->data_size ) { + this->event.object.overlay->data_size += 1024; + rle_p = this->event.object.overlay->rle = + realloc( this->event.object.overlay->rle, + this->event.object.overlay->data_size * sizeof(rle_elem_t) ); + rle_p += this->event.object.overlay->num_rle; } +#ifdef DEBUG_RLE + lprintf("(%d, %d), ", rle.len, rle.color); +#endif + *rle_p++ = rle; + this->event.object.overlay->num_rle++; + rle.color = *c; rle.len = 1; } else { rle.len++; } } +#ifdef DEBUG_RLE + lprintf("(%d, %d)\n", rle.len, rle.color); +#endif *rle_p++ = rle; - this->event.object.overlay->num_rle++; + this->event.object.overlay->num_rle++; + /* another row done */ + required--; } - +#ifdef DEBUG_RLE + lprintf("osd_show %p rle ends\n", osd); +#endif lprintf("num_rle = %d\n", this->event.object.overlay->num_rle); memcpy(this->event.object.overlay->clip_color, osd->color, sizeof(osd->color)); @@ -1307,9 +1333,9 @@ static void osd_draw_bitmap(osd_object_t *osd, uint8_t *bitmap, /* update clipping area */ osd->x1 = MIN( osd->x1, x1 ); - osd->x2 = MAX( osd->x2, x1+width-1 ); + osd->x2 = MAX( osd->x2, x1+width ); osd->y1 = MIN( osd->y1, y1 ); - osd->y2 = MAX( osd->y2, y1+height-1 ); + osd->y2 = MAX( osd->y2, y1+height ); for( y=0; y<height; y++ ) { if ( palette_map ) { |