diff options
-rw-r--r-- | src/xine-engine/alphablend.c | 2138 | ||||
-rw-r--r-- | src/xine-engine/alphablend.h | 136 |
2 files changed, 2274 insertions, 0 deletions
diff --git a/src/xine-engine/alphablend.c b/src/xine-engine/alphablend.c new file mode 100644 index 000000000..9733ca6b3 --- /dev/null +++ b/src/xine-engine/alphablend.c @@ -0,0 +1,2138 @@ +/* + * + * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 + * + * Copyright (C) 2000 Thomas Mirlacher + * 2002-2004 the xine project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *------------------------------------------------------------ + * + */ + +/* +#define LOG_BLEND_YUV +#define LOG_BLEND_RGB16 +*/ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "xine_internal.h" +#include "video_out.h" +#include "alphablend.h" +#include "bswap.h" + + +#define BLEND_COLOR(dst, src, mask, o) ((((src&mask)*o + ((dst&mask)*(0x0f-o)))/0xf) & mask) + +#define BLEND_BYTE(dst, src, o) (((src)*o + ((dst)*(0xf-o)))/0xf) + +static void mem_blend16(uint16_t *mem, uint16_t clr, uint8_t o, int len) { + uint16_t *limit = mem + len; + while (mem < limit) { + *mem = + BLEND_COLOR(*mem, clr, 0xf800, o) | + BLEND_COLOR(*mem, clr, 0x07e0, o) | + BLEND_COLOR(*mem, clr, 0x001f, o); + mem++; + } +} + +static void mem_blend24(uint8_t *mem, uint8_t r, uint8_t g, uint8_t b, + uint8_t o, int len) { + uint8_t *limit = mem + len*3; + while (mem < limit) { + *mem = BLEND_BYTE(*mem, r, o); + mem++; + *mem = BLEND_BYTE(*mem, g, o); + mem++; + *mem = BLEND_BYTE(*mem, b, o); + mem++; + } +} + +static void mem_blend32(uint8_t *mem, uint8_t *src, uint8_t o, int len) { + uint8_t *limit = mem + len*4; + while (mem < limit) { + *mem = BLEND_BYTE(*mem, src[0], o); + mem++; + *mem = BLEND_BYTE(*mem, src[1], o); + mem++; + *mem = BLEND_BYTE(*mem, src[2], o); + mem++; + *mem = BLEND_BYTE(*mem, src[3], o); + mem++; + } +} + +/* + * Some macros for fixed point arithmetic. + * + * The blend_rgb* routines perform rle image scaling using + * scale factors that are expressed as integers scaled with + * a factor of 2**16. + * + * INT_TO_SCALED()/SCALED_TO_INT() converts from integer + * to scaled fixed point and back. + */ +#define SCALE_SHIFT 16 +#define SCALE_FACTOR (1<<SCALE_SHIFT) +#define INT_TO_SCALED(i) ((i) << SCALE_SHIFT) +#define SCALED_TO_INT(sc) ((sc) >> SCALE_SHIFT) + + +static rle_elem_t * +rle_img_advance_line(rle_elem_t *rle, rle_elem_t *rle_limit, int w) +{ + int x; + + for (x = 0; x < w && rle < rle_limit; ) { + x += rle->len; + rle++; + } + return rle; +} + +/* + * heck, this function is overly complicated and currently buggy. + * if James would like to revive it (implementing proper clipping - + * not to confuse with button highlight) i would have no objections, + * but for now i will be using an alternate version based on rgb24. [MF] + * + * obs: this function has about 420 lines. other blend_rgb16 has 165. + */ +#if JAMES_BLEND_RGB16_FUNCTION +void blend_rgb16 (uint8_t * img, vo_overlay_t * img_overl, + int img_width, int img_height, + int dst_width, int dst_height, + alphablend_t *extra_data) +{ + uint8_t *trans; + clut_t *clut; + + int src_width = img_overl->width; + int src_height = img_overl->height; + rle_elem_t *rle = img_overl->rle; + rle_elem_t *rle_start = img_overl->rle; + rle_elem_t *rle_limit = rle + img_overl->num_rle; + int x_off = img_overl->x + extra_data->offset_x; + int y_off = img_overl->y + extra_data->offset_y; + int x, y, x1_scaled, x2_scaled; + int dy, dy_step, x_scale; /* scaled 2**SCALE_SHIFT */ + int clip_right, clip_left, clip_top; + int hili_right, hili_left; + uint16_t *img_pix; + int rlelen; + int rle_this_bite; + int rle_remainder; + int zone_state=0; + uint8_t clr_next,clr; + uint16_t o; + double img_offset; + int stripe_height; +/* + * Let zone_state keep state. + * 0 = Starting. + * 1 = Above button. + * 2 = Left of button. + * 3 = Inside of button. + * 4 = Right of button. + * 5 = Below button. + * 6 = Finished. + * + * Each time round the loop, update the state. + * We can do this easily and cheaply(fewer IF statements per cycle) as we are testing rle end position anyway. + * Possible optimization is to ensure that rle never overlaps from outside to inside a button. + * Possible optimization is to pre-scale the RLE overlay, so that no scaling is needed here. + */ + +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16: img_height=%i, dst_height=%i\n", img_height, dst_height); + printf("blend_rgb16: img_width=%i, dst_width=%i\n", img_width, dst_width); + if (img_width & 1) { printf("blend_rgb16s: odd\n");} + else { printf("blend_rgb16s: even\n");} + +#endif +/* stripe_height is used in yuv2rgb scaling, so use the same scale factor here for overlays. */ + stripe_height = 16 * img_height / dst_height; +/* dy_step = INT_TO_SCALED(dst_height) / img_height; */ + dy_step = INT_TO_SCALED(16) / stripe_height; + x_scale = INT_TO_SCALED(img_width) / dst_width; +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16: dy_step=%i, x_scale=%i\n", dy_step, x_scale); +#endif + if (img_width & 1) img_width++; + img_offset = ( ( (y_off * img_height) / dst_height) * img_width) + + ( (x_off * img_width) / dst_width); +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16: x=%i, y=%i, w=%i, h=%i, img_offset=%lf\n", img_overl->x, img_overl->y, + img_overl->width, + img_overl->height, + img_offset); +#endif + img_pix = (uint16_t *) img + (int)img_offset; +/* + + (y_off * img_height / dst_height) * img_width + + (x_off * img_width / dst_width); +*/ + + /* checks to avoid drawing overlay outside the destination buffer */ + if( (x_off + src_width) <= dst_width ) + clip_right = src_width; + else + clip_right = dst_width - x_off; + + if( x_off >= 0 ) + clip_left = 0; + else + clip_left = -x_off; + + if( y_off >= 0 ) + clip_top = 0; + else + clip_top = -y_off; + + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; + + /* make highlight area fit into clip area */ + if( img_overl->hili_right <= clip_right ) + hili_right = img_overl->hili_right; + else + hili_right = clip_right; + + if( img_overl->hili_left >= clip_left ) + hili_left = img_overl->hili_left; + else + hili_left = clip_left; + + rlelen = rle_remainder = rle_this_bite = 0; + rle_remainder = rlelen = rle->len; + clr_next = rle->color; + rle++; + y = dy = 0; + x = x1_scaled = x2_scaled = 0; + +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16 started\n"); +#endif + + while (zone_state != 6) { + clr = clr_next; + switch (zone_state) { + case 0: /* Starting */ + /* FIXME: Get libspudec to set hili_top to -1 if no button */ + if (img_overl->hili_top < 0) { +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16: No button highlight area\n"); +#endif + + zone_state = 7; + break; + } +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16: Button highlight area found. (%d,%d) .. (%d,%d)\n", + img_overl->hili_left, + img_overl->hili_top, + img_overl->hili_right, + img_overl->hili_bottom); +#endif + if (y < img_overl->hili_top) { + zone_state = 1; + break; + } else if (y >= img_overl->hili_bottom) { + zone_state = 5; + break; + } else if (x < hili_left) { + zone_state = 2; + break; + } else if (x >= hili_right) { + zone_state = 4; + break; + } else { + zone_state = 3; + break; + } + break; + case 1: /* Above highlight area */ + clut = (clut_t*) img_overl->color; + trans = img_overl->trans; + o = trans[clr]; + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + /*printf("(x,y) = (%03i,%03i), clr=%03x, len=%03i, zone=%i\n", x, y, clr, rle_this_bite, zone_state); */ + if (o) { + x1_scaled = SCALED_TO_INT( x * x_scale ); + x2_scaled = SCALED_TO_INT( (x + rle_this_bite) * x_scale); + mem_blend16(img_pix+x1_scaled, *((uint16_t *)&clut[clr]), o, x2_scaled-x1_scaled); + } + x += rle_this_bite; + if (x >= src_width ) { + x -= src_width; + img_pix += img_width; + + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + rle_start = rle; + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + } + rle_remainder = rlelen = rle->len; + clr_next = rle->color; + rle++; + if (rle >= rle_limit) { + zone_state = 6; + } + if (y >= img_overl->hili_top) { + zone_state = 2; +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16: Button highlight top reached. y=%i, top=%i\n", + y, img_overl->hili_top); +#endif + if (x >= hili_left) { + zone_state = 3; + if (x >= hili_right) { + zone_state = 4; + } + } + } + break; + case 2: /* Left of button */ + clut = (clut_t*) img_overl->color; + trans = img_overl->trans; + o = trans[clr]; + if (x + rle_remainder <= hili_left) { + rle_this_bite = rle_remainder; + rle_remainder = rlelen = rle->len; + clr_next = rle->color; + rle++; + } else { + rle_this_bite = hili_left - x; + rle_remainder -= rle_this_bite; + zone_state = 3; + } + if (o) { + x1_scaled = SCALED_TO_INT( x * x_scale ); + x2_scaled = SCALED_TO_INT( (x + rle_this_bite) * x_scale); + mem_blend16(img_pix+x1_scaled, *((uint16_t *)&clut[clr]), o, x2_scaled-x1_scaled); + } + x += rle_this_bite; + if (x >= src_width ) { + x -= src_width; + img_pix += img_width; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + rle_start = rle; + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + if (y >= img_overl->hili_bottom) { + zone_state = 5; + break; + } + } + if (rle >= rle_limit) { + zone_state = 6; + } + break; + case 3: /* In button */ + clut = (clut_t*) img_overl->hili_color; + trans = img_overl->hili_trans; + o = trans[clr]; + if (x + rle_remainder <= hili_right) { + rle_this_bite = rle_remainder; + rle_remainder = rlelen = rle->len; + clr_next = rle->color; + rle++; + } else { + rle_this_bite = hili_right - x; + rle_remainder -= rle_this_bite; + zone_state = 4; + } + if (o) { + x1_scaled = SCALED_TO_INT( x * x_scale ); + x2_scaled = SCALED_TO_INT( (x + rle_this_bite) * x_scale); + mem_blend16(img_pix+x1_scaled, *((uint16_t *)&clut[clr]), o, x2_scaled-x1_scaled); + } + x += rle_this_bite; + if (x >= src_width ) { + x -= src_width; + img_pix += img_width; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + rle_start = rle; + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + if (y >= img_overl->hili_bottom) { + zone_state = 5; + break; + } + } + if (rle >= rle_limit) { + zone_state = 6; + } + break; + case 4: /* Right of button */ + clut = (clut_t*) img_overl->color; + trans = img_overl->trans; + o = trans[clr]; + if (x + rle_remainder <= src_width) { + rle_this_bite = rle_remainder; + rle_remainder = rlelen = rle->len; + clr_next = rle->color; + rle++; + } else { + rle_this_bite = src_width - x; + rle_remainder -= rle_this_bite; + zone_state = 2; + } + if (o) { + x1_scaled = SCALED_TO_INT( x * x_scale ); + x2_scaled = SCALED_TO_INT( (x + rle_this_bite) * x_scale); + mem_blend16(img_pix+x1_scaled, *((uint16_t *)&clut[clr]), o, x2_scaled-x1_scaled); + } + x += rle_this_bite; + if (x >= src_width ) { + x -= src_width; + img_pix += img_width; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + rle_start = rle; + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + if (y >= img_overl->hili_bottom) { + zone_state = 5; + break; + } + } + if (rle >= rle_limit) { + zone_state = 6; + } + break; + case 5: /* Below button */ + clut = (clut_t*) img_overl->color; + trans = img_overl->trans; + o = trans[clr]; + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + if (o) { + x1_scaled = SCALED_TO_INT( x * x_scale ); + x2_scaled = SCALED_TO_INT( (x + rle_this_bite) * x_scale); + mem_blend16(img_pix+x1_scaled, *((uint16_t *)&clut[clr]), o, x2_scaled-x1_scaled); + } + x += rle_this_bite; + if (x >= src_width ) { + x -= src_width; + img_pix += img_width; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + rle_start = rle; + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + } + rle_remainder = rlelen = rle->len; + clr_next = rle->color; + rle++; + if (rle >= rle_limit) { + zone_state = 6; + } + break; + case 6: /* Finished */ + _x_abort(); + + case 7: /* No button */ + clut = (clut_t*) img_overl->color; + trans = img_overl->trans; + o = trans[clr]; + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + if (o) { + x1_scaled = SCALED_TO_INT( x * x_scale ); + x2_scaled = SCALED_TO_INT( (x + rle_this_bite) * x_scale); + mem_blend16(img_pix+x1_scaled, *((uint16_t *)&clut[clr]), o, x2_scaled-x1_scaled); + } + x += rle_this_bite; + if (x >= src_width ) { + x -= src_width; + img_pix += img_width; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + rle_start = rle; + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + } + rle_remainder = rlelen = rle->len; + clr_next = rle->color; + rle++; + if (rle >= rle_limit) { + zone_state = 6; + } + break; + default: + ; + } + } +#ifdef LOG_BLEND_RGB16 + printf("blend_rgb16 ended\n"); +#endif + +} +#endif + +void _x_blend_rgb16 (uint8_t * img, vo_overlay_t * img_overl, + int img_width, int img_height, + int dst_width, int dst_height, + alphablend_t *extra_data) +{ + int src_width = img_overl->width; + int src_height = img_overl->height; + rle_elem_t *rle = img_overl->rle; + rle_elem_t *rle_limit = rle + img_overl->num_rle; + int x_off = img_overl->x + extra_data->offset_x; + int y_off = img_overl->y + extra_data->offset_y; + int x, y, x1_scaled, x2_scaled; + int dy, dy_step, x_scale; /* scaled 2**SCALE_SHIFT */ + int hili_right, hili_left; + int clip_right, clip_left, clip_top; + uint8_t *img_pix; + + dy_step = INT_TO_SCALED(dst_height) / img_height; + x_scale = INT_TO_SCALED(img_width) / dst_width; + + img_pix = img + 2 * ( (y_off * img_height / dst_height) * img_width + + (x_off * img_width / dst_width)); + + /* checks to avoid drawing overlay outside the destination buffer */ + if( (x_off + src_width) <= dst_width ) + clip_right = src_width; + else + clip_right = dst_width - x_off; + + if( x_off >= 0 ) + clip_left = 0; + else + clip_left = -x_off; + + if( y_off >= 0 ) + clip_top = 0; + else + clip_top = -y_off; + + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; + + /* make highlight area fit into clip area */ + if( img_overl->hili_right <= clip_right ) + hili_right = img_overl->hili_right; + else + hili_right = clip_right; + + if( img_overl->hili_left >= clip_left ) + hili_left = img_overl->hili_left; + else + hili_left = clip_left; + + for (dy = y = 0; y < src_height && rle < rle_limit; ) { + int mask = !(y < img_overl->hili_top || y >= img_overl->hili_bottom); + rle_elem_t *rle_start = rle; + + int rlelen = 0; + uint8_t clr = 0; + + for (x = x1_scaled = 0; x < src_width;) { + int rle_bite; + clut_t *colors; + uint8_t *trans; + uint16_t o; + int clipped = (y < clip_top); + + /* take next element from rle list everytime an element is finished */ + if (rlelen <= 0) { + if (rle >= rle_limit) + break; + + rlelen = rle->len; + clr = rle->color; + rle++; + } + + if (!mask) { + /* above or below highlight area */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } else { + /* treat cases where highlight border is inside rle->len pixels */ + if ( x < hili_left ) { + /* starts left */ + if( x + rlelen > hili_left ) { + /* ends not left */ + + /* choose the largest "bite" up to palette change */ + rle_bite = hili_left - x; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } else { + /* ends left */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } + if( x < clip_left ) + clipped = 1; + } else if( x + rlelen > hili_right ) { + /* ends right */ + if( x < hili_right ) { + /* starts not right */ + + /* choose the largest "bite" up to palette change */ + rle_bite = hili_right - x; + /* we're in the center area so choose highlight palette */ + colors = (clut_t*)img_overl->hili_color; + trans = img_overl->hili_trans; + } else { + /* starts right */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + + if( x + rle_bite >= clip_right ) + clipped = 1; + } + } else { + /* starts not left and ends not right */ + + rle_bite = rlelen; + /* we're in the center area so choose highlight palette */ + colors = (clut_t*)img_overl->hili_color; + trans = img_overl->hili_trans; + } + } + + x2_scaled = SCALED_TO_INT((x + rle_bite) * x_scale); + + o = trans[clr]; + if (o && !clipped) { + mem_blend16((uint16_t *) (img_pix + x1_scaled*2), + *((uint16_t *)&colors[clr]), + o, x2_scaled-x1_scaled); + } + + x1_scaled = x2_scaled; + x += rle_bite; + rlelen -= rle_bite; + } + + img_pix += img_width * 2; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + } +} + +void _x_blend_rgb24 (uint8_t * img, vo_overlay_t * img_overl, + int img_width, int img_height, + int dst_width, int dst_height, + alphablend_t *extra_data) +{ + int src_width = img_overl->width; + int src_height = img_overl->height; + rle_elem_t *rle = img_overl->rle; + rle_elem_t *rle_limit = rle + img_overl->num_rle; + int x_off = img_overl->x + extra_data->offset_x; + int y_off = img_overl->y + extra_data->offset_y; + int x, y, x1_scaled, x2_scaled; + int dy, dy_step, x_scale; /* scaled 2**SCALE_SHIFT */ + int hili_right, hili_left; + int clip_right, clip_left, clip_top; + uint8_t *img_pix; + + dy_step = INT_TO_SCALED(dst_height) / img_height; + x_scale = INT_TO_SCALED(img_width) / dst_width; + + img_pix = img + 3 * ( (y_off * img_height / dst_height) * img_width + + (x_off * img_width / dst_width)); + + /* checks to avoid drawing overlay outside the destination buffer */ + if( (x_off + src_width) <= dst_width ) + clip_right = src_width; + else + clip_right = dst_width - x_off; + + if( x_off >= 0 ) + clip_left = 0; + else + clip_left = -x_off; + + if( y_off >= 0 ) + clip_top = 0; + else + clip_top = -y_off; + + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; + + /* make highlight area fit into clip area */ + if( img_overl->hili_right <= clip_right ) + hili_right = img_overl->hili_right; + else + hili_right = clip_right; + + if( img_overl->hili_left >= clip_left ) + hili_left = img_overl->hili_left; + else + hili_left = clip_left; + + for (dy = y = 0; y < src_height && rle < rle_limit; ) { + int mask = !(y < img_overl->hili_top || y >= img_overl->hili_bottom); + rle_elem_t *rle_start = rle; + + int rlelen = 0; + uint8_t clr = 0; + + for (x = x1_scaled = 0; x < src_width;) { + int rle_bite; + clut_t *colors; + uint8_t *trans; + uint16_t o; + int clipped = (y < clip_top); + + /* take next element from rle list everytime an element is finished */ + if (rlelen <= 0) { + if (rle >= rle_limit) + break; + + rlelen = rle->len; + clr = rle->color; + rle++; + } + + if (!mask) { + /* above or below highlight area */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } else { + /* treat cases where highlight border is inside rle->len pixels */ + if ( x < hili_left ) { + /* starts left */ + if( x + rlelen > hili_left ) { + /* ends not left */ + + /* choose the largest "bite" up to palette change */ + rle_bite = hili_left - x; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } else { + /* ends left */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } + if( x < clip_left ) + clipped = 1; + } else if( x + rlelen > hili_right ) { + /* ends right */ + if( x < hili_right ) { + /* starts not right */ + + /* choose the largest "bite" up to palette change */ + rle_bite = hili_right - x; + /* we're in the center area so choose highlight palette */ + colors = (clut_t*)img_overl->hili_color; + trans = img_overl->hili_trans; + } else { + /* starts right */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + + if( x + rle_bite >= clip_right ) + clipped = 1; + } + } else { + /* starts not left and ends not right */ + + rle_bite = rlelen; + /* we're in the center area so choose highlight palette */ + colors = (clut_t*)img_overl->hili_color; + trans = img_overl->hili_trans; + } + } + + x2_scaled = SCALED_TO_INT((x + rle_bite) * x_scale); + + o = trans[clr]; + if (o && !clipped) { + mem_blend24(img_pix + x1_scaled*3, + colors[clr].cb, colors[clr].cr, colors[clr].y, + o, x2_scaled-x1_scaled); + } + + x1_scaled = x2_scaled; + x += rle_bite; + rlelen -= rle_bite; + } + + img_pix += img_width * 3; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + } +} + +void _x_blend_rgb32 (uint8_t * img, vo_overlay_t * img_overl, + int img_width, int img_height, + int dst_width, int dst_height, + alphablend_t *extra_data) +{ + int src_width = img_overl->width; + int src_height = img_overl->height; + rle_elem_t *rle = img_overl->rle; + rle_elem_t *rle_limit = rle + img_overl->num_rle; + int x_off = img_overl->x + extra_data->offset_x; + int y_off = img_overl->y + extra_data->offset_y; + int x, y, x1_scaled, x2_scaled; + int dy, dy_step, x_scale; /* scaled 2**SCALE_SHIFT */ + int hili_right, hili_left; + int clip_right, clip_left, clip_top; + uint8_t *img_pix; + + dy_step = INT_TO_SCALED(dst_height) / img_height; + x_scale = INT_TO_SCALED(img_width) / dst_width; + + img_pix = img + 4 * ( (y_off * img_height / dst_height) * img_width + + (x_off * img_width / dst_width)); + + /* checks to avoid drawing overlay outside the destination buffer */ + if( (x_off + src_width) <= dst_width ) + clip_right = src_width; + else + clip_right = dst_width - x_off; + + if( x_off >= 0 ) + clip_left = 0; + else + clip_left = -x_off; + + if( y_off >= 0 ) + clip_top = 0; + else + clip_top = -y_off; + + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; + + /* make highlight area fit into clip area */ + if( img_overl->hili_right <= clip_right ) + hili_right = img_overl->hili_right; + else + hili_right = clip_right; + + if( img_overl->hili_left >= clip_left ) + hili_left = img_overl->hili_left; + else + hili_left = clip_left; + + for (y = dy = 0; y < src_height && rle < rle_limit; ) { + int mask = !(y < img_overl->hili_top || y >= img_overl->hili_bottom); + rle_elem_t *rle_start = rle; + + int rlelen = 0; + uint8_t clr = 0; + + for (x = x1_scaled = 0; x < src_width;) { + int rle_bite; + clut_t *colors; + uint8_t *trans; + uint16_t o; + int clipped = (y < clip_top); + + /* take next element from rle list everytime an element is finished */ + if (rlelen <= 0) { + if (rle >= rle_limit) + break; + + rlelen = rle->len; + clr = rle->color; + rle++; + } + + if (!mask) { + /* above or below highlight area */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } else { + /* treat cases where highlight border is inside rle->len pixels */ + if ( x < hili_left ) { + /* starts left */ + if( x + rlelen > hili_left ) { + /* ends not left */ + + /* choose the largest "bite" up to palette change */ + rle_bite = hili_left - x; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } else { + /* ends left */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + } + if( x < clip_left ) + clipped = 1; + } else if( x + rlelen > hili_right ) { + /* ends right */ + if( x < hili_right ) { + /* starts not right */ + + /* choose the largest "bite" up to palette change */ + rle_bite = hili_right - x; + /* we're in the center area so choose highlight palette */ + colors = (clut_t*)img_overl->hili_color; + trans = img_overl->hili_trans; + } else { + /* starts right */ + + rle_bite = rlelen; + /* choose palette for surrounding area */ + colors = (clut_t*)img_overl->color; + trans = img_overl->trans; + + if( x + rle_bite >= clip_right ) + clipped = 1; + } + } else { + /* starts not left and ends not right */ + + rle_bite = rlelen; + /* we're in the center area so choose highlight palette */ + colors = (clut_t*)img_overl->hili_color; + trans = img_overl->hili_trans; + } + } + + x2_scaled = SCALED_TO_INT((x + rle_bite) * x_scale); + + o = trans[clr]; + if (o && !clipped) { + mem_blend32(img_pix + x1_scaled*4, (uint8_t *)&colors[clr], o, x2_scaled-x1_scaled); + } + + x1_scaled = x2_scaled; + x += rle_bite; + rlelen -= rle_bite; + } + + img_pix += img_width * 4; + dy += dy_step; + if (dy >= INT_TO_SCALED(1)) { + dy -= INT_TO_SCALED(1); + ++y; + while (dy >= INT_TO_SCALED(1)) { + rle = rle_img_advance_line(rle, rle_limit, src_width); + dy -= INT_TO_SCALED(1); + ++y; + } + } else { + rle = rle_start; /* y-scaling, reuse the last rle encoded line */ + } + } +} + +static void mem_blend8(uint8_t *mem, uint8_t val, uint8_t o, size_t sz) +{ + uint8_t *limit = mem + sz; + while (mem < limit) { + *mem = BLEND_BYTE(*mem, val, o); + mem++; + } +} + +static void blend_yuv_exact(uint8_t *dst_cr, uint8_t *dst_cb, + int src_width, int x_odd, + uint8_t *(*blend_yuv_data)[ 3 ][ 2 ]) +{ + 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 components 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++; + } +} + +static uint8_t *(*blend_yuv_grow_extra_data(alphablend_t *extra_data, int osd_width))[ 3 ][ 2 ] +{ + struct __attribute__((packed)) header_s { + int id; + int max_width; + uint8_t *data[ 3 ][ 2 ]; + } **header = (struct header_s **)&extra_data->buffer; + + int needed_buffer_size = sizeof (**header) + osd_width * sizeof (uint8_t[ 3 ][ 2 ]); + + if (extra_data->buffer_size < needed_buffer_size) { + + free(extra_data->buffer); + extra_data->buffer = xine_xmalloc(needed_buffer_size); + + if (!extra_data->buffer) { + extra_data->buffer_size = 0; + return 0; + } + + extra_data->buffer_size = needed_buffer_size; + (*header)->max_width = 0; + } + + if ((*header)->id != ME_FOURCC('y', 'u', 'v', 0) || (*header)->max_width < osd_width) { + (*header)->id = ME_FOURCC('y', 'u', 'v', 0); + (*header)->max_width = osd_width; + + (*header)->data[ 0 ][ 0 ] = ((uint8_t *)extra_data->buffer) + sizeof (**header); + (*header)->data[ 0 ][ 1 ] = (*header)->data[ 0 ][ 0 ] + osd_width; + (*header)->data[ 1 ][ 0 ] = (*header)->data[ 0 ][ 1 ] + osd_width; + (*header)->data[ 1 ][ 1 ] = (*header)->data[ 1 ][ 0 ] + osd_width; + (*header)->data[ 2 ][ 0 ] = (*header)->data[ 1 ][ 1 ] + osd_width; + (*header)->data[ 2 ][ 1 ] = (*header)->data[ 2 ][ 0 ] + osd_width; + } + + return &(*header)->data; +} + +void _x_blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, + int dst_width, int dst_height, int dst_pitches[3], + alphablend_t *extra_data) +{ + int enable_exact_blending = !extra_data->disable_exact_blending; + clut_t *my_clut; + uint8_t *my_trans; + + int src_width = img_overl->width; + int src_height = img_overl->height; + rle_elem_t *rle = img_overl->rle; + rle_elem_t *rle_limit = rle + img_overl->num_rle; + int x_off = img_overl->x + extra_data->offset_x; + int y_off = img_overl->y + extra_data->offset_y; + int x_odd = x_off & 1; + int y_odd = y_off & 1; + int ymask,xmask; + int rle_this_bite; + int rle_remainder; + int rlelen; + int x, y; + int hili_right, hili_left; + int clip_right, clip_left, clip_top; + uint8_t clr=0; + + int any_line_buffered = 0; + int exact_blend_width = ((src_width <= (dst_width - x_off)) ? src_width : (dst_width - x_off)); + int exact_blend_width_m2 = (x_odd + exact_blend_width + 1) & ~1; /* make it a (larger) multiple of 2 */ + uint8_t *(*blend_yuv_data)[ 3 ][ 2 ] = 0; + + 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); + 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 + my_clut = (clut_t*) img_overl->hili_color; + my_trans = img_overl->hili_trans; + + /* checks to avoid drawing overlay outside the destination buffer */ + if( (x_off + src_width) <= dst_width ) + clip_right = src_width; + else + clip_right = dst_width - x_off; + + if( x_off >= 0 ) + clip_left = 0; + else + clip_left = -x_off; + + if( y_off >= 0 ) + clip_top = 0; + else + clip_top = -y_off; + + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; + + /* make highlight area fit into clip area */ + if( img_overl->hili_right <= clip_right ) + hili_right = img_overl->hili_right; + else + hili_right = clip_right; + + if( img_overl->hili_left >= clip_left ) + hili_left = img_overl->hili_left; + else + hili_left = clip_left; + + if (src_height <= 0) + return; + + if (enable_exact_blending) { + if (exact_blend_width <= 0) + return; + + blend_yuv_data = blend_yuv_grow_extra_data(extra_data, exact_blend_width_m2); + if (!blend_yuv_data) + return; + + /* make linebuffer transparent */ + memset(&(*blend_yuv_data)[ 0 ][ 0 ][ 0 ], 0, exact_blend_width_m2); + memset(&(*blend_yuv_data)[ 0 ][ 1 ][ 0 ], 0, exact_blend_width_m2); + } + + rlelen=rle_remainder=0; + for (y = 0; y < src_height; y++) { + if (rle >= rle_limit) { +#ifdef LOG_BLEND_YUV + printf("y-rle_limit\n"); +#endif + break; + } + + ymask = ((y < img_overl->hili_top) || (y >= img_overl->hili_bottom)); + xmask = 0; +#ifdef LOG_BLEND_YUV + printf("X started ymask=%d y=%d src_height=%d\n",ymask, y, src_height); +#endif + + for (x = 0; x < src_width;) { + uint16_t o; + int clipped = (y < clip_top); + + 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 + + if ((rlelen < 0) || (rle_remainder < 0)) { +#ifdef LOG_BLEND_YUV + printf("alphablend: major bug in blend_yuv < 0\n"); +#endif + } + if (rlelen == 0) { + rle_remainder = rlelen = rle->len; + clr = rle->color; + rle++; + } + if (rle_remainder == 0) { + rle_remainder = rlelen; + } + if ((rle_remainder + x) > src_width) { + /* Do something for long rlelengths */ + rle_remainder = src_width - x; + } +#ifdef LOG_BLEND_YUV + printf("2:rle_len=%d, remainder=%d, x=%d\n",rlelen, rle_remainder, x); +#endif + + if (ymask == 0) { + if (x < hili_left) { + /* Starts outside highlight area */ + if ((x + rle_remainder) > hili_left ) { +#ifdef LOG_BLEND_YUV + printf("Outside highlight left %d, ending inside\n", hili_left); +#endif + /* Cutting needed, starts outside, ends inside */ + rle_this_bite = (hili_left - x); + rle_remainder -= rle_this_bite; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + xmask = 0; + } else { +#ifdef LOG_BLEND_YUV + printf("Outside highlight left %d, ending outside\n", hili_left); +#endif + /* no cutting needed, starts outside, ends outside */ + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + xmask = 0; + } + if( x < clip_left ) + clipped = 1; + } else if (x < hili_right) { + /* Starts inside highlight area */ + if ((x + rle_remainder) > hili_right ) { +#ifdef LOG_BLEND_YUV + printf("Inside highlight right %d, ending outside\n", hili_right); +#endif + /* Cutting needed, starts inside, ends outside */ + rle_this_bite = (hili_right - x); + rle_remainder -= rle_this_bite; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->hili_color; + my_trans = img_overl->hili_trans; + xmask++; + } else { +#ifdef LOG_BLEND_YUV + printf("Inside highlight right %d, ending inside\n", hili_right); +#endif + /* no cutting needed, starts inside, ends inside */ + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->hili_color; + my_trans = img_overl->hili_trans; + xmask++; + } + } else if (x >= hili_right) { + /* Starts outside highlight area, ends outside highlight area */ + if ((x + rle_remainder ) > src_width ) { +#ifdef LOG_BLEND_YUV + printf("Outside highlight right %d, ending eol\n", hili_right); +#endif + /* Cutting needed, starts outside, ends at right edge */ + /* It should never reach here due to the earlier test of src_width */ + rle_this_bite = (src_width - x ); + rle_remainder -= rle_this_bite; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + xmask = 0; + } else { + /* no cutting needed, starts outside, ends outside */ +#ifdef LOG_BLEND_YUV + printf("Outside highlight right %d, ending outside\n", hili_right); +#endif + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + xmask = 0; + } + if( x + rle_this_bite >= clip_right ) + clipped = 1; + } + } else { + /* Outside highlight are due to y */ + /* no cutting needed, starts outside, ends outside */ + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + xmask = 0; + } + o = my_trans[clr]; +#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 (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; + } + + if (enable_exact_blending) { + /* remember opacity of current line */ + memset(&(*blend_yuv_data)[ 0 ][ (y + y_odd) & 1 ][ x + x_odd ], o, rle_this_bite); + any_line_buffered |= ((y + y_odd) & 1) ? 2 : 1; + } + + if (o && !clipped) { + if(o >= 15) { + memset(dst_y + x, my_clut[clr].y, rle_this_bite); + if (!enable_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); + } + } + } else { + mem_blend8(dst_y + x, my_clut[clr].y, o, rle_this_bite); + if (!enable_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); + } + } + } + + if (enable_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); + } + } + } +#ifdef LOG_BLEND_YUV + printf("rle_this_bite=%d, remainder=%d, x=%d\n",rle_this_bite, rle_remainder, x); +#endif + x += rle_this_bite; + } + + if ((y + y_odd) & 1) { + if (enable_exact_blending) { + /* blend buffered lines */ + if (any_line_buffered) { + if (!(any_line_buffered & 2)) { + /* make second line transparent */ + memset(&(*blend_yuv_data)[ 0 ][ 1 ][ 0 ], 0, exact_blend_width_m2); + } + + blend_yuv_exact(dst_cr, dst_cb, exact_blend_width, x_odd, blend_yuv_data); + + any_line_buffered = 0; + } + } + + dst_cr += dst_pitches[2]; + dst_cb += dst_pitches[1]; + } + + dst_y += dst_pitches[0]; + } + + if (enable_exact_blending) { + /* blend buffered lines */ + if (any_line_buffered) { + if (!(any_line_buffered & 2)) { + /* make second line transparent */ + memset(&(*blend_yuv_data)[ 0 ][ 1 ][ 0 ], 0, exact_blend_width_m2); + } + + blend_yuv_exact(dst_cr, dst_cb, exact_blend_width, x_odd, blend_yuv_data); + } + } + +#ifdef LOG_BLEND_YUV + printf("overlay_blend ended\n"); +#endif +} + +static void blend_yuy2_exact(uint8_t *dst_cr, uint8_t *dst_cb, + int src_width, int x_odd, + uint8_t *(*blend_yuy2_data)[ 3 ]) +{ + int x; + + for (x = 0; x < src_width; x += 2) { + /* get opacity of the 2 pixels that share chroma */ + int o0 = (*blend_yuy2_data)[ 0 ][ x + 0 ]; + int o1 = (*blend_yuy2_data)[ 0 ][ x + 1 ]; + + /* are there any pixels a little bit opaque? */ + if (o0 || o1) { + /* get the chroma components of the 2 pixels */ + int cr0 = -128 + (*blend_yuy2_data)[ 1 ][ x + 0 ]; + int cr1 = -128 + (*blend_yuy2_data)[ 1 ][ x + 1 ]; + + int cb0 = -128 + (*blend_yuy2_data)[ 2 ][ x + 0 ]; + int cb1 = -128 + (*blend_yuy2_data)[ 2 ][ x + 1 ]; + + /* are all pixels completely opaque? */ + if (o0 >= 0xf && o1 >= 0xf) { + /* set the output chroma to the average of the two pixels */ + *dst_cr = 128 + (cr0 + cr1) / 2; + *dst_cb = 128 + (cb0 + cb1) / 2; + } else { + int t2, cr, cb; + + /* blending required, so clamp opacity values to allowed range */ + if (o0 > 0xf) o0 = 0xf; + if (o1 > 0xf) o1 = 0xf; + + /* calculate transparency of background over the two pixels */ + t2 = (0xf - o0) + (0xf - o1); + + /* get background chroma */ + cr = -128 + *dst_cr; + cb = -128 + *dst_cb; + + /* blend the output chroma to the average of the two pixels */ + *dst_cr = 128 + (cr * t2 + cr0 * o0 + cr1 * o1) / (2 * 0xf); + *dst_cb = 128 + (cb * t2 + cb0 * o0 + cb1 * o1) / (2 * 0xf); + } + } + + /* next chroma sample */ + dst_cr += 4; + dst_cb += 4; + } +} + +static uint8_t *(*blend_yuy2_grow_extra_data(alphablend_t *extra_data, int osd_width))[ 3 ] +{ + struct __attribute__((packed)) header_s { + int id; + int max_width; + uint8_t *data[ 3 ]; + } **header = (struct header_s **)&extra_data->buffer; + + int needed_buffer_size = sizeof (**header) + osd_width * sizeof (uint8_t[ 3 ]); + + if (extra_data->buffer_size < needed_buffer_size) { + + free(extra_data->buffer); + extra_data->buffer = xine_xmalloc(needed_buffer_size); + + if (!extra_data->buffer) { + extra_data->buffer_size = 0; + return 0; + } + + extra_data->buffer_size = needed_buffer_size; + (*header)->max_width = 0; + } + + if ((*header)->id != ME_FOURCC('y', 'u', 'y', '2') || (*header)->max_width < osd_width) { + (*header)->id = ME_FOURCC('y', 'u', 'y', '2'); + (*header)->max_width = osd_width; + + (*header)->data[ 0 ] = ((uint8_t *)extra_data->buffer) + sizeof (**header); + (*header)->data[ 1 ] = (*header)->data[ 0 ] + osd_width; + (*header)->data[ 2 ] = (*header)->data[ 1 ] + osd_width; + } + + return &(*header)->data; +} + +void _x_blend_yuy2 (uint8_t * dst_img, vo_overlay_t * img_overl, + int dst_width, int dst_height, int dst_pitch, + alphablend_t *extra_data) +{ + int enable_exact_blending = !extra_data->disable_exact_blending; + clut_t *my_clut; + uint8_t *my_trans; + + int src_width = img_overl->width; + int src_height = img_overl->height; + rle_elem_t *rle = img_overl->rle; + rle_elem_t *rle_limit = rle + img_overl->num_rle; + int x_off = img_overl->x + extra_data->offset_x; + int y_off = img_overl->y + extra_data->offset_y; + int x_odd = x_off & 1; + int ymask; + int rle_this_bite; + int rle_remainder; + int rlelen; + int x, y; + int l = 0; + int hili_right, hili_left; + int clip_right, clip_left, clip_top; + + union { + uint32_t value; + uint8_t b[4]; + uint16_t h[2]; + } yuy2; + + uint8_t clr = 0; + + int any_line_buffered = 0; + int exact_blend_width = ((src_width <= (dst_width - x_off)) ? src_width : (dst_width - x_off)); + int exact_blend_width_m2 = (x_odd + exact_blend_width + 1) & ~1; /* make it a (larger) multiple of 2 */ + uint8_t *(*blend_yuy2_data)[ 3 ] = 0; + + uint8_t *dst_y = dst_img + dst_pitch * y_off + 2 * x_off; + uint8_t *dst; + + my_clut = (clut_t*) img_overl->hili_color; + my_trans = img_overl->hili_trans; + + /* checks to avoid drawing overlay outside the destination buffer */ + if( (x_off + src_width) <= dst_width ) + clip_right = src_width; + else + clip_right = dst_width - x_off; + + if( x_off >= 0 ) + clip_left = 0; + else + clip_left = -x_off; + + if( y_off >= 0 ) + clip_top = 0; + else + clip_top = -y_off; + + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; + + /* make highlight area fit into clip area */ + if( img_overl->hili_right <= clip_right ) + hili_right = img_overl->hili_right; + else + hili_right = clip_right; + + if( img_overl->hili_left >= clip_left ) + hili_left = img_overl->hili_left; + else + hili_left = clip_left; + + if (src_height <= 0) + return; + + if (enable_exact_blending) { + if (exact_blend_width <= 0) + return; + + blend_yuy2_data = blend_yuy2_grow_extra_data(extra_data, exact_blend_width_m2); + if (!blend_yuy2_data) + return; + + /* make linebuffer transparent */ + memset(&(*blend_yuy2_data)[ 0 ][ 0 ], 0, exact_blend_width_m2); + } + + rlelen=rle_remainder=0; + for (y = 0; y < src_height; y++) { + if (rle >= rle_limit) + break; + + ymask = ((y < img_overl->hili_top) || (y >= img_overl->hili_bottom)); + + dst = dst_y; + for (x = 0; x < src_width;) { + uint16_t o; + int clipped = (y < clip_top); + + if (rle >= rle_limit) + break; + + if ((rlelen < 0) || (rle_remainder < 0)) { +#ifdef LOG_BLEND_YUV + printf("alphablend: major bug in blend_yuv < 0\n"); +#endif + } + if (rlelen == 0) { + rle_remainder = rlelen = rle->len; + clr = rle->color; + rle++; + } + if (rle_remainder == 0) { + rle_remainder = rlelen; + } + if ((rle_remainder + x) > src_width) { + /* Do something for long rlelengths */ + rle_remainder = src_width - x; + } +#ifdef LOG_BLEND_YUV + printf("2:rle_len=%d, remainder=%d, x=%d\n",rlelen, rle_remainder, x); +#endif + + if (ymask == 0) { + if (x < hili_left) { + /* Starts outside highlight area */ + if ((x + rle_remainder) > hili_left ) { +#ifdef LOG_BLEND_YUV + printf("Outside highlight left %d, ending inside\n", hili_left); +#endif + /* Cutting needed, starts outside, ends inside */ + rle_this_bite = (hili_left - x); + rle_remainder -= rle_this_bite; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + } else { +#ifdef LOG_BLEND_YUV + printf("Outside highlight left %d, ending outside\n", hili_left); +#endif + /* no cutting needed, starts outside, ends outside */ + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + } + if( x < clip_left ) + clipped = 1; + } else if (x < hili_right) { + /* Starts inside highlight area */ + if ((x + rle_remainder) > hili_right ) { +#ifdef LOG_BLEND_YUV + printf("Inside highlight right %d, ending outside\n", hili_right); +#endif + /* Cutting needed, starts inside, ends outside */ + rle_this_bite = (hili_right - x); + rle_remainder -= rle_this_bite; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->hili_color; + my_trans = img_overl->hili_trans; + } else { +#ifdef LOG_BLEND_YUV + printf("Inside highlight right %d, ending inside\n", hili_right); +#endif + /* no cutting needed, starts inside, ends inside */ + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->hili_color; + my_trans = img_overl->hili_trans; + } + } else if (x >= hili_right) { + /* Starts outside highlight area, ends outsite highlight area */ + if ((x + rle_remainder ) > src_width ) { +#ifdef LOG_BLEND_YUV + printf("Outside highlight right %d, ending eol\n", hili_right); +#endif + /* Cutting needed, starts outside, ends at right edge */ + /* It should never reach here due to the earlier test of src_width */ + rle_this_bite = (src_width - x ); + rle_remainder -= rle_this_bite; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + } else { + /* no cutting needed, starts outside, ends outside */ +#ifdef LOG_BLEND_YUV + printf("Outside highlight right %d, ending outside\n", hili_right); +#endif + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + } + if( x + rle_this_bite >= clip_right ) + clipped = 1; + } + } else { + /* Outside highlight are due to y */ + /* no cutting needed, starts outside, ends outside */ + rle_this_bite = rle_remainder; + rle_remainder = 0; + rlelen -= rle_this_bite; + my_clut = (clut_t*) img_overl->color; + my_trans = img_overl->trans; + } + o = my_trans[clr]; + + 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; + } + + if (enable_exact_blending) { + /* remember opacity of current line */ + memset(&(*blend_yuy2_data)[ 0 ][ x + x_odd ], o, rle_this_bite); + any_line_buffered = 1; + } + + if (o && !clipped) { + if (!enable_exact_blending) { + l = rle_this_bite>>1; + if( !((x_odd+x) & 1) ) { + yuy2.b[0] = my_clut[clr].y; + yuy2.b[1] = my_clut[clr].cb; + yuy2.b[2] = my_clut[clr].y; + yuy2.b[3] = my_clut[clr].cr; + } else { + yuy2.b[0] = my_clut[clr].y; + yuy2.b[1] = my_clut[clr].cr; + yuy2.b[2] = my_clut[clr].y; + yuy2.b[3] = my_clut[clr].cb; + } + } + if (o >= 15) { + if (!enable_exact_blending) { + while(l--) { + *(uint16_t *)dst = yuy2.h[0]; + dst += 2; + *(uint16_t *)dst = yuy2.h[1]; + dst += 2; + } + if(rle_this_bite & 1) { + *(uint16_t *)dst = yuy2.h[0]; + dst += 2; + } + } else { + l = rle_this_bite; + while (l--) { + *dst = my_clut[clr].y; + dst += 2; + } + } + } else { + if (!enable_exact_blending) { + if( l ) { + mem_blend32(dst, &yuy2.b[0], o, l); + dst += 4*l; + } + + if(rle_this_bite & 1) { + *dst = BLEND_BYTE(*dst, yuy2.b[0], o); + dst++; + *dst = BLEND_BYTE(*dst, yuy2.b[1], o); + dst++; + } + } else { + l = rle_this_bite; + while (l--) { + *dst = BLEND_BYTE(*dst, my_clut[clr].y, o); + dst += 2; + } + } + } + + if (enable_exact_blending) { + /* remember chroma of current line */ + memset(&(*blend_yuy2_data)[ 1 ][ x + x_odd ], my_clut[ clr ].cr, rle_this_bite); + memset(&(*blend_yuy2_data)[ 2 ][ x + x_odd ], my_clut[ clr ].cb, rle_this_bite); + } + } else { + dst += rle_this_bite*2; + } + } + + x += rle_this_bite; + } + + if (enable_exact_blending) { + /* blend buffered line */ + if (any_line_buffered) { + blend_yuy2_exact(dst_y - x_odd * 2 + 3, dst_y - x_odd * 2 + 1, exact_blend_width, x_odd, blend_yuy2_data); + + any_line_buffered = 0; + } + } + + dst_y += dst_pitch; + } +} + +void _x_clear_xx44_palette(xx44_palette_t *p) +{ + register int i; + register uint32_t *cluts = p->cluts; + register int *ids = p->lookup_cache; + + i= p->size; + while(i--) + *cluts++ = 0; + i = 2*OVL_PALETTE_SIZE; + while(i--) + *ids++ = -1; + p->max_used=1; +} + +void _x_init_xx44_palette(xx44_palette_t *p, unsigned num_entries) +{ + p->size = (num_entries > XX44_PALETTE_SIZE) ? XX44_PALETTE_SIZE : num_entries; +} + +void _x_dispose_xx44_palette(xx44_palette_t *p) +{ +} + +static void colorToPalette(const uint32_t *icolor, unsigned char *palette_p, + unsigned num_xvmc_components, char *xvmc_components) +{ + const clut_t *color = (const clut_t *) icolor; + int i; + for (i=0; i<num_xvmc_components; ++i) { + switch(xvmc_components[i]) { + case 'V': *palette_p = color->cr; break; + case 'U': *palette_p = color->cb; break; + case 'Y': + default: *palette_p = color->y; break; + } + *palette_p++; + } +} + + +void _x_xx44_to_xvmc_palette(const xx44_palette_t *p,unsigned char *xvmc_palette, + unsigned first_xx44_entry, unsigned num_xx44_entries, + unsigned num_xvmc_components, char *xvmc_components) +{ + register int i; + register const uint32_t *cluts = p->cluts + first_xx44_entry; + + for (i=0; i<num_xx44_entries; ++i) { + if ((cluts - p->cluts) < p->size) { + colorToPalette(cluts++, xvmc_palette, num_xvmc_components, xvmc_components); + xvmc_palette += num_xvmc_components; + } + } +} + +static int xx44_paletteIndex(xx44_palette_t *p, int color, uint32_t clut) +{ + + register int i; + register uint32_t *cluts = p->cluts; + register int tmp; + + if ((tmp = p->lookup_cache[color]) >= 0) + if (cluts[tmp] == clut) return tmp; + + for (i=0; i<p->max_used; ++i) { + if (*cluts++ == clut) return p->lookup_cache[color] = i; + } + + if (p->max_used == p->size -1) { + printf("video_out: Warning! Out of xx44 palette colors!\n"); + return 1; + } + p->cluts[p->max_used] = clut; + return p->lookup_cache[color] = p->max_used++; +} + +static void memblend_xx44(uint8_t *mem,uint8_t val, register size_t size, uint8_t mask) +{ + register uint8_t + masked_val; + + if (0 == (masked_val = val & mask)) return; + + while(size--) { + if ((*mem & mask) <= masked_val ) *mem = val; + mem++; + } +} + +void _x_blend_xx44 (uint8_t *dst_img, vo_overlay_t *img_overl, + int dst_width, int dst_height, int dst_pitch, + alphablend_t *extra_data, + xx44_palette_t *palette,int ia44) +{ + int src_width = img_overl->width; + int src_height = img_overl->height; + rle_elem_t *rle = img_overl->rle; + rle_elem_t *rle_limit = rle + img_overl->num_rle; + int mask; + int x_off = img_overl->x + extra_data->offset_x; + int y_off = img_overl->y + extra_data->offset_y; + int x, y; + uint8_t norm_pixel,hili_pixel; + uint8_t *dst_y; + uint8_t *dst; + uint8_t alphamask = (ia44) ? 0x0F : 0xF0; + int hili_right, hili_left; + int clip_right, clip_left, clip_top; + + if (!img_overl) + return; + + dst_y = dst_img + dst_pitch*y_off + x_off; + + /* checks to avoid drawing overlay outside the destination buffer */ + if( (x_off + src_width) <= dst_width ) + clip_right = src_width; + else + clip_right = dst_width - x_off; + + if( x_off >= 0 ) + clip_left = 0; + else + clip_left = -x_off; + + if( y_off >= 0 ) + clip_top = 0; + else + clip_top = -y_off; + + if( (src_height + y_off) > dst_height ) + src_height = dst_height - y_off; + + /* make highlight area fit into clip area */ + if( img_overl->hili_right <= clip_right ) + hili_right = img_overl->hili_right; + else + hili_right = clip_right; + + if( img_overl->hili_left >= clip_left ) + hili_left = img_overl->hili_left; + else + hili_left = clip_left; + + for (y = 0; y < src_height; y++) { + + mask = !(y < img_overl->hili_top || y >= img_overl->hili_bottom); + dst = dst_y; + + for (x = 0; x < src_width;) { + int len = (x + rle->len > hili_right) ? hili_right - x : rle->len; + int clipped = (y < clip_top); + + if (len > 0) { + norm_pixel = (uint8_t)((xx44_paletteIndex(palette,rle->color, + img_overl->color[rle->color]) << 4) | + (img_overl->trans[rle->color] & 0x0F)); + hili_pixel = (uint8_t)((xx44_paletteIndex(palette,rle->color+OVL_PALETTE_SIZE, + img_overl->hili_color[rle->color]) << 4) | + (img_overl->hili_trans[rle->color] & 0x0F)); + if (!ia44) { + norm_pixel = ((norm_pixel & 0x0F) << 4) | ((norm_pixel & 0xF0) >> 4); + hili_pixel = ((hili_pixel & 0x0F) << 4) | ((hili_pixel & 0xF0) >> 4); + } + if (mask) { + if (x < hili_left) { + if (x < clip_left) + clipped = 1; + + if (x + len <= hili_left) { + if(!clipped) + memblend_xx44(dst,norm_pixel,len, alphamask); + dst += len; + } else { + if(!clipped) + memblend_xx44(dst,norm_pixel,hili_left -x, alphamask); + dst += hili_left - x; + len -= hili_left - x; + if (len <= hili_right - hili_left) { + if(!clipped) + memblend_xx44(dst,hili_pixel,len, alphamask); + dst += len; + } else { + if(!clipped) + memblend_xx44(dst,hili_pixel, hili_right - hili_left, + alphamask); + dst += hili_right - hili_left; + len -= hili_right - hili_left; + if(!clipped) + memblend_xx44(dst,norm_pixel,len, alphamask); + dst += len; + } + } + } else if (x < hili_right) { + if (len <= hili_right - x) { + if(!clipped) + memblend_xx44(dst,hili_pixel,len, alphamask); + dst += len; + } else { + if(!clipped) + memblend_xx44(dst,hili_pixel,hili_right - x,alphamask); + if (len > clip_right - x) + clipped = 1; + dst += hili_right - x; + len -= hili_right - x; + if(!clipped) + memblend_xx44(dst,norm_pixel,len, alphamask); + dst += len; + } + } else { + if (x > clip_right) + clipped = 1; + if(!clipped) + memblend_xx44(dst,norm_pixel,len, alphamask); + dst += len; + } + } else { + if(!clipped) + memblend_xx44(dst,norm_pixel,len, alphamask); + dst += len; + } + } + x += rle->len; + rle++; + if (rle >= rle_limit) break; + } + if (rle >= rle_limit) break; + dst_y += dst_pitch; + } +} + +static void alphablend_disable_exact_osd_alpha_blending_changed(void *user_data, xine_cfg_entry_t *entry) +{ + alphablend_t *extra_data = (alphablend_t *)user_data; + + extra_data->disable_exact_blending = entry->num_value; +} + +void _x_alphablend_init(alphablend_t *extra_data, xine_t *xine) +{ + config_values_t *config = xine->config; + + extra_data->buffer = 0; + extra_data->buffer_size = 0; + extra_data->offset_x = 0; + extra_data->offset_y = 0; + + extra_data->disable_exact_blending = + config->register_bool(config, "video.output.disable_exact_alphablend", 0, + _("disable exact alpha blending of overlays"), + _("If you experience a performance impact when an On Screen Display or other " + "overlays like DVD subtitles are active, then you might want to enable this option.\n" + "The result is that alpha blending of overlays is less accurate than before, " + "but the CPU usage will be decreased as well."), + 10, alphablend_disable_exact_osd_alpha_blending_changed, extra_data); +} + +void _x_alphablend_free(alphablend_t *extra_data) +{ + if (extra_data->buffer) { + free(extra_data->buffer); + extra_data->buffer = NULL; + } + + extra_data->buffer_size = 0; +} + diff --git a/src/xine-engine/alphablend.h b/src/xine-engine/alphablend.h new file mode 100644 index 000000000..ee784c4b1 --- /dev/null +++ b/src/xine-engine/alphablend.h @@ -0,0 +1,136 @@ +/* + * + * Copyright (C) 2000 Thomas Mirlacher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * The author may be reached as <dent@linuxvideo.org> + * + *------------------------------------------------------------ + * + */ + +#ifndef __ALPHABLEND_H__ +#define __ALPHABLEND_H__ + +#include "video_out.h" + +typedef struct { + void *buffer; + int buffer_size; + + int disable_exact_blending; + + int offset_x, offset_y; +} alphablend_t; + +void _x_alphablend_init(alphablend_t *extra_data, xine_t *xine); +void _x_alphablend_free(alphablend_t *extra_data); + +/* _MSC_VER port changes */ +#undef ATTRIBUTE_PACKED +#undef PRAGMA_PACK_BEGIN +#undef PRAGMA_PACK_END + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) || defined(__ICC) +#define ATTRIBUTE_PACKED __attribute__ ((packed)) +#define PRAGMA_PACK 0 +#endif + +#if !defined(ATTRIBUTE_PACKED) +#define ATTRIBUTE_PACKED +#define PRAGMA_PACK 1 +#endif + +#if PRAGMA_PACK +#pragma pack(8) +#endif + +typedef struct { /* CLUT == Color LookUp Table */ + uint8_t cb : 8; + uint8_t cr : 8; + uint8_t y : 8; + uint8_t foo : 8; +} ATTRIBUTE_PACKED clut_t; + + +#if PRAGMA_PACK +#pragma pack() +#endif + +#define XX44_PALETTE_SIZE 32 + +typedef struct { + unsigned size; + unsigned max_used; + uint32_t cluts[XX44_PALETTE_SIZE]; + /* cache palette entries for both colors and hili_colors */ + int lookup_cache[OVL_PALETTE_SIZE*2]; +} xx44_palette_t; + + +void _x_blend_rgb16 (uint8_t * img, vo_overlay_t * img_overl, + int img_width, int img_height, + int dst_width, int dst_height, + alphablend_t *extra_data); + +void _x_blend_rgb24 (uint8_t * img, vo_overlay_t * img_overl, + int img_width, int img_height, + int dst_width, int dst_height, + alphablend_t *extra_data); + +void _x_blend_rgb32 (uint8_t * img, vo_overlay_t * img_overl, + int img_width, int img_height, + int dst_width, int dst_height, + alphablend_t *extra_data); + +void _x_blend_yuv (uint8_t *dst_base[3], vo_overlay_t * img_overl, + int dst_width, int dst_height, int dst_pitches[3], + alphablend_t *extra_data); + +void _x_blend_yuy2 (uint8_t * dst_img, vo_overlay_t * img_overl, + int dst_width, int dst_height, int dst_pitch, + alphablend_t *extra_data); + +/* + * This function isn't too smart about blending. We want to avoid creating new + * colors in the palette as a result from two non-zero colors needed to be + * blended. Instead we choose the color with the highest alpha value to be + * visible. Some parts of the code taken from the "VeXP" project. + */ + +void _x_blend_xx44 (uint8_t *dst_img, vo_overlay_t *img_overl, + int dst_width, int dst_height, int dst_pitch, + alphablend_t *extra_data, + xx44_palette_t *palette,int ia44); + +/* + * Functions to handle the xine-specific palette. + */ + +void _x_clear_xx44_palette(xx44_palette_t *p); +void _x_init_xx44_palette(xx44_palette_t *p, unsigned num_entries); +void _x_dispose_xx44_palette(xx44_palette_t *p); + +/* + * Convert the xine-specific palette to something useful. + */ + +void _x_xx44_to_xvmc_palette(const xx44_palette_t *p,unsigned char *xvmc_palette, + unsigned first_xx44_entry, unsigned num_xx44_entries, + unsigned num_xvmc_components, char *xvmc_components); + + +#endif |