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 file is distributed under the GPL, see file COPYING for details
14 #define OUTBUFFER_SIZE 0x8000
18 typedef struct _JPEGDESTMGR
19 { struct jpeg_destination_mgr mgr;
22 struct jpeg_compress_struct cinfo;
23 struct jpeg_error_mgr jerr;
24 } JPEGDESTMGR, * LPJPEGDESTMGR;
26 // Destination manager callbacks
28 void RFXSWF_init_destination(j_compress_ptr cinfo)
29 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
30 dmgr->buffer = (JOCTET*)malloc(OUTBUFFER_SIZE);
31 dmgr->mgr.next_output_byte = dmgr->buffer;
32 dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
35 boolean RFXSWF_empty_output_buffer(j_compress_ptr cinfo)
36 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
37 swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE);
38 dmgr->mgr.next_output_byte = dmgr->buffer;
39 dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
43 void RFXSWF_term_destination(j_compress_ptr cinfo)
44 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
45 swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE-dmgr->mgr.free_in_buffer);
47 dmgr->mgr.free_in_buffer = 0;
50 JPEGBITS * swf_SetJPEGBitsStart(TAG * t,int width,int height,int quality)
54 // redirect compression lib output to local SWF Tag structure
56 jpeg = (JPEGDESTMGR *)malloc(sizeof(JPEGDESTMGR));
57 if (!jpeg) return NULL;
59 memset(jpeg,0x00,sizeof(JPEGDESTMGR));
60 jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
62 jpeg_create_compress(&jpeg->cinfo);
64 jpeg->mgr.init_destination = RFXSWF_init_destination;
65 jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer;
66 jpeg->mgr.term_destination = RFXSWF_term_destination;
70 jpeg->cinfo.dest = (struct jpeg_destination_mgr *)jpeg;
74 jpeg->cinfo.image_width = width;
75 jpeg->cinfo.image_height = height;
76 jpeg->cinfo.input_components = 3;
77 jpeg->cinfo.in_color_space = JCS_RGB;
79 jpeg_set_defaults(&jpeg->cinfo);
80 jpeg_set_quality(&jpeg->cinfo,quality,TRUE);
82 // write tables to SWF
84 jpeg_write_tables(&jpeg->cinfo);
86 // compess image to SWF
88 jpeg_suppress_tables(&jpeg->cinfo, TRUE);
89 jpeg_start_compress(&jpeg->cinfo, FALSE);
91 return (JPEGBITS *)jpeg;
94 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits,U8 ** data,int n)
95 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
97 jpeg_write_scanlines(&jpeg->cinfo,data,n);
101 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data)
102 { return swf_SetJPEGBitsLines(jpegbits,&data,1);
105 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
106 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
107 if (!jpeg) return -1;
108 jpeg_finish_compress(&jpeg->cinfo);
113 void swf_SetJPEGBits2(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
117 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
118 for (y=0;y<height;y++)
119 { U8 scanline[3*width];
121 for (x=0;x<width;x++)
122 { scanline[p++] = bitmap[width*y+x].r;
123 scanline[p++] = bitmap[width*y+x].g;
124 scanline[p++] = bitmap[width*y+x].b;
126 swf_SetJPEGBitsLine(jpeg,scanline);
128 swf_SetJPEGBitsFinish(jpeg);
131 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
132 { struct jpeg_decompress_struct cinfo;
133 struct jpeg_error_mgr jerr;
138 cinfo.err = jpeg_std_error(&jerr);
139 jpeg_create_decompress(&cinfo);
141 if ((f=fopen(fname,"rb"))==NULL) {
142 fprintf(stderr, "rfxswf: file open error\n");
146 jpeg_stdio_src(&cinfo,f);
147 jpeg_read_header(&cinfo, TRUE);
148 jpeg_start_decompress(&cinfo);
150 out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
151 scanline = (U8*)malloc(4*cinfo.output_width);
156 if(cinfo.out_color_space == JCS_GRAYSCALE) {
157 for (y=0;y<cinfo.output_height;y++)
159 jpeg_read_scanlines(&cinfo,&js,1);
160 for(x=cinfo.output_width-1;x>=0;x--) {
161 js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
163 swf_SetJPEGBitsLines(out,(U8**)&js,1);
166 else if(cinfo.out_color_space == JCS_RGB)
168 for (y=0;y<cinfo.output_height;y++)
169 { jpeg_read_scanlines(&cinfo,&js,1);
170 swf_SetJPEGBitsLines(out,(U8**)&js,1);
173 else if(cinfo.out_color_space == JCS_YCCK)
176 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
179 else if(cinfo.out_color_space == JCS_YCbCr)
181 for (y=0;y<cinfo.output_height;y++) {
183 for(x=0;x<cinfo.output_width;x++) {
187 js[x*3+0] = y + ((360*(v-128))>>8);
188 js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8);
189 js[x*3+2] = y + ((455 * (u-128))>>8);
193 else if(cinfo.out_color_space == JCS_CMYK)
195 for (y=0;y<cinfo.output_height;y++)
197 jpeg_read_scanlines(&cinfo,&js,1);
198 /* This routine seems to work for now-
199 It's a mixture of 3 different
200 CMYK->RGB conversion routines I found in the
201 web. (which all produced garbage)
202 I'm happily accepting suggestions. (mk)*/
203 for(x=0;x<cinfo.output_width;x++) {
204 int white = 255 - js[x*4+3];
205 js[x*3+0] = white - ((js[x*4]*white)>>8);
206 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
207 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
209 swf_SetJPEGBitsLines(out,(U8**)&js,1);
214 swf_SetJPEGBitsFinish(out);
215 jpeg_finish_decompress(&cinfo);
221 #endif // HAVE_JPEGLIB
223 // Lossless compression texture based on zlib
227 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
229 U8*data=malloc(OUTBUFFER_SIZE);
231 zs->avail_out = OUTBUFFER_SIZE;
233 { int status = deflate(zs,Z_NO_FLUSH);
238 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
244 if (zs->next_out!=data)
245 { swf_SetBlock(t,data,zs->next_out - data);
247 zs->avail_out = OUTBUFFER_SIZE;
260 int status = deflate(zs,Z_FINISH);
261 if (status!=Z_OK && status!=Z_STREAM_END)
264 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
270 if (zs->next_out!=data)
272 swf_SetBlock(t,data,zs->next_out - data);
274 zs->avail_out = OUTBUFFER_SIZE;
277 if(status == Z_STREAM_END)
285 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
289 switch (bitmap_flags)
291 return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
293 bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
299 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
303 swf_SetU8(t,bitmap_flags);
305 swf_SetU16(t,height);
309 memset(&zs,0x00,sizeof(z_stream));
313 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
314 { zs.avail_in = bps*height;
317 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
320 } else res = -3; // zlib error
323 while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
325 /* Flash players up to MX crash or do strange things if they encounter a
326 DefineLossless Tag with a payload of less than 63 bytes. They also
327 substitute the whole bitmap by a red rectangle.
329 This loop fills up the tag with zeroes so that this doesn't happen.
337 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
338 { RGBA * pal = palette;
339 int bps = BYTES_PER_SCANLINE(width);
342 if (!pal) // create default palette for grayscale images
344 pal = malloc(256*sizeof(RGBA));
345 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
349 if ((ncolors<2)||(ncolors>256)||(!t)) {
350 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
351 return -1; // parameter error
354 swf_SetU8(t,BMF_8BIT);
356 swf_SetU16(t,height);
357 swf_SetU8(t,ncolors-1); // number of pal entries
361 memset(&zs,0x00,sizeof(z_stream));
365 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
366 { U8 * zpal; // compress palette
367 if ((zpal = malloc(ncolors*4)))
371 /* be careful with ST_DEFINEBITSLOSSLESS2, because
372 the Flash player produces great bugs if you use too many
373 alpha colors in your palette. The only sensible result that
374 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
375 make transparent parts in sprites. That's the cause why alpha
376 handling is implemented in lossless routines of rfxswf.
378 Indeed: I haven't understood yet how flash player handles
379 alpha values different from 0 and 0xff in lossless bitmaps...
382 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
383 { for (i=0;i<ncolors;i++)
390 zs.avail_in = 4*ncolors;
393 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
399 zs.avail_in = 3*ncolors;
404 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
408 zs.avail_in = (bps*height*sizeof(U8));
410 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
415 } else res = -2; // memory error
416 } else res = -3; // zlib error
419 if (!palette) free(pal);
421 while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
423 /* Flash players up to MX crash or do strange things if they encounter a
424 DefineLossless(2) Tag with a payload of less than 63 bytes. They also
425 substitute the whole bitmap by a red rectangle.
427 This loop fills up the tag with zeroes so that this doesn't happen.
435 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
436 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
442 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
443 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
453 swf_SetU32(tag, 0); //placeholder
454 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
455 for (y=0;y<height;y++)
456 { U8 scanline[3*width];
458 for (x=0;x<width;x++)
459 { scanline[p++] = bitmap[width*y+x].r;
460 scanline[p++] = bitmap[width*y+x].g;
461 scanline[p++] = bitmap[width*y+x].b;
463 swf_SetJPEGBitsLine(jpeg,scanline);
465 swf_SetJPEGBitsFinish(jpeg);
466 PUT32(&tag->data[pos], tag->len - pos - 4);
468 data=malloc(OUTBUFFER_SIZE);
469 memset(&zs,0x00,sizeof(z_stream));
471 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
472 fprintf(stderr, "rfxswf: zlib compression failed");
477 zs.avail_out = OUTBUFFER_SIZE;
479 for (y=0;y<height;y++)
480 { U8 scanline[width];
482 for (x=0;x<width;x++) {
483 scanline[p++] = bitmap[width*y+x].a;
486 zs.next_in = scanline;
489 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
490 fprintf(stderr, "rfxswf: zlib compression failed");
493 if(zs.next_out != data) {
494 swf_SetBlock(tag, data, zs.next_out - data);
496 zs.avail_out = OUTBUFFER_SIZE;
505 int ret = deflate(&zs, Z_FINISH);
507 ret != Z_STREAM_END) {
508 fprintf(stderr, "rfxswf: zlib compression failed");
511 if(zs.next_out != data) {
512 swf_SetBlock(tag, data, zs.next_out - data);
514 zs.avail_out = OUTBUFFER_SIZE;
516 if (ret == Z_STREAM_END) {
527 #undef OUTBUFFER_SIZE