From 278211c8514cd4002bd1e3907e9f3cdb00206f03 Mon Sep 17 00:00:00 2001 From: kmkaplan Date: Thu, 10 Jan 2002 17:17:37 +0000 Subject: Add spudec_new_scaled and spudec_draw_scaled for vobsub support. Change what is passed to spudec_new for vobsub support. Suppress conditional on USE_DVDREAD. Correct a bug where some subtitles with no Stop display control code would not display long enough. Be more strict with bogus packets. Transform everything that should into unsigned. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@4079 b3059339-0415-0410-9bf9-f77b7e298cf2 --- spudec.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 310 insertions(+), 39 deletions(-) (limited to 'spudec.c') diff --git a/spudec.c b/spudec.c index d887936748..af31bd7aaa 100644 --- a/spudec.c +++ b/spudec.c @@ -1,3 +1,4 @@ +#undef WITH_NO_ANTIALIASING /* SPUdec.c Skeleton of function spudec_process_controll() is from xine sources. Further works: @@ -14,35 +15,46 @@ #include "config.h" #include "mp_msg.h" -#ifdef USE_DVDREAD - #include +#include #include #include #include +#ifndef WITH_NO_ANTIALIASING +#include +#endif #include "spudec.h" +#define MIN(a, b) ((a)<(b)?(a):(b)) + typedef struct { - dvd_priv_t *dvd_info; /* Info from libmpdemux */ + unsigned int global_palette[16]; + unsigned int orig_frame_width, orig_frame_height; unsigned char* packet; size_t packet_reserve; /* size of the memory pointed to by packet */ - int packet_offset; /* end of the currently assembled fragment */ - int packet_size; /* size of the packet once all fragments are assembled */ - int control_start; /* index of start of control data */ - int palette[4]; - int alpha[4]; - int now_pts; - int start_pts, end_pts; - int start_col, end_col; - int start_row, end_row; - int width, height, stride; - int current_nibble[2]; /* next data nibble (4 bits) to be - processed (for RLE decoding) for - even and odd lines */ + unsigned int packet_offset; /* end of the currently assembled fragment */ + unsigned int packet_size; /* size of the packet once all fragments are assembled */ + unsigned int control_start; /* index of start of control data */ + unsigned int palette[4]; + unsigned int alpha[4]; + unsigned int now_pts; + unsigned int start_pts, end_pts; + unsigned int start_col, end_col; + unsigned int start_row, end_row; + unsigned int width, height, stride; + unsigned int current_nibble[2]; /* next data nibble (4 bits) to be + processed (for RLE decoding) for + even and odd lines */ int deinterlace_oddness; /* 0 or 1, index into current_nibble */ size_t image_size; /* Size of the image buffer */ unsigned char *image; /* Grayscale value */ unsigned char *aimage; /* Alpha value */ + int scaled; /* flag if the image has already been scaled */ + unsigned int scaled_start_col, scaled_start_row; + unsigned int scaled_width, scaled_height, scaled_stride; + size_t scaled_image_size; + unsigned char *scaled_image; + unsigned char *scaled_aimage; } spudec_handle_t; static inline unsigned int get_be16(const unsigned char *p) @@ -65,7 +77,7 @@ static void next_line(spudec_handle_t *this) static inline unsigned char get_nibble(spudec_handle_t *this) { unsigned char nib; - int *nibblep = this->current_nibble + this->deinterlace_oddness; + unsigned int *nibblep = this->current_nibble + this->deinterlace_oddness; if (*nibblep / 2 >= this->control_start) { mp_msg(MSGT_SPUDEC,MSGL_WARN, "SPUdec: ERROR: get_nibble past end of packet\n"); return 0; @@ -95,15 +107,16 @@ static inline int mkalpha(int i) static void spudec_process_data(spudec_handle_t *this) { - int cmap[4], alpha[4]; - int i, x, y; + unsigned int cmap[4], alpha[4]; + unsigned int i, x, y; + this->scaled = 0; for (i = 0; i < 4; ++i) { alpha[i] = mkalpha(this->alpha[i]); if (alpha[i] == 0) cmap[i] = 0; else { - cmap[i] = ((this->dvd_info->cur_pgc->palette[this->palette[i]] >> 16) & 0xff) - alpha[i]; + cmap[i] = ((this->global_palette[this->palette[i]] >> 16) & 0xff) - alpha[i]; if (cmap[i] < 0) cmap[i] = 0; } @@ -134,7 +147,7 @@ static void spudec_process_data(spudec_handle_t *this) while (this->current_nibble[0] < i && this->current_nibble[1] / 2 < this->control_start && y < this->height) { - int len, color; + unsigned int len, color; unsigned int rle = 0; rle = get_nibble(this); if (rle < 0x04) { @@ -164,13 +177,13 @@ static void spudec_process_data(spudec_handle_t *this) } } -static void spudec_process_control(spudec_handle_t *this, int pts100) +static void spudec_process_control(spudec_handle_t *this, unsigned int pts100) { int a,b; /* Temporary vars */ - int date, type; - int off; - int start_off = 0; - int next_off; + unsigned int date, type; + unsigned int off; + unsigned int start_off = 0; + unsigned int next_off; this->control_start = get_be16(this->packet + 2); next_off = this->control_start; @@ -191,6 +204,7 @@ static void spudec_process_control(spudec_handle_t *this, int pts100) /* Start display */ mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Start display!\n"); this->start_pts = pts100 + date; + this->end_pts = UINT_MAX; break; case 0x02: /* Stop display */ @@ -223,11 +237,11 @@ static void spudec_process_control(spudec_handle_t *this, int pts100) b = get_be24(this->packet + off + 3); this->start_col = a >> 12; this->end_col = a & 0xfff; - this->width = this->end_col - this->start_col + 1; + this->width = (this->end_col < this->start_col) ? 0 : this->end_col - this->start_col + 1; this->stride = (this->width + 7) & ~7; /* Kludge: draw_alpha needs width multiple of 8 */ this->start_row = b >> 12; this->end_row = b & 0xfff; - this->height = this->end_row - this->start_row /* + 1 */; + this->height = (this->end_row < this->start_row) ? 0 : this->end_row - this->start_row /* + 1 */; mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Coords col: %d - %d row: %d - %d (%dx%d)\n", this->start_col, this->end_col, this->start_row, this->end_row, this->width, this->height); @@ -257,17 +271,21 @@ static void spudec_process_control(spudec_handle_t *this, int pts100) } } -static void spudec_decode(spudec_handle_t *this,int pts100) +static void spudec_decode(spudec_handle_t *this, unsigned int pts100) { spudec_process_control(this, pts100); spudec_process_data(this); } -void spudec_assemble(void *this, unsigned char *packet, int len, int pts100) +void spudec_assemble(void *this, unsigned char *packet, unsigned int len, unsigned int pts100) { spudec_handle_t *spu = (spudec_handle_t*)this; // spudec_heartbeat(this, pts100); + if (len < 2) { + mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: packet too short\n"); + return; + } if (spu->packet_offset == 0) { unsigned int len2 = get_be16(packet); // Start new fragment @@ -280,6 +298,10 @@ void spudec_assemble(void *this, unsigned char *packet, int len, int pts100) if (spu->packet != NULL) { spu->deinterlace_oddness = 0; spu->packet_size = len2; + if (len > len2) { + mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2); + return; + } memcpy(spu->packet, packet, len); spu->packet_offset = len; } @@ -297,8 +319,8 @@ void spudec_assemble(void *this, unsigned char *packet, int len, int pts100) // check if we have a complete packet (unfortunatelly packet_size is bad // for some disks) // if (spu->packet_offset == spu->packet_size) - { int x=0,y; - while(x>=0 && x+4<=spu->packet_offset){ + { unsigned int x=0,y; + while(x+4<=spu->packet_offset){ y=get_be16(spu->packet+x+2); // next control pointer mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size); if(x>=4 && x==y){ // if it points to self - we're done! @@ -327,11 +349,11 @@ void spudec_assemble(void *this, unsigned char *packet, int len, int pts100) void spudec_reset(void *this) // called after seek { spudec_handle_t *spu = (spudec_handle_t*)this; - spu->now_pts = -1; + spu->now_pts = 0; spu->packet_size = spu->packet_offset = 0; } -void spudec_heartbeat(void *this, int pts100) +void spudec_heartbeat(void *this, unsigned int pts100) { ((spudec_handle_t *)this)->now_pts = pts100; } @@ -344,28 +366,277 @@ void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsi spu->image, spu->aimage, spu->stride); } -void *spudec_new(dvd_priv_t *dvd_info) +/* transform mplayer's alpha value into an opacity value that is linear */ +static inline int canon_alpha(int alpha) +{ + return alpha ? 256 - alpha : 0; +} + +void spudec_draw_scaled(void *me, unsigned int dxs, unsigned int dys, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)) +{ + spudec_handle_t *spu = (spudec_handle_t *)me; + if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts) { + if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0 + || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) { + if (spu->image) + draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height, + spu->image, spu->aimage, spu->stride); + } + else { + if (!spu->scaled) { /* Resizing is needed */ + /* scaled_x = scalex * x / 0x100 + scaled_y = scaley * y / 0x100 + order of operations is important because of rounding. */ + unsigned int scalex = 0x100 * dxs / spu->orig_frame_width; + unsigned int scaley = 0x100 * dys / spu->orig_frame_height; + spu->scaled_start_col = spu->start_col * scalex / 0x100; + spu->scaled_start_row = spu->start_row * scaley / 0x100; + spu->scaled_width = spu->width * scalex / 0x100; + spu->scaled_height = spu->height * scaley / 0x100; + /* Kludge: draw_alpha needs width multiple of 8 */ + spu->scaled_stride = (spu->scaled_width + 7) & ~7; + if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) { + if (spu->scaled_image) { + free(spu->scaled_image); + spu->scaled_image_size = 0; + } + spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height); + if (spu->scaled_image) { + spu->scaled_image_size = spu->scaled_stride * spu->scaled_height; + spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size; + } + } + if (spu->scaled_image) { + unsigned int x, y; + /* Kludge: draw_alpha needs width multiple of 8. */ + if (spu->scaled_width < spu->scaled_stride) + for (y = 0; y < spu->scaled_height; ++y) { + memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0, + spu->scaled_stride - spu->scaled_width); + /* FIXME: Why is this one needed? */ + memset(spu->scaled_image + y * spu->scaled_stride + spu->scaled_width, 0, + spu->scaled_stride - spu->scaled_width); + } +#ifdef WITH_NO_ANTIALIASING + for (y = 0; y < spu->scaled_height; ++y) { + int unscaled_y = y * 0x100 / scaley; + int strides = spu->stride * unscaled_y; + int scaled_strides = spu->scaled_stride * y; + for (x = 0; x < spu->scaled_width; ++x) { + int unscaled_x = x * 0x100 / scalex; + spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x]; + spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x]; + } + } +#else + { + /* Any pixel (x, y) represents pixels from the original + rectangular region comprised between the columns + unscaled_y and unscaled_y + 0x100 / scaley and the rows + unscaled_x and unscaled_x + 0x100 / scalex + + The original rectangular region that the scaled pixel + represents is cut in 9 rectangular areas like this: + + +---+-----------------+---+ + | 1 | 2 | 3 | + +---+-----------------+---+ + | | | | + | 4 | 5 | 6 | + | | | | + +---+-----------------+---+ + | 7 | 8 | 9 | + +---+-----------------+---+ + + The width of the left column is at most one pixel and + it is never null and its right column is at a pixel + boundary. The height of the top row is at most one + pixel it is never null and its bottom row is at a + pixel boundary. The width and height of region 5 are + integral values. The width of the right column is + what remains and is less than one pixel. The height + of the bottom row is what remains and is less than + one pixel. + + The row above 1, 2, 3 is unscaled_y. The row between + 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4, + 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom. + The row beneath 7, 8, 9 is unscaled_y_bottom. + + The column left of 1, 4, 7 is unscaled_x. The column + between 1, 4, 7 and 2, 5, 8 is left_right_column. The + column between 2, 5, 8 and 3, 6, 9 is (unsigned + int)unscaled_x_right. The column right of 3, 6, 9 is + unscaled_x_right. */ + const double inv_scalex = (double) 0x100 / scalex; + const double inv_scaley = (double) 0x100 / scaley; + for (y = 0; y < spu->scaled_height; ++y) { + const double unscaled_y = y * inv_scaley; + const double unscaled_y_bottom = unscaled_y + inv_scaley; + const unsigned int top_low_row = MIN(unscaled_y_bottom, unscaled_y + 1.0); + const double top = top_low_row - unscaled_y; + const unsigned int height = unscaled_y_bottom > top_low_row + ? (unsigned int) unscaled_y_bottom - top_low_row + : 0; + const double bottom = unscaled_y_bottom > top_low_row + ? unscaled_y_bottom - floor(unscaled_y_bottom) + : 0.0; + for (x = 0; x < spu->scaled_width; ++x) { + const double unscaled_x = x * inv_scalex; + const double unscaled_x_right = unscaled_x + inv_scalex; + const unsigned int left_right_column = MIN(unscaled_x_right, unscaled_x + 1.0); + const double left = left_right_column - unscaled_x; + const unsigned int width = unscaled_x_right > left_right_column + ? (unsigned int) unscaled_x_right - left_right_column + : 0; + const double right = unscaled_x_right > left_right_column + ? unscaled_x_right - floor(unscaled_x_right) + : 0.0; + double color = 0.0; + double alpha = 0.0; + double tmp; + unsigned int base; + /* Now use these informations to compute a good alpha, + and lightness. The sum is on each of the 9 + region's surface and alpha and lightness. + + transformed alpha = sum(surface * alpha) / sum(surface) + transformed color = sum(surface * alpha * color) / sum(surface * alpha) + */ + /* 1: top left part */ + base = spu->stride * (unsigned int) unscaled_y; + tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]); + alpha += tmp; + color += tmp * spu->image[base + (unsigned int) unscaled_x]; + /* 2: top center part */ + if (width > 0) { + unsigned int walkx; + for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { + base = spu->stride * (unsigned int) unscaled_y + walkx; + tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + } + /* 3: top right part */ + if (right > 0.0) { + base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right; + tmp = right * top * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + /* 4: center left part */ + if (height > 0) { + unsigned int walky; + for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { + base = spu->stride * walky + (unsigned int) unscaled_x; + tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + } + /* 5: center part */ + if (width > 0 && height > 0) { + unsigned int walky; + for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { + unsigned int walkx; + base = spu->stride * walky; + for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { + tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]); + alpha += tmp; + color += tmp * spu->image[base + walkx]; + } + } + } + /* 6: center right part */ + if (right > 0.0 && height > 0) { + unsigned int walky; + for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { + base = spu->stride * walky + (unsigned int) unscaled_x_right; + tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + } + /* 7: bottom left part */ + if (bottom > 0.0) { + base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x; + tmp = left * bottom * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + /* 8: bottom center part */ + if (width > 0 && bottom > 0.0) { + unsigned int walkx; + base = spu->stride * (unsigned int) unscaled_y_bottom; + for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { + tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]); + alpha += tmp; + color += tmp * spu->image[base + walkx]; + } + } + /* 9: bottom right part */ + if (right > 0.0 && bottom > 0.0) { + base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right; + tmp = right * bottom * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + /* Finally mix these transparency and brightness information suitably */ + base = spu->scaled_stride * y + x; + spu->scaled_image[base] = color / alpha; + spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000; + if (spu->scaled_aimage[base]) { + spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base]; + if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255) + spu->scaled_image[base] = 256 - spu->scaled_aimage[base]; + } + } + } + } +#endif + spu->scaled = 1; + } + } + if (spu->scaled_image) + draw_alpha(spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height, + spu->scaled_image, spu->scaled_aimage, spu->scaled_stride); + } + } +} + +void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height) { spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t)); if (this) { - this->dvd_info = dvd_info; + if (palette) + memcpy(this->global_palette, palette, sizeof(this->global_palette)); + this->packet = NULL; + this->image = NULL; + this->scaled_image = NULL; + this->orig_frame_width = frame_width; + this->orig_frame_height = frame_height; } else perror("FATAL: spudec_init: calloc"); return this; } +void *spudec_new(unsigned int *palette) +{ + return spudec_new_scaled(palette, 0, 0); +} + void spudec_free(void *this) { spudec_handle_t *spu = (spudec_handle_t*)this; if (spu) { if (spu->packet) free(spu->packet); + if (spu->scaled_image) + free(spu->scaled_image); if (spu->image) free(spu->image); free(spu); } } - -#endif /* USE_DVDREAD */ - -- cgit v1.2.3