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 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 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 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 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
142 { struct jpeg_decompress_struct cinfo;
143 struct jpeg_error_mgr jerr;
148 cinfo.err = jpeg_std_error(&jerr);
149 jpeg_create_decompress(&cinfo);
151 if ((f=fopen(fname,"rb"))==NULL) {
152 fprintf(stderr, "rfxswf: file open error\n");
156 jpeg_stdio_src(&cinfo,f);
157 jpeg_read_header(&cinfo, TRUE);
158 jpeg_start_decompress(&cinfo);
160 out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
161 scanline = (U8*)malloc(4*cinfo.output_width);
166 if(cinfo.out_color_space == JCS_GRAYSCALE) {
167 for (y=0;y<cinfo.output_height;y++)
169 jpeg_read_scanlines(&cinfo,&js,1);
170 for(x=cinfo.output_width-1;x>=0;x--) {
171 js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
173 swf_SetJPEGBitsLines(out,(U8**)&js,1);
176 else if(cinfo.out_color_space == JCS_RGB)
178 for (y=0;y<cinfo.output_height;y++)
179 { jpeg_read_scanlines(&cinfo,&js,1);
180 swf_SetJPEGBitsLines(out,(U8**)&js,1);
183 else if(cinfo.out_color_space == JCS_YCCK)
186 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
189 else if(cinfo.out_color_space == JCS_YCbCr)
191 for (y=0;y<cinfo.output_height;y++) {
193 for(x=0;x<cinfo.output_width;x++) {
197 js[x*3+0] = y + ((360*(v-128))>>8);
198 js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8);
199 js[x*3+2] = y + ((455 * (u-128))>>8);
203 else if(cinfo.out_color_space == JCS_CMYK)
205 for (y=0;y<cinfo.output_height;y++)
207 jpeg_read_scanlines(&cinfo,&js,1);
208 /* This routine seems to work for now-
209 It's a mixture of 3 different
210 CMYK->RGB conversion routines I found in the
211 web. (which all produced garbage)
212 I'm happily accepting suggestions. (mk)*/
213 for(x=0;x<cinfo.output_width;x++) {
214 int white = 255 - js[x*4+3];
215 js[x*3+0] = white - ((js[x*4]*white)>>8);
216 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
217 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
219 swf_SetJPEGBitsLines(out,(U8**)&js,1);
224 swf_SetJPEGBitsFinish(out);
225 jpeg_finish_decompress(&cinfo);
231 #endif // HAVE_JPEGLIB
233 // Lossless compression texture based on zlib
237 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
239 U8*data=malloc(OUTBUFFER_SIZE);
241 zs->avail_out = OUTBUFFER_SIZE;
243 { int status = deflate(zs,Z_NO_FLUSH);
248 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
254 if (zs->next_out!=data)
255 { swf_SetBlock(t,data,zs->next_out - data);
257 zs->avail_out = OUTBUFFER_SIZE;
270 int status = deflate(zs,Z_FINISH);
271 if (status!=Z_OK && status!=Z_STREAM_END)
274 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
280 if (zs->next_out!=data)
282 swf_SetBlock(t,data,zs->next_out - data);
284 zs->avail_out = OUTBUFFER_SIZE;
287 if(status == Z_STREAM_END)
295 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
299 switch (bitmap_flags)
301 return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
303 bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
309 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
313 swf_SetU8(t,bitmap_flags);
315 swf_SetU16(t,height);
319 memset(&zs,0x00,sizeof(z_stream));
323 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
324 { zs.avail_in = bps*height;
327 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
330 } else res = -3; // zlib error
333 while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
335 /* Flash players up to MX crash or do strange things if they encounter a
336 DefineLossless Tag with a payload of less than 63 bytes. They also
337 substitute the whole bitmap by a red rectangle.
339 This loop fills up the tag with zeroes so that this doesn't happen.
347 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
348 { RGBA * pal = palette;
349 int bps = BYTES_PER_SCANLINE(width);
352 if (!pal) // create default palette for grayscale images
354 pal = malloc(256*sizeof(RGBA));
355 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
359 if ((ncolors<2)||(ncolors>256)||(!t)) {
360 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
361 return -1; // parameter error
364 swf_SetU8(t,BMF_8BIT);
366 swf_SetU16(t,height);
367 swf_SetU8(t,ncolors-1); // number of pal entries
371 memset(&zs,0x00,sizeof(z_stream));
375 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
376 { U8 * zpal; // compress palette
377 if ((zpal = malloc(ncolors*4)))
381 /* be careful with ST_DEFINEBITSLOSSLESS2, because
382 the Flash player produces great bugs if you use too many
383 alpha colors in your palette. The only sensible result that
384 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
385 make transparent parts in sprites. That's the cause why alpha
386 handling is implemented in lossless routines of rfxswf.
388 Indeed: I haven't understood yet how flash player handles
389 alpha values different from 0 and 0xff in lossless bitmaps...
392 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
393 { for (i=0;i<ncolors;i++)
400 zs.avail_in = 4*ncolors;
403 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
409 zs.avail_in = 3*ncolors;
414 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
418 zs.avail_in = (bps*height*sizeof(U8));
420 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
425 } else res = -2; // memory error
426 } else res = -3; // zlib error
429 if (!palette) free(pal);
431 while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
433 /* Flash players up to MX crash or do strange things if they encounter a
434 DefineLossless(2) Tag with a payload of less than 63 bytes. They also
435 substitute the whole bitmap by a red rectangle.
437 This loop fills up the tag with zeroes so that this doesn't happen.
445 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
446 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
452 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
453 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
463 swf_SetU32(tag, 0); //placeholder
464 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
465 for (y=0;y<height;y++)
466 { U8 scanline[3*width];
468 for (x=0;x<width;x++)
469 { scanline[p++] = bitmap[width*y+x].r;
470 scanline[p++] = bitmap[width*y+x].g;
471 scanline[p++] = bitmap[width*y+x].b;
473 swf_SetJPEGBitsLine(jpeg,scanline);
475 swf_SetJPEGBitsFinish(jpeg);
476 PUT32(&tag->data[pos], tag->len - pos - 4);
478 data=malloc(OUTBUFFER_SIZE);
479 memset(&zs,0x00,sizeof(z_stream));
481 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
482 fprintf(stderr, "rfxswf: zlib compression failed");
487 zs.avail_out = OUTBUFFER_SIZE;
489 for (y=0;y<height;y++)
490 { U8 scanline[width];
492 for (x=0;x<width;x++) {
493 scanline[p++] = bitmap[width*y+x].a;
496 zs.next_in = scanline;
499 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
500 fprintf(stderr, "rfxswf: zlib compression failed");
503 if(zs.next_out != data) {
504 swf_SetBlock(tag, data, zs.next_out - data);
506 zs.avail_out = OUTBUFFER_SIZE;
515 int ret = deflate(&zs, Z_FINISH);
517 ret != Z_STREAM_END) {
518 fprintf(stderr, "rfxswf: zlib compression failed");
521 if(zs.next_out != data) {
522 swf_SetBlock(tag, data, zs.next_out - data);
524 zs.avail_out = OUTBUFFER_SIZE;
526 if (ret == Z_STREAM_END) {
537 #undef OUTBUFFER_SIZE