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);
424 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
425 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
431 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
432 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
442 swf_SetU32(tag, 0); //placeholder
443 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
444 for (y=0;y<height;y++)
445 { U8 scanline[3*width];
447 for (x=0;x<width;x++)
448 { scanline[p++] = bitmap[width*y+x].r;
449 scanline[p++] = bitmap[width*y+x].g;
450 scanline[p++] = bitmap[width*y+x].b;
452 swf_SetJPEGBitsLine(jpeg,scanline);
454 swf_SetJPEGBitsFinish(jpeg);
455 PUT32(&tag->data[pos], tag->len - pos - 4);
457 data=malloc(OUTBUFFER_SIZE);
458 memset(&zs,0x00,sizeof(z_stream));
460 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
461 fprintf(stderr, "rfxswf: zlib compression failed");
466 zs.avail_out = OUTBUFFER_SIZE;
468 for (y=0;y<height;y++)
469 { U8 scanline[width];
471 for (x=0;x<width;x++) {
472 scanline[p++] = bitmap[width*y+x].a;
475 zs.next_in = scanline;
478 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
479 fprintf(stderr, "rfxswf: zlib compression failed");
482 if(zs.next_out != data) {
483 swf_SetBlock(tag, data, zs.next_out - data);
485 zs.avail_out = OUTBUFFER_SIZE;
494 int ret = deflate(&zs, Z_FINISH);
496 ret != Z_STREAM_END) {
497 fprintf(stderr, "rfxswf: zlib compression failed");
500 if(zs.next_out != data) {
501 swf_SetBlock(tag, data, zs.next_out - data);
503 zs.avail_out = OUTBUFFER_SIZE;
505 if (ret == Z_STREAM_END) {
516 #undef OUTBUFFER_SIZE