/* * Copyright (C) 2002, 2003 Jan Panteltje , * * Modified by Zachary Brewster-Geisz, 2003, to work on big-endian * machines. * * Modified by Henry Mason, 2003, to use both PNG and BMP, and to use * the dvdauthor submux format. * * Modified and copy right Jan Panteltje 2002 * 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. * * With many changes by Scott Smith (trckjunky@users.sourceforge.net) * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include "config.h" #include "compat.h" #include #include #include #include "rgb.h" #define FALSE 0 #define TRUE (!FALSE) #define CBUFSIZE 65536 #define PSBUFSIZE 10 static unsigned int add_offset; static int debug = 0; static int full_size = FALSE; static unsigned int pts, spts, subi, subs, subno; static int ofs, ofs1; static unsigned char sub[65536]; static unsigned char next_bits; static char *base_name; static int have_bits; static FILE *fdo; typedef struct { unsigned char r, g, b, t; } palt; static palt bpal[16]; struct spu { unsigned char *img; unsigned int x0, y0, xd, yd, pts[2], subno, force_display, nummap; struct colormap *map; struct spu *next; }; static struct spu *spus=0; struct button { char *name; int autoaction; int x1,y1,x2,y2; char *up,*down,*left,*right; int grp; }; struct dispdetails { int pts[2]; int numpal; uint32_t palette[16]; int numcoli; uint32_t coli[6]; int numbuttons; struct button *buttons; struct dispdetails *next; }; static struct dispdetails *dd=0; struct colormap { uint16_t color; uint16_t contrast; int x1,y1,x2,y2; }; static unsigned int read4(unsigned char *p) { return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; } static unsigned int read2(unsigned char *p) { return (p[0]<<8)|p[1]; } static char *readpstr(char *b,int *i) { char *s=strdup(b+i[0]); i[0]+=strlen(s)+1; return s; } static unsigned char get_next_bits() { if (!have_bits) { next_bits = sub[ofs++]; have_bits = TRUE; return next_bits >> 4; } have_bits = FALSE; return next_bits & 15; } static unsigned int getpts(unsigned char *buf) { if (!(buf[1] & 0xc0) || (buf[2] < 4) || ((buf[3] & 0xe1) != 0x21) || ((buf[5] & 1) != 1) || ((buf[7] & 1) != 1)) return -1; return (buf[7] >> 1) + ((unsigned int) buf[6] << 7) + (((unsigned int) buf[5] & 254) << 14) + ((unsigned int) buf[4] << 22) + (((unsigned int) buf[3] & 14) << 29); } static void addspu(struct spu *s) { struct spu **f=&spus; while( *f ) f=&(f[0]->next); *f=s; } static void adddd(struct dispdetails *d) { struct dispdetails **dp=ⅆ while (*dp ) dp=&(dp[0]->next); *dp=d; } static int dvddecode() { unsigned int io; uint16_t size, dsize, i, x, y, t; unsigned char c; struct spu *s; size = read2(sub); dsize = read2(sub+2); ofs = -1; if (debug > 1) fprintf(stderr, "packet: %d bytes, first block offset=%d\n", size, dsize); s=malloc(sizeof(struct spu)); memset(s,0,sizeof(struct spu)); s->subno=subno++; s->pts[0] = s->pts[1] = -1; s->nummap=1; s->map=malloc(sizeof(struct colormap)); memset(s->map,0,sizeof(struct colormap)); s->map[0].x2=0x7fffffff; s->map[0].y2=0x7fffffff; i = dsize + 4; t = read2(sub+dsize); if (debug > 2) fprintf(stderr, "\tBLK(%5d): time offset: %d; next: %d\n", dsize, t, read2(sub+dsize+2)); while (i < size) { c = sub[i]; switch (c) { case 0x0: //force start display if (debug > 4) fprintf(stderr, "\tcmd(%5d): force start display\n",i); s->force_display = TRUE; // fall through case 0x01: if (debug > 4 && c==0x01) fprintf(stderr, "\tcmd(%5d): start display\n",i); i++; s->pts[0] = t * 1024 + spts; break; case 0x02: if (debug > 4) fprintf(stderr, "\tcmd(%5d): end display\n",i); s->pts[1] = t * 1024 + spts; i++; break; case 0x03: if (debug > 4) fprintf(stderr, "\tcmd(%5d): palette=%02x%02x\n", i, sub[i + 1], sub[i + 2]); s->map[0].color=read2(sub+i+1); i += 3; break; case 0x04: if (debug > 4) fprintf(stderr, "\tcmd(%5d): transparency=%02x%02x\n", i, sub[i + 1], sub[i + 2]); s->map[0].contrast=read2(sub+i+1); i += 3; break; case 0x05: s->x0 = ((((unsigned int) sub[i + 1]) << 4) + (sub[i + 2] >> 4)); s->xd = (((sub[i + 2] & 0x0f) << 8) + sub[i + 3]) - s->x0 + 1; s->y0 = ((((unsigned int) sub[i + 4]) << 4) + (sub[i + 5] >> 4)); s->yd = (((sub[i + 5] & 0x0f) << 8) + sub[i + 6]) - s->y0 + 1; if (debug > 4) fprintf(stderr, "\tcmd(%5d): image corner=%d,%d, size=%d,%d\n", i, s->x0, s->y0, s->xd, s->yd); i += 7; break; case 0x06: if( ofs>=0 ) fprintf(stderr,"WARN: image pointer already supplied for this subpicture\n"); ofs = read2(sub+i+1); ofs1 = read2(sub+i+3); if (debug > 4) fprintf(stderr, "\tcmd(%5d): image offsets=%d,%d\n", i, ofs, ofs1); i += 5; break; case 0xff: if (i + 5 > size) { if (debug > 4) fprintf(stderr,"\tcmd(%5d): end cmd\n",i); i = size; break; } t = read2(sub + i + 1); if (debug > 4) { fprintf(stderr, "\tcmd(%5d): end cmd\n",i); fprintf(stderr, "\tBLK(%5d): time offset: %d; next: %d\n", i+1, t, read2(sub+i+3)); } if ((sub[i + 3] != sub[dsize + 2]) || (sub[i + 4] != sub[dsize + 3])) { if (debug > 0) { fprintf(stderr, "invalid control header (%02x%02x != %02x%02x) dsize=%d!\n", sub[i + 3], sub[i + 4], sub[dsize + 2], sub[dsize + 3], dsize); } i = size; break; } i += 5; break; default: if (debug > 4) fprintf(stderr, "\tcmd(%5d): 0x%x\n", i, c); if (debug > 0) fprintf(stderr, "invalid sequence in control header (%02x)!\n", c); return -1; } /* end switch command */ } /* end while i < size */ have_bits = FALSE; x = y = 0; io = 0; s->img = malloc( s->xd*s->yd); if( ofs<0 && yyd ) { fprintf(stderr,"WARN: No image data supplied for this subtitle\n"); } else { while ((ofs < dsize) && (y < s->yd)) { i = get_next_bits(); if (i < 4) { i = (i << 4) + get_next_bits(); if (i < 16) { i = (i << 4) + get_next_bits(); if (i < 0x40) { i = (i << 4) + get_next_bits(); if (i < 4) { i=i+(s->xd-x)*4; } } } } c = i & 3; i = i >> 2; while (i--) { s->img[io++] = c; if (++x == s->xd) { y += 2; x = 0; if ((y >= s->yd) && !(y & 1)) { y = 1; io = s->xd; ofs = ofs1; } else io += s->xd; have_bits = FALSE; } } } } if (s->pts[0] == -1) return 0; s->pts[0] += add_offset; if( s->pts[1] != -1 ) s->pts[1] += add_offset; addspu(s); return 0; } /* end fuction dvd_decode */ /* * from Y -> R * from V -> G * from U -> B */ static void ycrcb_to_rgb(int *Y, int *Cr, int *Cb) { int R, G, B; R = YCrCb2R(*Y,*Cr,*Cb); G = YCrCb2G(*Y,*Cr,*Cb); B = YCrCb2B(*Y,*Cr,*Cb); *Y = R; *Cr = G; *Cb = B; } static void absorb_palette(struct dispdetails *d) { int i; for( i=0; inumpal; i++ ) { int Y,Cr,Cb; Y=(d->palette[i]>>16)&255; Cr=(d->palette[i]>>8)&255; Cb=(d->palette[i])&255; bpal[i].r=YCrCb2R(Y,Cr,Cb); bpal[i].g=YCrCb2G(Y,Cr,Cb); bpal[i].b=YCrCb2B(Y,Cr,Cb); } } static void pluck_dd() { struct dispdetails *d=dd; int i; dd=d->next; absorb_palette(d); for( i=0; inumbuttons; i++ ) { free(d->buttons[i].name); free(d->buttons[i].up); free(d->buttons[i].down); free(d->buttons[i].left); free(d->buttons[i].right); } free(d->buttons); free(d); } static unsigned char cmap_find(int x,int y,struct colormap *map,int nummap,int ci) { int i; unsigned char cix=0; for( i=0; i=map[i].x1 && y>=map[i].y1 && x<=map[i].x2 && y<=map[i].y2 ) cix=(((map[i].contrast>>(ci*4))&15)<<4) | (((map[i].color>>(ci*4))&15)); return cix; } static int write_png(char *file_name,struct spu *s,struct colormap *map,int nummap) { unsigned int a, x, y, nonzero; unsigned char *out_buf, *temp; FILE *fp; png_structp png_ptr; png_infop info_ptr; temp = out_buf = malloc(s->xd * s->yd * 4); nonzero=0; for (y = 0; y < s->yd; y++) { for (x = 0; x < s->xd; x++) { unsigned char cix=cmap_find(x+s->x0,y+s->y0,map,nummap,s->img[y * s->xd + x]); *temp++ = bpal[cix&15].r; *temp++ = bpal[cix&15].g; *temp++ = bpal[cix&15].b; *temp++ = (cix>>4)*17; if( cix&0xf0 ) nonzero=1; } } if( !nonzero ) { free(out_buf); return 1; } fp = fopen(file_name, "wb"); if (!fp) { fprintf(stderr, "error, unable to open/create file: %s\n", file_name); return -1; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return -1; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp) NULL); return -1; } if (setjmp(png_ptr->jmpbuf)) { png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return -1; } png_init_io(png_ptr, fp); /* turn on or off filtering, and/or choose specific filters */ png_set_filter(png_ptr, 0, PNG_FILTER_NONE); /* set the zlib compression level */ png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); /* set other zlib parameters */ png_set_compression_mem_level(png_ptr, 8); png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); if (full_size) { png_set_IHDR(png_ptr, info_ptr, 720, 576, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } else { png_set_IHDR(png_ptr, info_ptr, s->xd, s->yd, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } png_write_info(png_ptr, info_ptr); png_set_packing(png_ptr); if (out_buf != NULL) { png_byte *row_pointers[576]; if (full_size) { char *image; temp = out_buf; image = malloc(720 * 576 * 4); memset(image, 0, 720 * 576 * 4); // fill image full transparrent // insert image on the correct position for (y = s->y0; y < s->y0 + s->yd; y++) { char *to = &image[y * 720 * 4 + s->x0 * 4]; for (x = 0; x < s->xd; x++) { *to++ = *temp++; *to++ = *temp++; *to++ = *temp++; *to++ = *temp++; } } s->y0 = 0; s->x0 = 0; s->yd = 576; s->xd = 720; free(out_buf); out_buf = image; } for (a = 0; a < s->yd; a++) { row_pointers[a] = out_buf + a * (s->xd * 4); } png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); free(out_buf); } png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return 0; } static void write_pts(char *preamble,int pts) { fprintf(fdo, " %s=\"%02d:%02d:%02d.%02d\"",preamble, (pts / (60 * 60 * 90000)) % 24, (pts / (60 * 90000)) % 60, (pts / 90000) % 60, (pts / 900) % 100); } /* copy the content of buf to expbuf converting '&' '<' '>' '"' expbuf must be big enough to contain the expanded buffer */ static void xml_buf(unsigned char *expbuf,unsigned char *buf) { unsigned char *p; do { switch (*buf) { case '&': p="&"; break; case '<': p="<"; break; case '>': p=">"; break; case '"': p="""; break; default: p=NULL; break; } if( p ) { while ( (*expbuf++ = *p++) ) ; --expbuf; } else *expbuf++ = *buf; } while ( *buf++ ) ; } static void write_menu_image(struct spu *s,struct dispdetails *d,char *type,int offset) { unsigned char nbuf[256]; unsigned char ebuf[256*6]; int nummap=d->numbuttons+1, i; struct colormap *map=malloc(sizeof(struct colormap)*nummap); memset(map,0,sizeof(struct colormap)); // set the first one blank map[0].x2=0x7fffffff; map[0].y2=0x7fffffff; for( i=0; inumbuttons; i++ ) { uint32_t cc=d->coli[2*d->buttons[i].grp-2+offset]; map[i+1].x1=d->buttons[i].x1; map[i+1].y1=d->buttons[i].y1; map[i+1].x2=d->buttons[i].x2; map[i+1].y2=d->buttons[i].y2; map[i+1].color=cc>>16; map[i+1].contrast=cc; } sprintf(nbuf, "%s%05d%c.png", base_name, s->subno, type[0]); if( !write_png(nbuf,s,map,nummap) ) { xml_buf(ebuf,nbuf); fprintf(fdo," %s=\"%s\"",type,ebuf); } free(map); } static void write_spu(struct spu *s,struct dispdetails *d) { unsigned char nbuf[256]; unsigned char ebuf[256*6]; int i; if( d ) absorb_palette(d); fprintf(fdo,"\t\tsubno); if( !write_png(nbuf,s,s->map,s->nummap) ) { xml_buf(ebuf,nbuf); fprintf(fdo," image=\"%s\"",ebuf); } if( d && d->numbuttons ) { write_menu_image(s,d,"highlight",0); write_menu_image(s,d,"select",1); } write_pts("start",s->pts[0]); if( s->pts[1] != -1 ) write_pts("end",s->pts[1]); if( s->x0 || s->y0 ) fprintf(fdo," xoffset=\"%d\" yoffset=\"%d\"",s->x0,s->y0); if (s->force_display) fprintf(fdo, " force=\"yes\""); if( d && d->numbuttons ) { fprintf(fdo,">\n"); for( i=0; inumbuttons; i++ ) { struct button *b=d->buttons+i; if( b->autoaction ) fprintf(fdo,"\t\t\t\n",b->name); else { fprintf(fdo,"\t\t\t