3 Bitmap functions (needs libjpeg)
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #define OUTBUFFER_SIZE 0x8000
28 typedef struct _JPEGDESTMGR
29 { struct jpeg_destination_mgr mgr;
32 struct jpeg_compress_struct cinfo;
33 struct jpeg_error_mgr jerr;
34 } JPEGDESTMGR, * LPJPEGDESTMGR;
36 // Destination manager callbacks
38 static void RFXSWF_init_destination(j_compress_ptr cinfo)
39 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
40 dmgr->buffer = (JOCTET*)malloc(OUTBUFFER_SIZE);
41 dmgr->mgr.next_output_byte = dmgr->buffer;
42 dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
45 static boolean RFXSWF_empty_output_buffer(j_compress_ptr cinfo)
46 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
47 swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE);
48 dmgr->mgr.next_output_byte = dmgr->buffer;
49 dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
53 static void RFXSWF_term_destination(j_compress_ptr cinfo)
54 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
55 swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE-dmgr->mgr.free_in_buffer);
57 dmgr->mgr.free_in_buffer = 0;
60 JPEGBITS * swf_SetJPEGBitsStart(TAG * t,int width,int height,int quality)
64 // redirect compression lib output to local SWF Tag structure
66 jpeg = (JPEGDESTMGR *)malloc(sizeof(JPEGDESTMGR));
67 if (!jpeg) return NULL;
69 memset(jpeg,0x00,sizeof(JPEGDESTMGR));
70 jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
72 jpeg_create_compress(&jpeg->cinfo);
74 jpeg->mgr.init_destination = RFXSWF_init_destination;
75 jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer;
76 jpeg->mgr.term_destination = RFXSWF_term_destination;
80 jpeg->cinfo.dest = (struct jpeg_destination_mgr *)jpeg;
84 jpeg->cinfo.image_width = width;
85 jpeg->cinfo.image_height = height;
86 jpeg->cinfo.input_components = 3;
87 jpeg->cinfo.in_color_space = JCS_RGB;
89 jpeg_set_defaults(&jpeg->cinfo);
90 jpeg_set_quality(&jpeg->cinfo,quality,TRUE);
92 // write tables to SWF
94 jpeg_write_tables(&jpeg->cinfo);
96 // compess image to SWF
98 jpeg_suppress_tables(&jpeg->cinfo, TRUE);
99 jpeg_start_compress(&jpeg->cinfo, FALSE);
101 return (JPEGBITS *)jpeg;
104 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits,U8 ** data,int n)
105 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
106 if (!jpeg) return -1;
107 jpeg_write_scanlines(&jpeg->cinfo,data,n);
111 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data)
112 { return swf_SetJPEGBitsLines(jpegbits,&data,1);
115 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
116 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
117 if (!jpeg) return -1;
118 jpeg_finish_compress(&jpeg->cinfo);
123 void swf_SetJPEGBits2(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
127 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
128 for (y=0;y<height;y++)
129 { U8 scanline[3*width];
131 for (x=0;x<width;x++)
132 { scanline[p++] = bitmap[width*y+x].r;
133 scanline[p++] = bitmap[width*y+x].g;
134 scanline[p++] = bitmap[width*y+x].b;
136 swf_SetJPEGBitsLine(jpeg,scanline);
138 swf_SetJPEGBitsFinish(jpeg);
141 void swf_GetJPEGSize(char * fname, int*width, int*height)
142 { struct jpeg_decompress_struct cinfo;
143 struct jpeg_error_mgr jerr;
147 cinfo.err = jpeg_std_error(&jerr);
148 jpeg_create_decompress(&cinfo);
149 if ((fi=fopen(fname,"rb"))==NULL) {
150 fprintf(stderr, "rfxswf: file open error\n");
153 jpeg_stdio_src(&cinfo, fi);
154 jpeg_read_header(&cinfo, TRUE);
155 *width = cinfo.image_width;
156 *height = cinfo.image_height;
157 jpeg_destroy_decompress(&cinfo);
161 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
162 { struct jpeg_decompress_struct cinfo;
163 struct jpeg_error_mgr jerr;
168 cinfo.err = jpeg_std_error(&jerr);
169 jpeg_create_decompress(&cinfo);
171 if ((f=fopen(fname,"rb"))==NULL) {
172 fprintf(stderr, "rfxswf: file open error\n");
176 jpeg_stdio_src(&cinfo,f);
177 jpeg_read_header(&cinfo, TRUE);
178 jpeg_start_decompress(&cinfo);
180 out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
181 scanline = (U8*)malloc(4*cinfo.output_width);
186 if(cinfo.out_color_space == JCS_GRAYSCALE) {
187 for (y=0;y<cinfo.output_height;y++)
189 jpeg_read_scanlines(&cinfo,&js,1);
190 for(x=cinfo.output_width-1;x>=0;x--) {
191 js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
193 swf_SetJPEGBitsLines(out,(U8**)&js,1);
196 else if(cinfo.out_color_space == JCS_RGB)
198 for (y=0;y<cinfo.output_height;y++)
199 { jpeg_read_scanlines(&cinfo,&js,1);
200 swf_SetJPEGBitsLines(out,(U8**)&js,1);
203 else if(cinfo.out_color_space == JCS_YCCK)
206 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
209 else if(cinfo.out_color_space == JCS_YCbCr)
211 for (y=0;y<cinfo.output_height;y++) {
213 for(x=0;x<cinfo.output_width;x++) {
217 js[x*3+0] = y + ((360*(v-128))>>8);
218 js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8);
219 js[x*3+2] = y + ((455 * (u-128))>>8);
223 else if(cinfo.out_color_space == JCS_CMYK)
225 for (y=0;y<cinfo.output_height;y++)
227 jpeg_read_scanlines(&cinfo,&js,1);
228 /* This routine seems to work for now-
229 It's a mixture of 3 different
230 CMYK->RGB conversion routines I found in the
231 web. (which all produced garbage)
232 I'm happily accepting suggestions. (mk)*/
233 for(x=0;x<cinfo.output_width;x++) {
234 int white = 255 - js[x*4+3];
235 js[x*3+0] = white - ((js[x*4]*white)>>8);
236 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
237 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
239 swf_SetJPEGBitsLines(out,(U8**)&js,1);
244 swf_SetJPEGBitsFinish(out);
245 jpeg_finish_decompress(&cinfo);
251 /* jpeg_source_mgr functions */
252 static void tag_init_source(struct jpeg_decompress_struct *cinfo)
254 TAG*tag = (TAG*)cinfo->client_data;
255 swf_SetTagPos(tag, 2);
256 cinfo->src->bytes_in_buffer = 0;
258 static boolean tag_fill_input_buffer(struct jpeg_decompress_struct *cinfo)
260 TAG*tag = (TAG*)cinfo->client_data;
261 if(tag->data[tag->pos+0] == 0xff &&
262 tag->data[tag->pos+1] == 0xd9 &&
263 tag->data[tag->pos+2] == 0xff &&
264 tag->data[tag->pos+3] == 0xd8) {
268 if(tag->pos >= tag->len) {
269 cinfo->src->next_input_byte = 0;
270 cinfo->src->bytes_in_buffer = 0;
273 cinfo->src->next_input_byte = &tag->data[tag->pos];
274 cinfo->src->bytes_in_buffer = 1;//tag->len - tag->pos;
278 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
280 TAG*tag = (TAG*)cinfo->client_data;
281 cinfo->src->next_input_byte = 0;
282 cinfo->src->bytes_in_buffer = 0;
285 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
287 return jpeg_resync_to_restart(cinfo, desired);
289 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
291 TAG*tag = (TAG*)cinfo->client_data;
293 RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height)
295 struct jpeg_decompress_struct cinfo;
296 struct jpeg_error_mgr jerr;
297 struct jpeg_source_mgr mgr;
302 if(tag->id == ST_DEFINEBITSJPEG) {
303 fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported");
306 if(tag->id == ST_DEFINEBITSJPEG3) {
307 fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not yet supported");
311 cinfo.err = jpeg_std_error(&jerr);
312 jpeg_create_decompress(&cinfo);
314 cinfo.client_data = (void*)tag;
316 cinfo.src->init_source = tag_init_source;
317 cinfo.src->fill_input_buffer = tag_fill_input_buffer;
318 cinfo.src->skip_input_data = tag_skip_input_data;
319 cinfo.src->resync_to_restart = jpeg_resync_to_restart;
320 cinfo.src->term_source = tag_term_source;
321 cinfo.out_color_space = JCS_RGB;
323 jpeg_read_header(&cinfo, TRUE);
324 *width = cinfo.image_width;
325 *height = cinfo.image_height;
326 dest = malloc(sizeof(RGBA)*cinfo.image_width*cinfo.image_height);
328 jpeg_start_decompress(&cinfo);
330 for (y=0;y<cinfo.output_height;y++) {
331 RGBA* line = &dest[y*cinfo.image_width];
334 jpeg_read_scanlines(&cinfo,&to,1);
335 for(x=cinfo.output_width-1;x>=0;--x) {
346 jpeg_finish_decompress(&cinfo);
348 jpeg_destroy_decompress(&cinfo);
353 #endif // HAVE_JPEGLIB
355 // Lossless compression texture based on zlib
359 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
361 U8*data=malloc(OUTBUFFER_SIZE);
363 zs->avail_out = OUTBUFFER_SIZE;
365 { int status = deflate(zs,Z_NO_FLUSH);
370 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
376 if (zs->next_out!=data)
377 { swf_SetBlock(t,data,zs->next_out - data);
379 zs->avail_out = OUTBUFFER_SIZE;
392 int status = deflate(zs,Z_FINISH);
393 if (status!=Z_OK && status!=Z_STREAM_END)
396 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
402 if (zs->next_out!=data)
404 swf_SetBlock(t,data,zs->next_out - data);
406 zs->avail_out = OUTBUFFER_SIZE;
409 if(status == Z_STREAM_END)
417 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
421 switch (bitmap_flags)
423 return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
425 bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
431 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
435 swf_SetU8(t,bitmap_flags);
437 swf_SetU16(t,height);
441 memset(&zs,0x00,sizeof(z_stream));
445 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
446 { zs.avail_in = bps*height;
449 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
452 } else res = -3; // zlib error
457 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
458 { RGBA * pal = palette;
459 int bps = BYTES_PER_SCANLINE(width);
462 if (!pal) // create default palette for grayscale images
464 pal = malloc(256*sizeof(RGBA));
465 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
469 if ((ncolors<2)||(ncolors>256)||(!t)) {
470 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
471 return -1; // parameter error
474 swf_SetU8(t,BMF_8BIT);
476 swf_SetU16(t,height);
477 swf_SetU8(t,ncolors-1); // number of pal entries
481 memset(&zs,0x00,sizeof(z_stream));
485 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
486 { U8 * zpal; // compress palette
487 if ((zpal = malloc(ncolors*4)))
491 /* be careful with ST_DEFINEBITSLOSSLESS2, because
492 the Flash player produces great bugs if you use too many
493 alpha colors in your palette. The only sensible result that
494 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
495 make transparent parts in sprites. That's the cause why alpha
496 handling is implemented in lossless routines of rfxswf.
498 Indeed: I haven't understood yet how flash player handles
499 alpha values different from 0 and 0xff in lossless bitmaps...
502 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
503 { for (i=0;i<ncolors;i++)
510 zs.avail_in = 4*ncolors;
513 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
519 zs.avail_in = 3*ncolors;
524 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
528 zs.avail_in = (bps*height*sizeof(U8));
530 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
535 } else res = -2; // memory error
536 } else res = -3; // zlib error
539 if (!palette) free(pal);
544 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
545 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
548 RGBA* swf_DefineLosslessBitsTagToImage(TAG*tag, int*dwidth, int*dheight)
550 int id,format,height, width, pos;
551 U32 datalen, datalen2;
556 char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
561 if(tag->id != ST_DEFINEBITSLOSSLESS &&
562 tag->id != ST_DEFINEBITSLOSSLESS2) {
563 fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",GET16(tag->data));
566 swf_SetTagPos(tag, 0);
568 format = swf_GetU8(tag);
569 if(format == 3) bpp = 8;
570 if(format == 4) bpp = 16;
571 if(format == 5) bpp = 32;
572 if(format!=3 && format!=5) {
574 fprintf(stderr, "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",id);
576 fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", format, id);
579 *dwidth = width = swf_GetU16(tag);
580 *dheight = height = swf_GetU16(tag);
582 dest = malloc(sizeof(RGBA)*width*height);
584 if(format == 3) cols = swf_GetU8(tag) + 1;
588 datalen = (width*height*bpp/8+cols*8);
593 data = malloc(datalen);
594 error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
595 } while(error == Z_BUF_ERROR);
597 fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
602 palette = (RGBA*)malloc(cols*sizeof(RGBA));
603 for(t=0;t<cols;t++) {
604 palette[t].r = data[pos++];
605 palette[t].g = data[pos++];
606 palette[t].b = data[pos++];
608 palette[t].a = data[pos++];
612 for(y=0;y<height;y++) {
613 int srcwidth = width * (bpp/8);
616 // 32 bit to 24 bit "conversion"
617 for(x=0;x<width;x++) {
618 dest[pos2].r=data[pos+1];
619 dest[pos2].g=data[pos+2];
620 dest[pos2].b=data[pos+3];
623 pos+=4; //ignore padding byte
626 for(x=0;x<width;x++) {
627 dest[pos2].r=data[pos+1];
628 dest[pos2].g=data[pos+2];
629 dest[pos2].b=data[pos+3];
630 dest[pos2].a=data[pos+0]; //alpha
636 for(x=0;x<srcwidth;x++) {
637 dest[pos2]=palette[data[pos++]];
641 pos+=((srcwidth+3)&~3)-srcwidth; //align
651 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
652 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
662 swf_SetU32(tag, 0); //placeholder
663 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
664 for (y=0;y<height;y++)
665 { U8 scanline[3*width];
667 for (x=0;x<width;x++)
668 { scanline[p++] = bitmap[width*y+x].r;
669 scanline[p++] = bitmap[width*y+x].g;
670 scanline[p++] = bitmap[width*y+x].b;
672 swf_SetJPEGBitsLine(jpeg,scanline);
674 swf_SetJPEGBitsFinish(jpeg);
675 PUT32(&tag->data[pos], tag->len - pos - 4);
677 data=malloc(OUTBUFFER_SIZE);
678 memset(&zs,0x00,sizeof(z_stream));
680 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
681 fprintf(stderr, "rfxswf: zlib compression failed");
686 zs.avail_out = OUTBUFFER_SIZE;
688 for (y=0;y<height;y++)
689 { U8 scanline[width];
691 for (x=0;x<width;x++) {
692 scanline[p++] = bitmap[width*y+x].a;
695 zs.next_in = scanline;
698 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
699 fprintf(stderr, "rfxswf: zlib compression failed");
702 if(zs.next_out != data) {
703 swf_SetBlock(tag, data, zs.next_out - data);
705 zs.avail_out = OUTBUFFER_SIZE;
714 int ret = deflate(&zs, Z_FINISH);
716 ret != Z_STREAM_END) {
717 fprintf(stderr, "rfxswf: zlib compression failed");
720 if(zs.next_out != data) {
721 swf_SetBlock(tag, data, zs.next_out - data);
723 zs.avail_out = OUTBUFFER_SIZE;
725 if (ret == Z_STREAM_END) {
736 RGBA* swf_ExtractImage(TAG*tag, int*dwidth, int*dheight)
739 if(tag->id == ST_DEFINEBITSJPEG ||
740 tag->id == ST_DEFINEBITSJPEG2 ||
741 tag->id == ST_DEFINEBITSJPEG3) {
743 return swf_JPEG2TagToImage(tag, dwidth, dheight);
745 fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
749 if(tag->id == ST_DEFINEBITSLOSSLESS ||
750 tag->id == ST_DEFINEBITSLOSSLESS2) {
752 return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight);
754 fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
758 fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id, swf_TagGetName(tag));
762 #undef OUTBUFFER_SIZE