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*)rfx_alloc(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);
56 rfx_free(dmgr->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 *)rfx_calloc(sizeof(JPEGDESTMGR));
68 jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
70 jpeg_create_compress(&jpeg->cinfo);
72 jpeg->mgr.init_destination = RFXSWF_init_destination;
73 jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer;
74 jpeg->mgr.term_destination = RFXSWF_term_destination;
78 jpeg->cinfo.dest = (struct jpeg_destination_mgr *)jpeg;
82 jpeg->cinfo.image_width = width;
83 jpeg->cinfo.image_height = height;
84 jpeg->cinfo.input_components = 3;
85 jpeg->cinfo.in_color_space = JCS_RGB;
87 jpeg_set_defaults(&jpeg->cinfo);
88 jpeg_set_quality(&jpeg->cinfo,quality,TRUE);
90 // write tables to SWF
92 jpeg_write_tables(&jpeg->cinfo);
94 // compess image to SWF
96 jpeg_suppress_tables(&jpeg->cinfo, TRUE);
97 jpeg_start_compress(&jpeg->cinfo, FALSE);
99 return (JPEGBITS *)jpeg;
102 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits,U8 ** data,int n)
103 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
104 if (!jpeg) return -1;
105 jpeg_write_scanlines(&jpeg->cinfo,data,n);
109 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data)
110 { return swf_SetJPEGBitsLines(jpegbits,&data,1);
113 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
114 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
115 if (!jpeg) return -1;
116 jpeg_finish_compress(&jpeg->cinfo);
121 void swf_SetJPEGBits2(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
125 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
126 for (y=0;y<height;y++)
127 { U8 scanline[3*width];
129 for (x=0;x<width;x++)
130 { scanline[p++] = bitmap[width*y+x].r;
131 scanline[p++] = bitmap[width*y+x].g;
132 scanline[p++] = bitmap[width*y+x].b;
134 swf_SetJPEGBitsLine(jpeg,scanline);
136 swf_SetJPEGBitsFinish(jpeg);
139 void swf_GetJPEGSize(char * fname, int*width, int*height)
140 { struct jpeg_decompress_struct cinfo;
141 struct jpeg_error_mgr jerr;
145 cinfo.err = jpeg_std_error(&jerr);
146 jpeg_create_decompress(&cinfo);
147 if ((fi=fopen(fname,"rb"))==NULL) {
148 fprintf(stderr, "rfxswf: file open error\n");
151 jpeg_stdio_src(&cinfo, fi);
152 jpeg_read_header(&cinfo, TRUE);
153 *width = cinfo.image_width;
154 *height = cinfo.image_height;
155 jpeg_destroy_decompress(&cinfo);
159 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
160 { struct jpeg_decompress_struct cinfo;
161 struct jpeg_error_mgr jerr;
166 cinfo.err = jpeg_std_error(&jerr);
167 jpeg_create_decompress(&cinfo);
169 if ((f=fopen(fname,"rb"))==NULL) {
170 fprintf(stderr, "rfxswf: file open error\n");
174 jpeg_stdio_src(&cinfo,f);
175 jpeg_read_header(&cinfo, TRUE);
176 jpeg_start_decompress(&cinfo);
178 out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
179 scanline = (U8*)rfx_alloc(4*cinfo.output_width);
184 if(cinfo.out_color_space == JCS_GRAYSCALE) {
185 for (y=0;y<cinfo.output_height;y++)
187 jpeg_read_scanlines(&cinfo,&js,1);
188 for(x=cinfo.output_width-1;x>=0;x--) {
189 js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
191 swf_SetJPEGBitsLines(out,(U8**)&js,1);
194 else if(cinfo.out_color_space == JCS_RGB)
196 for (y=0;y<cinfo.output_height;y++)
197 { jpeg_read_scanlines(&cinfo,&js,1);
198 swf_SetJPEGBitsLines(out,(U8**)&js,1);
201 else if(cinfo.out_color_space == JCS_YCCK)
204 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
207 else if(cinfo.out_color_space == JCS_YCbCr)
209 for (y=0;y<cinfo.output_height;y++) {
211 for(x=0;x<cinfo.output_width;x++) {
215 js[x*3+0] = y + ((360*(v-128))>>8);
216 js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8);
217 js[x*3+2] = y + ((455 * (u-128))>>8);
221 else if(cinfo.out_color_space == JCS_CMYK)
223 for (y=0;y<cinfo.output_height;y++)
225 jpeg_read_scanlines(&cinfo,&js,1);
226 /* This routine seems to work for now-
227 It's a mixture of 3 different
228 CMYK->RGB conversion routines I found in the
229 web. (which all produced garbage)
230 I'm happily accepting suggestions. (mk)*/
231 for(x=0;x<cinfo.output_width;x++) {
232 int white = 255 - js[x*4+3];
233 js[x*3+0] = white - ((js[x*4]*white)>>8);
234 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
235 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
237 swf_SetJPEGBitsLines(out,(U8**)&js,1);
242 swf_SetJPEGBitsFinish(out);
243 jpeg_finish_decompress(&cinfo);
249 /* jpeg_source_mgr functions */
250 static void tag_init_source(struct jpeg_decompress_struct *cinfo)
252 TAG*tag = (TAG*)cinfo->client_data;
253 swf_SetTagPos(tag, 2);
254 cinfo->src->bytes_in_buffer = 0;
256 static boolean tag_fill_input_buffer(struct jpeg_decompress_struct *cinfo)
258 TAG*tag = (TAG*)cinfo->client_data;
259 if(tag->data[tag->pos+0] == 0xff &&
260 tag->data[tag->pos+1] == 0xd9 &&
261 tag->data[tag->pos+2] == 0xff &&
262 tag->data[tag->pos+3] == 0xd8) {
265 if(tag->pos >= tag->len) {
266 cinfo->src->next_input_byte = 0;
267 cinfo->src->bytes_in_buffer = 0;
270 cinfo->src->next_input_byte = &tag->data[tag->pos];
271 cinfo->src->bytes_in_buffer = 1;//tag->len - tag->pos;
275 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
277 TAG*tag = (TAG*)cinfo->client_data;
278 cinfo->src->next_input_byte = 0;
279 cinfo->src->bytes_in_buffer = 0;
282 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
284 return jpeg_resync_to_restart(cinfo, desired);
286 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
288 TAG*tag = (TAG*)cinfo->client_data;
290 RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height)
292 struct jpeg_decompress_struct cinfo;
293 struct jpeg_error_mgr jerr;
294 struct jpeg_source_mgr mgr;
299 if(tag->id == ST_DEFINEBITSJPEG) {
300 fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported");
303 if(tag->id == ST_DEFINEBITSJPEG3) {
304 fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not yet supported");
308 cinfo.err = jpeg_std_error(&jerr);
309 jpeg_create_decompress(&cinfo);
311 cinfo.client_data = (void*)tag;
313 cinfo.src->init_source = tag_init_source;
314 cinfo.src->fill_input_buffer = tag_fill_input_buffer;
315 cinfo.src->skip_input_data = tag_skip_input_data;
316 cinfo.src->resync_to_restart = jpeg_resync_to_restart;
317 cinfo.src->term_source = tag_term_source;
318 cinfo.out_color_space = JCS_RGB;
320 jpeg_read_header(&cinfo, TRUE);
321 *width = cinfo.image_width;
322 *height = cinfo.image_height;
323 dest = rfx_alloc(sizeof(RGBA)*cinfo.image_width*cinfo.image_height);
325 jpeg_start_decompress(&cinfo);
327 for (y=0;y<cinfo.output_height;y++) {
328 RGBA* line = &dest[y*cinfo.image_width];
331 jpeg_read_scanlines(&cinfo,&to,1);
332 for(x=cinfo.output_width-1;x>=0;--x) {
343 jpeg_finish_decompress(&cinfo);
345 jpeg_destroy_decompress(&cinfo);
350 #endif // HAVE_JPEGLIB
352 // Lossless compression texture based on zlib
356 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
358 U8*data=rfx_alloc(OUTBUFFER_SIZE);
360 zs->avail_out = OUTBUFFER_SIZE;
362 { int status = deflate(zs,Z_NO_FLUSH);
367 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
373 if (zs->next_out!=data)
374 { swf_SetBlock(t,data,zs->next_out - data);
376 zs->avail_out = OUTBUFFER_SIZE;
389 int status = deflate(zs,Z_FINISH);
390 if (status!=Z_OK && status!=Z_STREAM_END)
393 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
399 if (zs->next_out!=data)
401 swf_SetBlock(t,data,zs->next_out - data);
403 zs->avail_out = OUTBUFFER_SIZE;
406 if(status == Z_STREAM_END)
414 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
418 switch (bitmap_flags)
420 return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
422 bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
428 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
432 swf_SetU8(t,bitmap_flags);
434 swf_SetU16(t,height);
438 memset(&zs,0x00,sizeof(z_stream));
442 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
443 { zs.avail_in = bps*height;
446 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
449 } else res = -3; // zlib error
454 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
455 { RGBA * pal = palette;
456 int bps = BYTES_PER_SCANLINE(width);
459 if (!pal) // create default palette for grayscale images
461 pal = rfx_alloc(256*sizeof(RGBA));
462 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
466 if ((ncolors<2)||(ncolors>256)||(!t)) {
467 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
468 return -1; // parameter error
471 swf_SetU8(t,BMF_8BIT);
473 swf_SetU16(t,height);
474 swf_SetU8(t,ncolors-1); // number of pal entries
478 memset(&zs,0x00,sizeof(z_stream));
482 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
483 { U8 * zpal; // compress palette
484 if ((zpal = rfx_alloc(ncolors*4)))
488 /* be careful with ST_DEFINEBITSLOSSLESS2, because
489 the Flash player produces great bugs if you use too many
490 alpha colors in your palette. The only sensible result that
491 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
492 make transparent parts in sprites. That's the cause why alpha
493 handling is implemented in lossless routines of rfxswf.
495 Indeed: I haven't understood yet how flash player handles
496 alpha values different from 0 and 0xff in lossless bitmaps...
499 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
500 { for (i=0;i<ncolors;i++)
507 zs.avail_in = 4*ncolors;
510 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
516 zs.avail_in = 3*ncolors;
521 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
525 zs.avail_in = (bps*height*sizeof(U8));
527 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
532 } else res = -2; // memory error
533 } else res = -3; // zlib error
536 if (!palette) rfx_free(pal);
541 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
542 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
545 RGBA* swf_DefineLosslessBitsTagToImage(TAG*tag, int*dwidth, int*dheight)
547 int id,format,height, width, pos;
548 U32 datalen, datalen2;
553 char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
558 if(tag->id != ST_DEFINEBITSLOSSLESS &&
559 tag->id != ST_DEFINEBITSLOSSLESS2) {
560 fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",GET16(tag->data));
563 swf_SetTagPos(tag, 0);
565 format = swf_GetU8(tag);
566 if(format == 3) bpp = 8;
567 if(format == 4) bpp = 16;
568 if(format == 5) bpp = 32;
569 if(format!=3 && format!=5) {
571 fprintf(stderr, "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",id);
573 fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", format, id);
576 *dwidth = width = swf_GetU16(tag);
577 *dheight = height = swf_GetU16(tag);
579 dest = rfx_alloc(sizeof(RGBA)*width*height);
581 if(format == 3) cols = swf_GetU8(tag) + 1;
585 datalen = (width*height*bpp/8+cols*8);
590 data = rfx_alloc(datalen);
591 error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
592 } while(error == Z_BUF_ERROR);
594 fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
600 palette = (RGBA*)rfx_alloc(cols*sizeof(RGBA));
601 for(t=0;t<cols;t++) {
602 palette[t].r = data[pos++];
603 palette[t].g = data[pos++];
604 palette[t].b = data[pos++];
606 palette[t].a = data[pos++];
611 for(y=0;y<height;y++) {
612 int srcwidth = width * (bpp/8);
615 // 32 bit to 24 bit "conversion"
616 for(x=0;x<width;x++) {
617 dest[pos2].r=data[pos+1];
618 dest[pos2].g=data[pos+2];
619 dest[pos2].b=data[pos+3];
622 pos+=4; //ignore padding byte
625 for(x=0;x<width;x++) {
626 dest[pos2].r=data[pos+1];
627 dest[pos2].g=data[pos+2];
628 dest[pos2].b=data[pos+3];
629 dest[pos2].a=data[pos+0]; //alpha
635 for(x=0;x<srcwidth;x++) {
636 dest[pos2]=palette[data[pos++]];
640 pos+=((srcwidth+3)&~3)-srcwidth; //align
650 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
651 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
661 swf_SetU32(tag, 0); //placeholder
662 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
663 for (y=0;y<height;y++)
664 { U8 scanline[3*width];
666 for (x=0;x<width;x++)
667 { scanline[p++] = bitmap[width*y+x].r;
668 scanline[p++] = bitmap[width*y+x].g;
669 scanline[p++] = bitmap[width*y+x].b;
671 swf_SetJPEGBitsLine(jpeg,scanline);
673 swf_SetJPEGBitsFinish(jpeg);
674 PUT32(&tag->data[pos], tag->len - pos - 4);
676 data=rfx_alloc(OUTBUFFER_SIZE);
677 memset(&zs,0x00,sizeof(z_stream));
679 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
680 fprintf(stderr, "rfxswf: zlib compression failed");
685 zs.avail_out = OUTBUFFER_SIZE;
687 for (y=0;y<height;y++)
688 { U8 scanline[width];
690 for (x=0;x<width;x++) {
691 scanline[p++] = bitmap[width*y+x].a;
694 zs.next_in = scanline;
697 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
698 fprintf(stderr, "rfxswf: zlib compression failed");
701 if(zs.next_out != data) {
702 swf_SetBlock(tag, data, zs.next_out - data);
704 zs.avail_out = OUTBUFFER_SIZE;
713 int ret = deflate(&zs, Z_FINISH);
715 ret != Z_STREAM_END) {
716 fprintf(stderr, "rfxswf: zlib compression failed");
719 if(zs.next_out != data) {
720 swf_SetBlock(tag, data, zs.next_out - data);
722 zs.avail_out = OUTBUFFER_SIZE;
724 if (ret == Z_STREAM_END) {
735 RGBA* swf_ExtractImage(TAG*tag, int*dwidth, int*dheight)
738 if(tag->id == ST_DEFINEBITSJPEG ||
739 tag->id == ST_DEFINEBITSJPEG2 ||
740 tag->id == ST_DEFINEBITSJPEG3) {
742 return swf_JPEG2TagToImage(tag, dwidth, dheight);
744 fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
748 if(tag->id == ST_DEFINEBITSLOSSLESS ||
749 tag->id == ST_DEFINEBITSLOSSLESS2) {
751 return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight);
753 fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
757 fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id, swf_TagGetName(tag));
761 #undef OUTBUFFER_SIZE