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) {
267 if(tag->pos >= tag->len) {
268 cinfo->src->next_input_byte = 0;
269 cinfo->src->bytes_in_buffer = 0;
272 cinfo->src->next_input_byte = &tag->data[tag->pos];
273 cinfo->src->bytes_in_buffer = 1;//tag->len - tag->pos;
277 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
279 TAG*tag = (TAG*)cinfo->client_data;
280 cinfo->src->next_input_byte = 0;
281 cinfo->src->bytes_in_buffer = 0;
284 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
286 return jpeg_resync_to_restart(cinfo, desired);
288 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
290 TAG*tag = (TAG*)cinfo->client_data;
292 RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height)
294 struct jpeg_decompress_struct cinfo;
295 struct jpeg_error_mgr jerr;
296 struct jpeg_source_mgr mgr;
301 if(tag->id == ST_DEFINEBITSJPEG) {
302 fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported");
305 if(tag->id == ST_DEFINEBITSJPEG3) {
306 fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not yet supported");
310 cinfo.err = jpeg_std_error(&jerr);
311 jpeg_create_decompress(&cinfo);
313 cinfo.client_data = (void*)tag;
315 cinfo.src->init_source = tag_init_source;
316 cinfo.src->fill_input_buffer = tag_fill_input_buffer;
317 cinfo.src->skip_input_data = tag_skip_input_data;
318 cinfo.src->resync_to_restart = jpeg_resync_to_restart;
319 cinfo.src->term_source = tag_term_source;
320 cinfo.out_color_space = JCS_RGB;
322 jpeg_read_header(&cinfo, TRUE);
323 *width = cinfo.image_width;
324 *height = cinfo.image_height;
325 dest = malloc(sizeof(RGBA)*cinfo.image_width*cinfo.image_height);
327 jpeg_start_decompress(&cinfo);
329 for (y=0;y<cinfo.output_height;y++) {
330 RGBA* line = &dest[y*cinfo.image_width];
333 jpeg_read_scanlines(&cinfo,&to,1);
334 for(x=cinfo.output_width-1;x>=0;--x) {
345 jpeg_finish_decompress(&cinfo);
347 jpeg_destroy_decompress(&cinfo);
352 #endif // HAVE_JPEGLIB
354 // Lossless compression texture based on zlib
358 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
360 U8*data=malloc(OUTBUFFER_SIZE);
362 zs->avail_out = OUTBUFFER_SIZE;
364 { int status = deflate(zs,Z_NO_FLUSH);
369 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
375 if (zs->next_out!=data)
376 { swf_SetBlock(t,data,zs->next_out - data);
378 zs->avail_out = OUTBUFFER_SIZE;
391 int status = deflate(zs,Z_FINISH);
392 if (status!=Z_OK && status!=Z_STREAM_END)
395 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
401 if (zs->next_out!=data)
403 swf_SetBlock(t,data,zs->next_out - data);
405 zs->avail_out = OUTBUFFER_SIZE;
408 if(status == Z_STREAM_END)
416 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
420 switch (bitmap_flags)
422 return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
424 bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
430 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
434 swf_SetU8(t,bitmap_flags);
436 swf_SetU16(t,height);
440 memset(&zs,0x00,sizeof(z_stream));
444 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
445 { zs.avail_in = bps*height;
448 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
451 } else res = -3; // zlib error
456 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
457 { RGBA * pal = palette;
458 int bps = BYTES_PER_SCANLINE(width);
461 if (!pal) // create default palette for grayscale images
463 pal = malloc(256*sizeof(RGBA));
464 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
468 if ((ncolors<2)||(ncolors>256)||(!t)) {
469 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
470 return -1; // parameter error
473 swf_SetU8(t,BMF_8BIT);
475 swf_SetU16(t,height);
476 swf_SetU8(t,ncolors-1); // number of pal entries
480 memset(&zs,0x00,sizeof(z_stream));
484 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
485 { U8 * zpal; // compress palette
486 if ((zpal = malloc(ncolors*4)))
490 /* be careful with ST_DEFINEBITSLOSSLESS2, because
491 the Flash player produces great bugs if you use too many
492 alpha colors in your palette. The only sensible result that
493 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
494 make transparent parts in sprites. That's the cause why alpha
495 handling is implemented in lossless routines of rfxswf.
497 Indeed: I haven't understood yet how flash player handles
498 alpha values different from 0 and 0xff in lossless bitmaps...
501 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
502 { for (i=0;i<ncolors;i++)
509 zs.avail_in = 4*ncolors;
512 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
518 zs.avail_in = 3*ncolors;
523 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
527 zs.avail_in = (bps*height*sizeof(U8));
529 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
534 } else res = -2; // memory error
535 } else res = -3; // zlib error
538 if (!palette) free(pal);
543 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
544 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
547 RGBA* swf_DefineLosslessBitsTagToImage(TAG*tag, int*dwidth, int*dheight)
549 int id,format,height, width, pos;
550 U32 datalen, datalen2;
555 char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
560 if(tag->id != ST_DEFINEBITSLOSSLESS &&
561 tag->id != ST_DEFINEBITSLOSSLESS2) {
562 fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",GET16(tag->data));
565 swf_SetTagPos(tag, 0);
567 format = swf_GetU8(tag);
568 if(format == 3) bpp = 8;
569 if(format == 4) bpp = 16;
570 if(format == 5) bpp = 32;
571 if(format!=3 && format!=5) {
573 fprintf(stderr, "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",id);
575 fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", format, id);
578 *dwidth = width = swf_GetU16(tag);
579 *dheight = height = swf_GetU16(tag);
581 dest = malloc(sizeof(RGBA)*width*height);
583 if(format == 3) cols = swf_GetU8(tag) + 1;
587 datalen = (width*height*bpp/8+cols*8);
592 data = malloc(datalen);
593 error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
594 } while(error == Z_BUF_ERROR);
596 fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
601 palette = (RGBA*)malloc(cols*sizeof(RGBA));
602 for(t=0;t<cols;t++) {
603 palette[t].r = data[pos++];
604 palette[t].g = data[pos++];
605 palette[t].b = data[pos++];
607 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=malloc(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