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 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 #endif // HAVE_JPEGLIB
253 // Lossless compression texture based on zlib
257 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
259 U8*data=malloc(OUTBUFFER_SIZE);
261 zs->avail_out = OUTBUFFER_SIZE;
263 { int status = deflate(zs,Z_NO_FLUSH);
268 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
274 if (zs->next_out!=data)
275 { swf_SetBlock(t,data,zs->next_out - data);
277 zs->avail_out = OUTBUFFER_SIZE;
290 int status = deflate(zs,Z_FINISH);
291 if (status!=Z_OK && status!=Z_STREAM_END)
294 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
300 if (zs->next_out!=data)
302 swf_SetBlock(t,data,zs->next_out - data);
304 zs->avail_out = OUTBUFFER_SIZE;
307 if(status == Z_STREAM_END)
315 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
319 switch (bitmap_flags)
321 return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
323 bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
329 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
333 swf_SetU8(t,bitmap_flags);
335 swf_SetU16(t,height);
339 memset(&zs,0x00,sizeof(z_stream));
343 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
344 { zs.avail_in = bps*height;
347 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
350 } else res = -3; // zlib error
353 while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
355 /* Flash players up to MX crash or do strange things if they encounter a
356 DefineLossless Tag with a payload of less than 63 bytes. They also
357 substitute the whole bitmap by a red rectangle.
359 This loop fills up the tag with zeroes so that this doesn't happen.
367 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
368 { RGBA * pal = palette;
369 int bps = BYTES_PER_SCANLINE(width);
372 if (!pal) // create default palette for grayscale images
374 pal = malloc(256*sizeof(RGBA));
375 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
379 if ((ncolors<2)||(ncolors>256)||(!t)) {
380 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
381 return -1; // parameter error
384 swf_SetU8(t,BMF_8BIT);
386 swf_SetU16(t,height);
387 swf_SetU8(t,ncolors-1); // number of pal entries
391 memset(&zs,0x00,sizeof(z_stream));
395 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
396 { U8 * zpal; // compress palette
397 if ((zpal = malloc(ncolors*4)))
401 /* be careful with ST_DEFINEBITSLOSSLESS2, because
402 the Flash player produces great bugs if you use too many
403 alpha colors in your palette. The only sensible result that
404 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
405 make transparent parts in sprites. That's the cause why alpha
406 handling is implemented in lossless routines of rfxswf.
408 Indeed: I haven't understood yet how flash player handles
409 alpha values different from 0 and 0xff in lossless bitmaps...
412 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
413 { for (i=0;i<ncolors;i++)
420 zs.avail_in = 4*ncolors;
423 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
429 zs.avail_in = 3*ncolors;
434 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
438 zs.avail_in = (bps*height*sizeof(U8));
440 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
445 } else res = -2; // memory error
446 } else res = -3; // zlib error
449 if (!palette) free(pal);
451 while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
453 /* Flash players up to MX crash or do strange things if they encounter a
454 DefineLossless(2) Tag with a payload of less than 63 bytes. They also
455 substitute the whole bitmap by a red rectangle.
457 This loop fills up the tag with zeroes so that this doesn't happen.
465 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
466 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
472 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
473 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
483 swf_SetU32(tag, 0); //placeholder
484 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
485 for (y=0;y<height;y++)
486 { U8 scanline[3*width];
488 for (x=0;x<width;x++)
489 { scanline[p++] = bitmap[width*y+x].r;
490 scanline[p++] = bitmap[width*y+x].g;
491 scanline[p++] = bitmap[width*y+x].b;
493 swf_SetJPEGBitsLine(jpeg,scanline);
495 swf_SetJPEGBitsFinish(jpeg);
496 PUT32(&tag->data[pos], tag->len - pos - 4);
498 data=malloc(OUTBUFFER_SIZE);
499 memset(&zs,0x00,sizeof(z_stream));
501 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
502 fprintf(stderr, "rfxswf: zlib compression failed");
507 zs.avail_out = OUTBUFFER_SIZE;
509 for (y=0;y<height;y++)
510 { U8 scanline[width];
512 for (x=0;x<width;x++) {
513 scanline[p++] = bitmap[width*y+x].a;
516 zs.next_in = scanline;
519 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
520 fprintf(stderr, "rfxswf: zlib compression failed");
523 if(zs.next_out != data) {
524 swf_SetBlock(tag, data, zs.next_out - data);
526 zs.avail_out = OUTBUFFER_SIZE;
535 int ret = deflate(&zs, Z_FINISH);
537 ret != Z_STREAM_END) {
538 fprintf(stderr, "rfxswf: zlib compression failed");
541 if(zs.next_out != data) {
542 swf_SetBlock(tag, data, zs.next_out - data);
544 zs.avail_out = OUTBUFFER_SIZE;
546 if (ret == Z_STREAM_END) {
557 #undef OUTBUFFER_SIZE