X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fmodules%2Fswfbits.c;h=84a3b5065d5314687c5131db55b96d54f4f8f8ef;hp=a687ffb6def3154330e2d4b80a142a404f0e4568;hb=9b328ffcb7522c4c0d021a1a8c68e95390e1b2a7;hpb=ba50f044fa8cb1504465a9795cd7865cba934e60 diff --git a/lib/modules/swfbits.c b/lib/modules/swfbits.c index a687ffb..84a3b50 100644 --- a/lib/modules/swfbits.c +++ b/lib/modules/swfbits.c @@ -21,263 +21,542 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include +#include +#include +#include "../../config.h" +#ifdef HAVE_ZLIB +#include +#include +#endif +#include +#include + +#ifdef HAVE_JPEGLIB +#define HAVE_BOOLEAN +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif +#endif // HAVE_JPEGLIB + +#include "../rfxswf.h" + #define OUTBUFFER_SIZE 0x8000 +int swf_ImageHasAlpha(RGBA*img, int width, int height) +{ + int len = width*height; + int t; + int hasalpha=0; + for(t=0;t= 4 && img[t].a < 0xfc) + return 2; + if(img[t].a < 4) + hasalpha=1; + } + return hasalpha; +} + +/*int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette) +{ + int len = width*height; + int t; + int palsize = 0; + RGBA pal[512]; + U32*pal32=(U32*)pal; + int palette_overflow = 0; + U32 lastcol32 = 0; + + if(sizeof(RGBA)!=sizeof(U32)) + fprintf(stderr, "rfxswf: sizeof(RGBA)!=sizeof(U32))"); + + lastcol32 = pal32[palsize++] = *(U32*)&img[0]; + + for(t=1;t> 17) ^ col32; + hash ^= ((hash>>8) + 1) ^ hash; + hash &= 255; + + csize = size[hash]; + cpal = &pal[hash*256]; + for(i=0;idest; - dmgr->buffer = (JOCTET*)malloc(OUTBUFFER_SIZE); - dmgr->mgr.next_output_byte = dmgr->buffer; - dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE; +static void RFXSWF_init_destination(j_compress_ptr cinfo) +{ + JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest; + dmgr->buffer = (JOCTET *) rfx_alloc(OUTBUFFER_SIZE); + dmgr->mgr.next_output_byte = dmgr->buffer; + dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE; } static boolean RFXSWF_empty_output_buffer(j_compress_ptr cinfo) -{ JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest; - swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE); - dmgr->mgr.next_output_byte = dmgr->buffer; - dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE; - return TRUE; +{ + JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest; + swf_SetBlock(dmgr->t, (U8 *) dmgr->buffer, OUTBUFFER_SIZE); + dmgr->mgr.next_output_byte = dmgr->buffer; + dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE; + return TRUE; } -static void RFXSWF_term_destination(j_compress_ptr cinfo) -{ JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest; - swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE-dmgr->mgr.free_in_buffer); - free(dmgr->buffer); - dmgr->mgr.free_in_buffer = 0; +static void RFXSWF_term_destination(j_compress_ptr cinfo) +{ + JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest; + swf_SetBlock(dmgr->t, (U8 *) dmgr->buffer, + OUTBUFFER_SIZE - dmgr->mgr.free_in_buffer); + rfx_free(dmgr->buffer); + dmgr->mgr.free_in_buffer = 0; } -JPEGBITS * swf_SetJPEGBitsStart(TAG * t,int width,int height,int quality) +JPEGBITS *swf_SetJPEGBitsStart(TAG * t, int width, int height, int quality) { - JPEGDESTMGR * jpeg; - - // redirect compression lib output to local SWF Tag structure - - jpeg = (JPEGDESTMGR *)malloc(sizeof(JPEGDESTMGR)); - if (!jpeg) return NULL; - - memset(jpeg,0x00,sizeof(JPEGDESTMGR)); - jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr); + JPEGDESTMGR *jpeg; + + // redirect compression lib output to local SWF Tag structure + + jpeg = (JPEGDESTMGR *) rfx_calloc(sizeof(JPEGDESTMGR)); + + jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr); + + jpeg_create_compress(&jpeg->cinfo); + + jpeg->mgr.init_destination = RFXSWF_init_destination; + jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer; + jpeg->mgr.term_destination = RFXSWF_term_destination; + + jpeg->t = t; - jpeg_create_compress(&jpeg->cinfo); + jpeg->cinfo.dest = (struct jpeg_destination_mgr *) jpeg; - jpeg->mgr.init_destination = RFXSWF_init_destination; - jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer; - jpeg->mgr.term_destination = RFXSWF_term_destination; - - jpeg->t = t; + // init compression - jpeg->cinfo.dest = (struct jpeg_destination_mgr *)jpeg; + jpeg->cinfo.image_width = width; + jpeg->cinfo.image_height = height; + jpeg->cinfo.input_components = 3; + jpeg->cinfo.in_color_space = JCS_RGB; - // init compression - - jpeg->cinfo.image_width = width; - jpeg->cinfo.image_height = height; - jpeg->cinfo.input_components = 3; - jpeg->cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&jpeg->cinfo); + jpeg_set_quality(&jpeg->cinfo, quality, TRUE); - jpeg_set_defaults(&jpeg->cinfo); - jpeg_set_quality(&jpeg->cinfo,quality,TRUE); + // write tables to SWF - // write tables to SWF - - jpeg_write_tables(&jpeg->cinfo); + jpeg_write_tables(&jpeg->cinfo); - // compess image to SWF - - jpeg_suppress_tables(&jpeg->cinfo, TRUE); - jpeg_start_compress(&jpeg->cinfo, FALSE); + // compess image to SWF - return (JPEGBITS *)jpeg; + jpeg_suppress_tables(&jpeg->cinfo, TRUE); + jpeg_start_compress(&jpeg->cinfo, FALSE); + + return (JPEGBITS *) jpeg; } -int swf_SetJPEGBitsLines(JPEGBITS * jpegbits,U8 ** data,int n) -{ JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits; - if (!jpeg) return -1; - jpeg_write_scanlines(&jpeg->cinfo,data,n); - return 0; +int swf_SetJPEGBitsLines(JPEGBITS * jpegbits, U8 ** data, int n) +{ + JPEGDESTMGR *jpeg = (JPEGDESTMGR *) jpegbits; + if (!jpeg) + return -1; + jpeg_write_scanlines(&jpeg->cinfo, data, n); + return 0; } -int swf_SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data) -{ return swf_SetJPEGBitsLines(jpegbits,&data,1); +int swf_SetJPEGBitsLine(JPEGBITS * jpegbits, U8 * data) +{ + return swf_SetJPEGBitsLines(jpegbits, &data, 1); } int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits) -{ JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits; - if (!jpeg) return -1; - jpeg_finish_compress(&jpeg->cinfo); - free(jpeg); - return 0; -} - -void swf_SetJPEGBits2(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality) -{ - JPEGBITS* jpeg; - int y; - jpeg = swf_SetJPEGBitsStart(tag,width,height,quality); - for (y=0;y=0;x--) { - js[x*3] = js[x*3+1] = js[x*3+2] = js[x]; - } - swf_SetJPEGBitsLines(out,(U8**)&js,1); +{ + JPEGDESTMGR *jpeg = (JPEGDESTMGR *) jpegbits; + if (!jpeg) + return -1; + jpeg_finish_compress(&jpeg->cinfo); + jpeg_destroy_compress(&jpeg->cinfo); + rfx_free(jpeg); + return 0; +} + +#if defined(HAVE_JPEGLIB) +void swf_SetJPEGBits2(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality) +{ + JPEGBITS *jpeg; + int y; + jpeg = swf_SetJPEGBitsStart(tag, width, height, quality); + U8 *scanline = (U8*)rfx_alloc(3 * width); + for (y = 0; y < height; y++) { + int x, p = 0; + for (x = 0; x < width; x++) { + scanline[p++] = bitmap[width * y + x].r; + scanline[p++] = bitmap[width * y + x].g; + scanline[p++] = bitmap[width * y + x].b; } + swf_SetJPEGBitsLine(jpeg, scanline); } - else if(cinfo.out_color_space == JCS_RGB) - { - for (y=0;y>8); - js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8); - js[x*3+2] = y + ((455 * (u-128))>>8); - } + + jpeg_stdio_src(&cinfo, f); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + out = + swf_SetJPEGBitsStart(t, cinfo.output_width, cinfo.output_height, + quality); + scanline = (U8 *) rfx_alloc(4 * cinfo.output_width); + + if (scanline) { + int y; + U8 *js = scanline; + if (cinfo.out_color_space == JCS_GRAYSCALE) { + for (y = 0; y < cinfo.output_height; y++) { + int x; + jpeg_read_scanlines(&cinfo, &js, 1); + for (x = cinfo.output_width - 1; x >= 0; x--) { + js[x * 3] = js[x * 3 + 1] = js[x * 3 + 2] = js[x]; + } + swf_SetJPEGBitsLines(out, (U8 **) & js, 1); + } + } else if (cinfo.out_color_space == JCS_RGB) { + for (y = 0; y < cinfo.output_height; y++) { + jpeg_read_scanlines(&cinfo, &js, 1); + swf_SetJPEGBitsLines(out, (U8 **) & js, 1); + } + } else if (cinfo.out_color_space == JCS_YCCK) { + //FIXME + fprintf(stderr, "Error: Can't convert YCCK to RGB.\n"); + return -1; + } else if (cinfo.out_color_space == JCS_YCbCr) { + for (y = 0; y < cinfo.output_height; y++) { + int x; + for (x = 0; x < cinfo.output_width; x++) { + int y = js[x * 3 + 0]; + int u = js[x * 3 + 1]; + int v = js[x * 3 + 1]; + js[x * 3 + 0] = y + ((360 * (v - 128)) >> 8); + js[x * 3 + 1] = + y - ((88 * (u - 128) + 183 * (v - 128)) >> 8); + js[x * 3 + 2] = y + ((455 * (u - 128)) >> 8); + } + } + } else if (cinfo.out_color_space == JCS_CMYK) { + for (y = 0; y < cinfo.output_height; y++) { + int x; + jpeg_read_scanlines(&cinfo, &js, 1); + /* This routine seems to work for now- + It's a mixture of 3 different + CMYK->RGB conversion routines I found in the + web. (which all produced garbage) + I'm happily accepting suggestions. (mk) */ + for (x = 0; x < cinfo.output_width; x++) { + int white = 255 - js[x * 4 + 3]; + js[x * 3 + 0] = white - ((js[x * 4] * white) >> 8); + js[x * 3 + 1] = white - ((js[x * 4 + 1] * white) >> 8); + js[x * 3 + 2] = white - ((js[x * 4 + 2] * white) >> 8); + } + swf_SetJPEGBitsLines(out, (U8 **) & js, 1); + } } } - else if(cinfo.out_color_space == JCS_CMYK) - { - for (y=0;yRGB conversion routines I found in the - web. (which all produced garbage) - I'm happily accepting suggestions. (mk)*/ - for(x=0;x>8); - js[x*3+1] = white - ((js[x*4+1]*white)>>8); - js[x*3+2] = white - ((js[x*4+2]*white)>>8); - } - swf_SetJPEGBitsLines(out,(U8**)&js,1); + + rfx_free(scanline); + swf_SetJPEGBitsFinish(out); + jpeg_finish_decompress(&cinfo); + fclose(f); + + return 0; +} + +typedef struct _JPEGFILEMGR { + struct jpeg_destination_mgr mgr; + JOCTET *buffer; + struct jpeg_compress_struct* cinfo; + struct jpeg_error_mgr* jerr; + FILE*fi; +} JPEGFILEMGR; + +static void file_init_destination(j_compress_ptr cinfo) +{ + JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest); + struct jpeg_destination_mgr*dmgr = &fmgr->mgr; + + fmgr->buffer = (JOCTET*)rfx_alloc(OUTBUFFER_SIZE); + if(!fmgr->buffer) { + perror("malloc"); + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + + dmgr->next_output_byte = fmgr->buffer; + dmgr->free_in_buffer = OUTBUFFER_SIZE; +} + +static boolean file_empty_output_buffer(j_compress_ptr cinfo) +{ + JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest); + struct jpeg_destination_mgr*dmgr = &fmgr->mgr; + + if(fmgr->fi) + fwrite(fmgr->buffer, OUTBUFFER_SIZE, 1, fmgr->fi); + + dmgr->next_output_byte = fmgr->buffer; + dmgr->free_in_buffer = OUTBUFFER_SIZE; + return 1; +} + +static void file_term_destination(j_compress_ptr cinfo) +{ + JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest); + struct jpeg_destination_mgr*dmgr = &fmgr->mgr; + + if(fmgr->fi) + fwrite(fmgr->buffer, OUTBUFFER_SIZE-dmgr->free_in_buffer, 1, fmgr->fi); + + rfx_free(fmgr->buffer); + fmgr->buffer = 0; + dmgr->free_in_buffer = 0; + dmgr->next_output_byte = 0; +} + +void swf_SaveJPEG(char*filename, RGBA*pixels, int width, int height, int quality) +{ + JPEGFILEMGR fmgr; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char*data2 = 0; + int y; + + FILE*fi = fopen(filename, "wb"); + if(!fi) { + char buf[256]; + sprintf(buf, "rfxswf: Couldn't create %s", filename); + perror(buf); + return; + } + data2 = (unsigned char *)rfx_calloc(width*3); + + memset(&cinfo, 0, sizeof(cinfo)); + memset(&jerr, 0, sizeof(jerr)); + memset(&fmgr, 0, sizeof(fmgr)); + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + fmgr.mgr.init_destination = file_init_destination; + fmgr.mgr.empty_output_buffer = file_empty_output_buffer; + fmgr.mgr.term_destination = file_term_destination; + fmgr.fi = fi; + fmgr.cinfo = &cinfo; + fmgr.jerr = &jerr; + cinfo.dest = (struct jpeg_destination_mgr*)&fmgr; + + // init compression + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + cinfo.dct_method = JDCT_IFAST; + jpeg_set_quality(&cinfo,quality,TRUE); + + //jpeg_write_tables(&cinfo); + //jpeg_suppress_tables(&cinfo, TRUE); + jpeg_start_compress(&cinfo, FALSE); + + for(y=0;yclient_data; - swf_SetTagPos(tag, 2); + TAG *tag = (TAG *) cinfo->client_data; + if (tag->id == ST_DEFINEBITSJPEG3) { + swf_SetTagPos(tag, 6); + } else { + swf_SetTagPos(tag, 2); + } cinfo->src->bytes_in_buffer = 0; } static boolean tag_fill_input_buffer(struct jpeg_decompress_struct *cinfo) { - TAG*tag = (TAG*)cinfo->client_data; - if(tag->data[tag->pos+0] == 0xff && - tag->data[tag->pos+1] == 0xd9 && - tag->data[tag->pos+2] == 0xff && - tag->data[tag->pos+3] == 0xd8) { - printf("Skip\n"); - tag->pos += 4; + TAG *tag = (TAG *) cinfo->client_data; + if (tag->pos + 4 <= tag->len && + tag->data[tag->pos + 0] == 0xff && + tag->data[tag->pos + 1] == 0xd9 && + tag->data[tag->pos + 2] == 0xff && + tag->data[tag->pos + 3] == 0xd8) { + tag->pos += 4; } - if(tag->pos >= tag->len) { - cinfo->src->next_input_byte = 0; - cinfo->src->bytes_in_buffer = 0; - return 0; + if (tag->pos >= tag->len) { + cinfo->src->next_input_byte = 0; + cinfo->src->bytes_in_buffer = 0; + return 0; } cinfo->src->next_input_byte = &tag->data[tag->pos]; - cinfo->src->bytes_in_buffer = 1;//tag->len - tag->pos; + cinfo->src->bytes_in_buffer = 1; //tag->len - tag->pos; tag->pos += 1; return 1; } static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count) { - TAG*tag = (TAG*)cinfo->client_data; + TAG *tag = (TAG *) cinfo->client_data; cinfo->src->next_input_byte = 0; cinfo->src->bytes_in_buffer = 0; tag->pos += count; @@ -288,30 +567,39 @@ static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int d } static void tag_term_source(struct jpeg_decompress_struct *cinfo) { - TAG*tag = (TAG*)cinfo->client_data; + TAG *tag = (TAG *) cinfo->client_data; } -RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height) +RGBA *swf_JPEG2TagToImage(TAG * tag, int *width, int *height) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; struct jpeg_source_mgr mgr; - RGBA * dest; + RGBA *dest; + int y; + int offset = 0; + int oldtaglen = 0; *width = 0; *height = 0; - if(tag->id == ST_DEFINEBITSJPEG) { - fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported"); - return 0; + if (tag->id == ST_DEFINEBITSJPEG) { + fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported\n"); + return 0; } - if(tag->id == ST_DEFINEBITSJPEG3) { - fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not yet supported"); - return 0; + if (tag->id == ST_DEFINEBITSJPEG3) { +#ifdef HAVE_ZLIB + offset = swf_GetU32(tag); + oldtaglen = tag->len; + tag->len = offset+6; +#else + fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not possible: no zlib\n"); + return 0; +#endif } cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); + jpeg_create_decompress(&cinfo); - cinfo.client_data = (void*)tag; + cinfo.client_data = (void *) tag; cinfo.src = &mgr; cinfo.src->init_source = tag_init_source; cinfo.src->fill_input_buffer = tag_fill_input_buffer; @@ -323,441 +611,657 @@ RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height) jpeg_read_header(&cinfo, TRUE); *width = cinfo.image_width; *height = cinfo.image_height; - dest = malloc(sizeof(RGBA)*cinfo.image_width*cinfo.image_height); - + dest = (RGBA*) + rfx_alloc(sizeof(RGBA) * cinfo.image_width * cinfo.image_height); + jpeg_start_decompress(&cinfo); - int y; - for (y=0;y=0;--x) { - int b = to[x*3+0]; - int g = to[x*3+1]; - int r = to[x*3+2]; - line[x].r = r; - line[x].g = g; - line[x].b = b; - line[x].a = 255; - } + for (y = 0; y < cinfo.output_height; y++) { + RGBA *line = &dest[y * cinfo.image_width]; + U8 *to = (U8 *) line; + int x; + jpeg_read_scanlines(&cinfo, &to, 1); + for (x = cinfo.output_width - 1; x >= 0; --x) { + int r = to[x * 3 + 0]; + int g = to[x * 3 + 1]; + int b = to[x * 3 + 2]; + line[x].r = r; + line[x].g = g; + line[x].b = b; + line[x].a = 255; + } } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); + +#ifdef HAVE_ZLIB + if(offset) { + uLongf datalen = cinfo.output_width*cinfo.output_height; + U8* alphadata = (U8*)rfx_alloc(datalen); + int error; + tag->len = oldtaglen; + swf_SetTagPos(tag, 6+offset); + error = uncompress(alphadata, &datalen, &tag->data[tag->pos], tag->len - tag->pos); + if (error != Z_OK) { + fprintf(stderr, "rfxswf: Zlib error %d while extracting definejpeg3\n", error); + return 0; + } + for(y=0;ynext_out = data; - zs->avail_out = OUTBUFFER_SIZE; - while (1) - { int status = deflate(zs,Z_NO_FLUSH); +int RFXSWF_deflate_wraper(TAG * t, z_stream * zs, boolean finish) +{ + U8 *data = (U8*)rfx_alloc(OUTBUFFER_SIZE); + zs->next_out = data; + zs->avail_out = OUTBUFFER_SIZE; + while (1) { + int status = deflate(zs, Z_NO_FLUSH); + + if (status != Z_OK) { + fprintf(stderr, "rfxswf: zlib compression error (%i)\n", status); + rfx_free(data); + return status; + } - if (status!=Z_OK) - { -#ifdef DEBUG_RFXSWF - fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status); -#endif - free(data); - return status; - } + if (zs->next_out != data) { + swf_SetBlock(t, data, zs->next_out - data); + zs->next_out = data; + zs->avail_out = OUTBUFFER_SIZE; + } - if (zs->next_out!=data) - { swf_SetBlock(t,data,zs->next_out - data); - zs->next_out = data; - zs->avail_out = OUTBUFFER_SIZE; - } - - if (zs->avail_in==0) - break; - } - - if(!finish) { - free(data); - return 0; - } - - while(1) { - int status = deflate(zs,Z_FINISH); - if (status!=Z_OK && status!=Z_STREAM_END) - { -#ifdef DEBUG_RFXSWF - fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status); -#endif - free(data); - return status; + if (zs->avail_in == 0) + break; } - if (zs->next_out!=data) - { - swf_SetBlock(t,data,zs->next_out - data); - zs->next_out = data; - zs->avail_out = OUTBUFFER_SIZE; + if (!finish) { + rfx_free(data); + return 0; } - if(status == Z_STREAM_END) - break; - } - free(data); - return 0; + while (1) { + int status = deflate(zs, Z_FINISH); + if (status != Z_OK && status != Z_STREAM_END) { + fprintf(stderr, "rfxswf: zlib compression error (%i)\n", status); + rfx_free(data); + return status; + } + + if (zs->next_out != data) { + swf_SetBlock(t, data, zs->next_out - data); + zs->next_out = data; + zs->avail_out = OUTBUFFER_SIZE; + } + + if (status == Z_STREAM_END) + break; + } + rfx_free(data); + return 0; } -int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags) -{ int res = 0; - int bps; - - switch (bitmap_flags) - { case BMF_8BIT: - return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256); +int swf_SetLosslessBits(TAG * t, U16 width, U16 height, void *bitmap, U8 bitmap_flags) +{ + int res = 0; + int bps; + + switch (bitmap_flags) { + case BMF_8BIT: + return swf_SetLosslessBitsIndexed(t, width, height, (U8*)bitmap, NULL, 256); case BMF_16BIT: - bps = BYTES_PER_SCANLINE(sizeof(U16)*width); - break; + bps = BYTES_PER_SCANLINE(sizeof(U16) * width); + break; case BMF_32BIT: - bps = width*4; - break; + bps = width * 4; + break; default: - fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags); - return -1; - } - - swf_SetU8(t,bitmap_flags); - swf_SetU16(t,width); - swf_SetU16(t,height); - - { z_stream zs; - - memset(&zs,0x00,sizeof(z_stream)); - zs.zalloc = Z_NULL; - zs.zfree = Z_NULL; - - if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK) - { zs.avail_in = bps*height; - zs.next_in = bitmap; - - if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3; - deflateEnd(&zs); - - } else res = -3; // zlib error - } - return res; -} - -int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors) -{ RGBA * pal = palette; - int bps = BYTES_PER_SCANLINE(width); - int res = 0; - - if (!pal) // create default palette for grayscale images - { int i; - pal = malloc(256*sizeof(RGBA)); - for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;} - ncolors = 256; - } - - if ((ncolors<2)||(ncolors>256)||(!t)) { - fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors); - return -1; // parameter error - } - - swf_SetU8(t,BMF_8BIT); - swf_SetU16(t,width); - swf_SetU16(t,height); - swf_SetU8(t,ncolors-1); // number of pal entries - - { z_stream zs; - - memset(&zs,0x00,sizeof(z_stream)); - zs.zalloc = Z_NULL; - zs.zfree = Z_NULL; - - if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK) - { U8 * zpal; // compress palette - if ((zpal = malloc(ncolors*4))) - { U8 * pp = zpal; - int i; - - /* be careful with ST_DEFINEBITSLOSSLESS2, because - the Flash player produces great bugs if you use too many - alpha colors in your palette. The only sensible result that - can be archeived is setting one color to r=0,b=0,g=0,a=0 to - make transparent parts in sprites. That's the cause why alpha - handling is implemented in lossless routines of rfxswf. - - Indeed: I haven't understood yet how flash player handles - alpha values different from 0 and 0xff in lossless bitmaps... - */ - - if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel? - { for (i=0;i 256) || (!t)) { + fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", + ncolors); + return -1; // parameter error + } + + swf_SetU8(t, BMF_8BIT); + swf_SetU16(t, width); + swf_SetU16(t, height); + swf_SetU8(t, ncolors - 1); // number of pal entries + + { + z_stream zs; + + memset(&zs, 0x00, sizeof(z_stream)); + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) == Z_OK) { + U8 *zpal; // compress palette + if ((zpal = (U8*)rfx_alloc(ncolors * 4))) { + U8 *pp = zpal; + int i; + + /* be careful with ST_DEFINEBITSLOSSLESS2, because + the Flash player produces great bugs if you use too many + alpha colors in your palette. The only sensible result that + can be archeived is setting one color to r=0,b=0,g=0,a=0 to + make transparent parts in sprites. That's the cause why alpha + handling is implemented in lossless routines of rfxswf. + + Indeed: I haven't understood yet how flash player handles + alpha values different from 0 and 0xff in lossless bitmaps... + */ + + if (swf_GetTagID(t) == ST_DEFINEBITSLOSSLESS2) // have alpha channel? + { + for (i = 0; i < ncolors; i++) { + pp[0] = pal[i].r; + pp[1] = pal[i].g; + pp[2] = pal[i].b; + pp[3] = pal[i].a; + pp += 4; + } + zs.avail_in = 4 * ncolors; + } else { + for (i = 0; i < ncolors; i++) // pack RGBA structures to RGB + { + pp[0] = pal[i].r; + pp[1] = pal[i].g; + pp[2] = pal[i].b; + pp += 3; + } + zs.avail_in = 3 * ncolors; + } + + zs.next_in = zpal; + + if (RFXSWF_deflate_wraper(t, &zs, FALSE) < 0) + res = -3; + + // compress bitmap + zs.next_in = bitmap; + zs.avail_in = (bps * height * sizeof(U8)); + + if (RFXSWF_deflate_wraper(t, &zs, TRUE) < 0) + res = -3; + + deflateEnd(&zs); + + rfx_free(zpal); + } else + res = -2; // memory error + } else + res = -3; // zlib error + } + + if (!palette) + rfx_free(pal); + + return res; +} + +int swf_SetLosslessBitsGrayscale(TAG * t, U16 width, U16 height, U8 * bitmap) +{ + return swf_SetLosslessBitsIndexed(t, width, height, bitmap, NULL, 256); +} + +void swf_PreMultiplyAlpha(RGBA*data, int width, int height) +{ + int num = width*height; + int t; + for(t=0;tid = ST_DEFINEBITSLOSSLESS; + } else { + tag->id = ST_DEFINEBITSLOSSLESS2; + /* FIXME: we're destroying the callers data here */ + swf_PreMultiplyAlpha(data, width, height); + } + num = swf_ImageGetNumberOfPaletteEntries(data, width, height, 0); + if(num>1 && num<=256) { + RGBA*palette = (RGBA*)malloc(sizeof(RGBA)*num); + int width2 = BYTES_PER_SCANLINE(width); + U8*data2 = (U8*)malloc(width2*height); + int len = width*height; + int x,y; + int r; + swf_ImageGetNumberOfPaletteEntries(data, width, height, palette); + for(y=0;yid == ST_DEFINEBITSLOSSLESS2; - int t,x,y; - RGBA*palette; - U8*data,*data2; - RGBA*dest; - if(tag->id != ST_DEFINEBITSLOSSLESS && - tag->id != ST_DEFINEBITSLOSSLESS2) { - fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",GET16(tag->data)); - return 0; + int t, x, y; + RGBA *palette = 0; + U8 *data, *data2; + RGBA *dest; + if (tag->id != ST_DEFINEBITSLOSSLESS && + tag->id != ST_DEFINEBITSLOSSLESS2) { + fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n", + GET16(tag->data)); + return 0; } swf_SetTagPos(tag, 0); - id =swf_GetU16(tag); + id = swf_GetU16(tag); format = swf_GetU8(tag); - if(format == 3) bpp = 8; - if(format == 4) bpp = 16; - if(format == 5) bpp = 32; - if(format!=3 && format!=5) { - if(format==4) - fprintf(stderr, "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",id); - else - fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", format, id); + if (format == 3) + bpp = 8; + if (format == 4) + bpp = 16; + if (format == 5) + bpp = 32; + if (format != 3 && format != 5) { + if (format == 4) + fprintf(stderr, + "rfxswf: Can't handle 16-bit palette images yet (image %d)\n", + id); + else + fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", + format, id); return 0; } *dwidth = width = swf_GetU16(tag); *dheight = height = swf_GetU16(tag); - - dest = malloc(sizeof(RGBA)*width*height); - if(format == 3) cols = swf_GetU8(tag) + 1; - else cols = 0; + dest = (RGBA*)rfx_alloc(sizeof(RGBA) * width * height); + + if (format == 3) + cols = swf_GetU8(tag) + 1; + else + cols = 0; data = 0; - datalen = (width*height*bpp/8+cols*8); + datalen = (width * height * bpp / 8 + cols * 8); do { - if(data) - free(data); - datalen+=4096; - data = malloc(datalen); - error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos); - } while(error == Z_BUF_ERROR); - if(error != Z_OK) { + if (data) + rfx_free(data); + datalen += 4096; + data = (U8*)rfx_alloc(datalen); + error = + uncompress(data, &datalen, &tag->data[tag->pos], + tag->len - tag->pos); + } while (error == Z_BUF_ERROR); + if (error != Z_OK) { fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id); return 0; } pos = 0; - - palette = (RGBA*)malloc(cols*sizeof(RGBA)); - for(t=0;t>16; + dest[pos2].g = (data[pos + 2]*alpha)>>16; + dest[pos2].b = (data[pos + 3]*alpha)>>16; + dest[pos2].a = data[pos + 0]; //alpha + pos2++; + pos += 4; + } + } + } else { + for (x = 0; x < srcwidth; x++) { + dest[pos2] = palette[data[pos++]]; + pos2++; + } + } + pos += ((srcwidth + 3) & ~3) - srcwidth; //align } - if(palette) - free(palette); - free(data); + if (palette) + rfx_free(palette); + rfx_free(data); return dest; } -#endif // HAVE_ZLIB +#endif // HAVE_ZLIB #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB) -int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality) -{ - JPEGBITS* jpeg; - int y; - int pos; - int res = 0; - U8 * data; - z_stream zs; - - pos = tag->len; - swf_SetU32(tag, 0); //placeholder - jpeg = swf_SetJPEGBitsStart(tag,width,height,quality); - for (y=0;ydata[pos], tag->len - pos - 4); - - data=malloc(OUTBUFFER_SIZE); - memset(&zs,0x00,sizeof(z_stream)); - - if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) { - fprintf(stderr, "rfxswf: zlib compression failed"); - return -3; - } - - zs.next_out = data; - zs.avail_out = OUTBUFFER_SIZE; - for (y=0;ylen; + swf_SetU32(tag, 0); //placeholder + jpeg = swf_SetJPEGBitsStart(tag, width, height, quality); + U8 *scanline = (U8*)rfx_alloc(3 * width); + for (y = 0; y < height; y++) { + int x, p = 0; + for (x = 0; x < width; x++) { + //int ia = bitmap[width*y+x].a; + //if(ia) { + // /* remove premultiplication */ + // ia = 0xff0000/ia; + //} + //scanline[p++] = (bitmap[width * y + x].r*ia)>>16; + //scanline[p++] = (bitmap[width * y + x].g*ia)>>16; + //scanline[p++] = (bitmap[width * y + x].b*ia)>>16; + scanline[p++] = bitmap[width * y + x].r; + scanline[p++] = bitmap[width * y + x].g; + scanline[p++] = bitmap[width * y + x].b; + } + swf_SetJPEGBitsLine(jpeg, scanline); + } + rfx_free(scanline); + swf_SetJPEGBitsFinish(jpeg); + PUT32(&tag->data[pos], tag->len - pos - 4); + + data = (U8*)rfx_alloc(OUTBUFFER_SIZE); + memset(&zs, 0x00, sizeof(z_stream)); + + if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) { + fprintf(stderr, "rfxswf: zlib compression failed"); + return -3; + } + + zs.next_out = data; + zs.avail_out = OUTBUFFER_SIZE; + + scanline = (U8*)rfx_alloc(width); + for (y = 0; y < height; y++) { + int x, p = 0; + for (x = 0; x < width; x++) { + scanline[p++] = bitmap[width * y + x].a; + } + zs.avail_in = width; + zs.next_in = scanline; + + while (1) { + if (deflate(&zs, Z_NO_FLUSH) != Z_OK) { + fprintf(stderr, "rfxswf: zlib compression failed"); + return -4; + } + if (zs.next_out != data) { + swf_SetBlock(tag, data, zs.next_out - data); + zs.next_out = data; + zs.avail_out = OUTBUFFER_SIZE; + } + if (!zs.avail_in) { + break; + } + } } - zs.avail_in = width; - zs.next_in = scanline; - while(1) { - if(deflate(&zs, Z_NO_FLUSH) != Z_OK) { + rfx_free(scanline); + + while (1) { + int ret = deflate(&zs, Z_FINISH); + if (ret != Z_OK && ret != Z_STREAM_END) { fprintf(stderr, "rfxswf: zlib compression failed"); - return -4; + return -5; } - if(zs.next_out != data) { + if (zs.next_out != data) { swf_SetBlock(tag, data, zs.next_out - data); zs.next_out = data; zs.avail_out = OUTBUFFER_SIZE; } - if(!zs.avail_in) { + if (ret == Z_STREAM_END) { break; } } - } - - while(1) { - int ret = deflate(&zs, Z_FINISH); - if (ret != Z_OK && - ret != Z_STREAM_END) { - fprintf(stderr, "rfxswf: zlib compression failed"); - return -5; - } - if(zs.next_out != data) { - swf_SetBlock(tag, data, zs.next_out - data); - zs.next_out = data; - zs.avail_out = OUTBUFFER_SIZE; - } - if (ret == Z_STREAM_END) { - break; - } - } - - deflateEnd(&zs); - free(data); - return 0; + + deflateEnd(&zs); + rfx_free(data); + return 0; +} + +#else +int swf_SetJPEGBits3(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality) +{ + fprintf(stderr, "Error: swftools compiled without jpeglib\n"); + return -1; } #endif -RGBA* swf_ExtractImage(TAG*tag, int*dwidth, int*dheight) + +#define NO_LOSSLESS +/* expects mem to be non-premultiplied */ +TAG* swf_AddImage(TAG*tag, int bitid, RGBA*mem, int width, int height, int quality) +{ + TAG *tag1 = 0, *tag2 = 0; + int has_alpha = swf_ImageHasAlpha(mem,width,height); + + /* try lossless image */ + +#ifdef NO_LOSSLESS + tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0); + tag1->len = 0x7fffffff; +#else + tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0); + swf_SetU16(tag1, bitid); + swf_SetLosslessImage(tag1, mem, width, height); +#endif + +#if defined(HAVE_JPEGLIB) + /* try jpeg image. Notice that if (and only if) we tried the lossless compression + above, the data will now be premultiplied with alpha. */ + if(has_alpha) { + tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG3); + swf_SetU16(tag2, bitid); + swf_SetJPEGBits3(tag2, width, height, mem, quality); + } else { + tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG2); + swf_SetU16(tag2, bitid); + swf_SetJPEGBits2(tag2, width, height, mem, quality); + } +#endif + + if(quality>100 || !tag2 || (tag1 && tag1->len < tag2->len)) { + /* use the zlib version- it's smaller */ + tag1->prev = tag; + if(tag) tag->next = tag1; + tag = tag1; + swf_DeleteTag(0, tag2); + } else { + /* use the jpeg version- it's smaller */ + tag2->prev = tag; + if(tag) tag->next = tag2; + tag = tag2; + swf_DeleteTag(0, tag1); + } + return tag; +} + +RGBA *swf_ExtractImage(TAG * tag, int *dwidth, int *dheight) { - RGBA*img; - if(tag->id == ST_DEFINEBITSJPEG || - tag->id == ST_DEFINEBITSJPEG2 || - tag->id == ST_DEFINEBITSJPEG3) { + RGBA *img; + + swf_SetTagPos(tag, 2); // id is 2 bytes + + if (tag->id == ST_DEFINEBITSJPEG || + tag->id == ST_DEFINEBITSJPEG2 || tag->id == ST_DEFINEBITSJPEG3) { #ifdef HAVE_JPEGLIB - return swf_JPEG2TagToImage(tag, dwidth, dheight); + return swf_JPEG2TagToImage(tag, dwidth, dheight); #else - fprintf(stderr, "rfxswf: Error: No JPEG library compiled in"); - return 0; + fprintf(stderr, "rfxswf: Error: No JPEG library compiled in"); + return 0; #endif } - if(tag->id == ST_DEFINEBITSLOSSLESS || - tag->id == ST_DEFINEBITSLOSSLESS2) { + if (tag->id == ST_DEFINEBITSLOSSLESS || + tag->id == ST_DEFINEBITSLOSSLESS2) { #ifdef HAVE_ZLIB - return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight); + return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight); #else - fprintf(stderr, "rfxswf: Error: No JPEG library compiled in"); - return 0; + fprintf(stderr, "rfxswf: Error: No JPEG library compiled in"); + return 0; #endif } - fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id, swf_TagGetName(tag)); + fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id, + swf_TagGetName(tag)); return 0; } #undef OUTBUFFER_SIZE + +void swf_RemoveJPEGTables(SWF * swf) +{ + TAG *tag = swf->firstTag; + TAG *tables_tag = 0; + while (tag) { + if (tag->id == ST_JPEGTABLES) { + tables_tag = tag; + } + tag = tag->next; + } + + if (!tables_tag) + return; + + tag = swf->firstTag; + while (tag) { + if (tag->id == ST_DEFINEBITSJPEG) { + int len = tag->len; + void *data = rfx_alloc(len); + swf_GetBlock(tag, (U8*)data, tag->len); + swf_ResetTag(tag, ST_DEFINEBITSJPEG2); + swf_SetBlock(tag, &((U8*)data)[0], 2); //id + swf_SetBlock(tag, tables_tag->data, tables_tag->len); + swf_SetBlock(tag, &((U8*)data)[2], len-2); + free(data); + } + tag = tag->next; + } + if (swf->firstTag == tables_tag) + swf->firstTag = tables_tag->next; + swf_DeleteTag(swf, tables_tag); +} +