}
return hasalpha;
}
-
-int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette)
+
+int swf_ImageGetNumberOfPaletteEntries2(RGBA*_img, int width, int height)
+{
+ int len = width*height;
+ int t;
+ U32* img = (U32*)_img;
+ U32 color1 = img[0];
+ U32 color2 = 0;
+ for(t=1;t<len;t++) {
+ if(img[t] != color1) {
+ color2 = img[t];
+ break;
+ }
+ }
+ if(t==len)
+ return 1;
+
+ for(;t<len;t++) {
+ if(img[t] != color1 && img[t] != color2) {
+ return width*height;
+ }
+ }
+ return 2;
+}
+
+/*int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette)
{
int len = width*height;
int t;
int palsize = 0;
- RGBA pal[256];
+ RGBA pal[512];
+ U32*pal32=(U32*)pal;
int palette_overflow = 0;
- for(t=0;t<len;t++) {
+ 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<len;t++) {
RGBA col = img[t];
U32 col32 = *(U32*)&img[t];
- int i=0;
+ int i;
+ if(col32==lastcol32)
+ continue;
for(i=0;i<palsize;i++) {
- if(col32 == *(U32*)&pal[i])
+ if(col32 == pal32[i])
break;
}
if(i==palsize) {
- if(palsize==256) {
+ if(palsize==512) {
palette_overflow = 1;
break;
}
- pal[palsize++] = col;
+ pal32[palsize++] = col32;
}
+ lastcol32 = col32;
}
if(palette_overflow)
return width*height;
if(palette)
memcpy(palette, pal, palsize*sizeof(RGBA));
return palsize;
+}*/
+
+int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette)
+{
+ int len = width*height;
+ int t;
+ int palsize = 0;
+ U32* pal;
+ int size[256];
+ int palette_overflow = 0;
+ U32 lastcol32 = 0;
+
+ pal = malloc(65536*sizeof(U32));
+
+ memset(size, 0, sizeof(size));
+
+ if(sizeof(RGBA)!=sizeof(U32))
+ fprintf(stderr, "rfxswf: sizeof(RGBA)!=sizeof(U32))");
+
+ lastcol32 = (*(U32*)&img[0])^0xffffffff; // don't match
+
+ for(t=0;t<len;t++) {
+ RGBA col = img[t];
+ U32 col32 = *(U32*)&img[t];
+ int i;
+ int csize;
+ U32 hash;
+ U32* cpal;
+ if(col32 == lastcol32)
+ continue;
+ hash = (col32 >> 17) ^ col32;
+ hash ^= ((hash>>8) + 1) ^ hash;
+ hash &= 255;
+
+ csize = size[hash];
+ cpal = &pal[hash*256];
+ for(i=0;i<csize;i++) {
+ if(col32 == cpal[i])
+ break;
+ }
+ if(i==csize) {
+ if(palsize==256) {
+ palette_overflow = 1;
+ break;
+ }
+ cpal[size[hash]++] = col32;
+ palsize++;
+ }
+ lastcol32 = col32;
+ }
+ if(palette_overflow) {
+ free(pal);
+ return width*height;
+ }
+ if(palette) {
+ int i = 0;
+ for(t=0;t<256;t++) {
+ int s;
+ int csize = size[t];
+ U32* cpal = &pal[t*256];
+ for(s=0;s<csize;s++)
+ palette[i++] = *(RGBA*)(&cpal[s]);
+ }
+ }
+ free(pal);
+ return palsize;
}
+
+
#ifdef HAVE_JPEGLIB
typedef struct _JPEGDESTMGR {
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 = 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;y<height;y++) {
+ int x;
+ RGBA*src = &pixels[y*width];
+ for(x=0;x<width;x++) {
+ data2[x*3+0] = src[x].r;
+ data2[x*3+1] = src[x].g;
+ data2[x*3+2] = src[x].b;
+ }
+ jpeg_write_scanlines(&cinfo, &data2, 1);
+ }
+ rfx_free(data2);
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ fclose(fi);
+}
+
/* jpeg_source_mgr functions */
static void tag_init_source(struct jpeg_decompress_struct *cinfo)
{
TAG *tag = (TAG *) cinfo->client_data;
- swf_SetTagPos(tag, 2);
+ 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)
struct jpeg_source_mgr mgr;
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");
+ 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");
+#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_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
+
+#ifdef HAVE_ZLIB
+ if(offset) {
+ U32 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;y<cinfo.output_height;y++) {
+ RGBA*line = &dest[y*cinfo.output_width];
+ U8*aline = &alphadata[y*cinfo.output_width];
+ int x;
+ for(x=0;x<cinfo.output_width;x++) {
+ line[x].a = aline[x];
+ }
+ }
+ free(alphadata);
+ }
+#endif
return dest;
}
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;t<num;t++) {
+ data[t].r = ((int)data[t].r*data[t].a)/255;
+ data[t].g = ((int)data[t].g*data[t].a)/255;
+ data[t].b = ((int)data[t].b*data[t].a)/255;
+ }
+}
+
void swf_SetLosslessImage(TAG*tag, RGBA*data, int width, int height)
{
int hasalpha = swf_ImageHasAlpha(data, width, height);
+ int num;
if(!hasalpha) {
tag->id = ST_DEFINEBITSLOSSLESS;
} else {
tag->id = ST_DEFINEBITSLOSSLESS2;
- /* TODO: premultiply alpha? */
+ swf_PreMultiplyAlpha(data, width, height);
}
- int num = swf_ImageGetNumberOfPaletteEntries(data, width, height, 0);
+ num = swf_ImageGetNumberOfPaletteEntries(data, width, height, 0);
if(num>1 && num<=256) {
RGBA*palette = (RGBA*)malloc(sizeof(RGBA)*num);
- swf_ImageGetNumberOfPaletteEntries(data, width, height, palette);
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;y<height;y++) {
RGBA*src = &data[width*y];
U8*dest = &data2[width2*y];
palette[t].b = data[pos++];
if (alpha) {
palette[t].a = data[pos++];
+ } else {
+ palette[t].a = 255;
}
}
}
}
} else {
for (x = 0; x < width; x++) {
- /* TODO: un-premultiply alpha? */
- dest[pos2].r = data[pos + 1];
- dest[pos2].g = data[pos + 2];
- dest[pos2].b = data[pos + 3];
+ /* TODO: is un-premultiplying alpha the right thing to do?
+ dest[pos2].r = data[pos + 1];
+ dest[pos2].g = data[pos + 2];
+ dest[pos2].b = data[pos + 3];*/
+ int alpha = data[pos+0];
+ if(alpha) {
+ dest[pos2].r = ((int)data[pos + 1]*255)/alpha;
+ dest[pos2].g = ((int)data[pos + 2]*255)/alpha;
+ dest[pos2].b = ((int)data[pos + 3]*255)/alpha;
+ } else {
+ dest[pos2].r = data[pos + 1];
+ dest[pos2].g = data[pos + 2];
+ dest[pos2].b = data[pos + 3];
+ }
dest[pos2].a = data[pos + 0]; //alpha
pos2++;
pos += 4;
rfx_free(data);
return 0;
}
+
+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 */
+ tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0);
+ swf_SetU16(tag1, bitid);
+ swf_SetLosslessImage(tag1, mem, width, height);
+
+ /* try jpeg image */
+ 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);
+ }
+
+ if(tag1 && tag1->len < tag2->len) {
+ /* use the zlib version- it's smaller */
+ tag1->prev = tag;
+ if(tag) tag->next = tag1;
+ tag = tag1;
+ swf_DeleteTag(tag2);
+ } else {
+ /* use the jpeg version- it's smaller */
+ tag2->prev = tag;
+ if(tag) tag->next = tag2;
+ tag = tag2;
+ swf_DeleteTag(tag1);
+ }
+ return tag;
+}
+
#endif
RGBA *swf_ExtractImage(TAG * tag, int *dwidth, int *dheight)
{
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
tag = swf->firstTag;
while (tag) {
if (tag->id == ST_DEFINEBITSJPEG) {
- void *data = rfx_alloc(tag->len);
+ int len = tag->len;
+ void *data = rfx_alloc(len);
swf_GetBlock(tag, 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, data, tag->len);
+ swf_SetBlock(tag, &((U8*)data)[2], len-2);
free(data);
}
tag = tag->next;
if(newwidth<=width) {
for(x=0;x<newwidth;x++) {
- lblockx[x] = p_x;
double ex = px + fx;
int fromx = (int)px;
int tox = (int)ex;
int xweight = (int)(rem*256/fx);
int xx;
int w = 0;
+ lblockx[x] = p_x;
if(tox>=width) tox = width-1;
for(xx=fromx;xx<=tox;xx++) {
if(xx==fromx && xx==tox) p_x->weight = 256;
}
} else {
for(x=0;x<newwidth;x++) {
- lblockx[x] = p_x;
int ix1 = (int)px;
int ix2 = ((int)px)+1;
- if(ix2>=width) ix2=width-1;
double r = px-ix1;
+ if(ix2>=width) ix2=width-1;
+ lblockx[x] = p_x;
if(bicubic)
r = -2*r*r*r+3*r*r;
p_x[0].weight = (int)(256*(1-r));
return lblockx;
}
+static void encodeMonochromeImage(RGBA*data, int width, int height, RGBA*colors)
+{
+ int t;
+ int len = width*height;
+
+ U32* img = (U32*)data;
+ U32 color1 = img[0];
+ U32 color2 = 0;
+ for(t=1;t<len;t++) {
+ if(img[t] != color1) {
+ color2 = img[t];
+ break;
+ }
+ }
+ *(U32*)&colors[0] = color1;
+ *(U32*)&colors[1] = color2;
+ for(t=0;t<len;t++) {
+ if(img[t] == color1) {
+ img[t] = 0;
+ } else {
+ img[t] = 0xffffffff;
+ }
+ }
+}
+
+static void decodeMonochromeImage(RGBA*data, int width, int height, RGBA*colors)
+{
+ int t;
+ int len = width*height;
+
+ for(t=0;t<len;t++) {
+ U32 m = data[t].r;
+ data[t].r = (colors[0].r * (255-m) + colors[1].r * m) >> 8;
+ data[t].g = (colors[0].g * (255-m) + colors[1].g * m) >> 8;
+ data[t].b = (colors[0].b * (255-m) + colors[1].b * m) >> 8;
+ data[t].a = (colors[0].a * (255-m) + colors[1].a * m) >> 8;
+ }
+}
+
RGBA* swf_ImageScale(RGBA*data, int width, int height, int newwidth, int newheight)
{
- if(newwidth<2 || newheight<2)
- return 0;
int x,y;
- RGBA* newdata= (RGBA*)malloc(newwidth*newheight*sizeof(RGBA));
+ RGBA* newdata;
scale_lookup_t *p, **lblockx,**lblocky;
- rgba_int_t*tmpline = (rgba_int_t*)malloc(width*sizeof(rgba_int_t));
+ rgba_int_t*tmpline;
+ int monochrome = 0;
+ RGBA monochrome_colors[2];
+
+ if(newwidth<1 || newheight<1)
+ return 0;
+
+ if(swf_ImageGetNumberOfPaletteEntries2(data, width, height) == 2) {
+ monochrome=1;
+ encodeMonochromeImage(data, width, height, monochrome_colors);
+ }
+
+ tmpline = (rgba_int_t*)malloc(width*sizeof(rgba_int_t));
+ newdata = (RGBA*)malloc(newwidth*newheight*sizeof(RGBA));
lblockx = make_scale_lookup(width, newwidth);
lblocky = make_scale_lookup(height, newheight);
/* create lookup table for y */
rgba_int_t*l = tmpline;
- scale_lookup_t*p_y;
+ scale_lookup_t*p_y,*p_x;
memset(tmpline, 0, width*sizeof(rgba_int_t));
for(p_y=lblocky[y];p_y<lblocky[y+1];p_y++) {
RGBA*line = &data[p_y->pos];
}
/* process x direction */
- scale_lookup_t*p_x = lblockx[0];
+ p_x = lblockx[0];
for(x=0;x<newwidth;x++) {
unsigned int r=0,g=0,b=0,a=0;
scale_lookup_t*p_x_to = lblockx[x+1];
destline++;
}
}
+
+ if(monochrome)
+ decodeMonochromeImage(newdata, newwidth, newheight, monochrome_colors);
+
free(tmpline);
free(*lblockx);
free(lblockx);
return newdata;
}
+