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++) {
188 js[x*3+0] = y + ((360*(v-128))>>8);
189 js[x*3+1] = y - ((88*(u-128)-183*(v-128))>>8);
190 js[x*3+2] = y + ((455 * (u-128))>>8);
194 else if(cinfo.out_color_space == JCS_CMYK)
196 for (y=0;y<cinfo.output_height;y++)
198 jpeg_read_scanlines(&cinfo,&js,1);
199 /* This routine seems to work for now-
200 It's a mixture of 3 different
201 CMYK->RGB conversion routines I found in the
202 web. (which all produced garbage)
203 I'm happily accepting suggestions. (mk)*/
204 for(x=0;x<cinfo.output_width;x++) {
205 int white = 255 - js[x*4+3];
206 js[x*3+0] = white - ((js[x*4]*white)>>8);
207 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
208 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
210 swf_SetJPEGBitsLines(out,(U8**)&js,1);
215 swf_SetJPEGBitsFinish(out);
216 jpeg_finish_decompress(&cinfo);
222 #endif // HAVE_JPEGLIB
224 // Lossless compression texture based on zlib
228 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
230 U8*data=malloc(OUTBUFFER_SIZE);
232 zs->avail_out = OUTBUFFER_SIZE;
234 { int status = deflate(zs,Z_NO_FLUSH);
239 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
245 if (zs->next_out!=data)
246 { swf_SetBlock(t,data,zs->next_out - data);
248 zs->avail_out = OUTBUFFER_SIZE;
261 int status = deflate(zs,Z_FINISH);
262 if (status!=Z_OK && status!=Z_STREAM_END)
265 fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
271 if (zs->next_out!=data)
273 swf_SetBlock(t,data,zs->next_out - data);
275 zs->avail_out = OUTBUFFER_SIZE;
278 if(status == Z_STREAM_END)
286 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
290 switch (bitmap_flags)
292 return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
294 bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
300 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
304 swf_SetU8(t,bitmap_flags);
306 swf_SetU16(t,height);
310 memset(&zs,0x00,sizeof(z_stream));
314 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
315 { zs.avail_in = bps*height;
318 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
321 } else res = -3; // zlib error
324 while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
326 /* Flash players up to MX crash or do strange things if they encounter a
327 DefineLossless Tag with a payload of less than 63 bytes. They also
328 substitute the whole bitmap by a red rectangle.
330 This loop fills up the tag with zeroes so that this doesn't happen.
338 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
339 { RGBA * pal = palette;
340 int bps = BYTES_PER_SCANLINE(width);
344 if (!pal) // create default palette for grayscale images
346 pal = malloc(256*sizeof(RGBA));
347 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
351 if ((ncolors<2)||(ncolors>256)||(!t)) {
352 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
353 return -1; // parameter error
356 swf_SetU8(t,BMF_8BIT);
358 swf_SetU16(t,height);
359 swf_SetU8(t,ncolors-1); // number of pal entries
363 memset(&zs,0x00,sizeof(z_stream));
367 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
368 { U8 * zpal; // compress palette
369 if ((zpal = malloc(ncolors*4)))
373 /* be careful with ST_DEFINEBITSLOSSLESS2, because
374 the Flash player produces great bugs if you use too many
375 alpha colors in your palette. The only sensible result that
376 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
377 make transparent parts in sprites. That's the cause why alpha
378 handling is implemented in lossless routines of rfxswf.
380 Indeed: I haven't understood yet how flash player handles
381 alpha values different from 0 and 0xff in lossless bitmaps...
384 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
385 { for (i=0;i<ncolors;i++)
392 zs.avail_in = 4*ncolors;
395 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
401 zs.avail_in = 3*ncolors;
406 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
410 zs.avail_in = (bps*height*sizeof(U8));
412 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
416 if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
419 } else res = -2; // memory error
420 } else res = -3; // zlib error
424 if (!palette) free(pal);
429 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
430 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
436 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
437 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
447 swf_SetU32(tag, 0); //placeholder
448 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
449 for (y=0;y<height;y++)
450 { U8 scanline[3*width];
452 for (x=0;x<width;x++)
453 { scanline[p++] = bitmap[width*y+x].r;
454 scanline[p++] = bitmap[width*y+x].g;
455 scanline[p++] = bitmap[width*y+x].b;
457 swf_SetJPEGBitsLine(jpeg,scanline);
459 swf_SetJPEGBitsFinish(jpeg);
460 PUT32(&tag->data[pos], tag->len - pos - 4);
462 data=malloc(OUTBUFFER_SIZE);
463 memset(&zs,0x00,sizeof(z_stream));
465 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
466 fprintf(stderr, "rfxswf: zlib compression failed");
471 zs.avail_out = OUTBUFFER_SIZE;
473 for (y=0;y<height;y++)
474 { U8 scanline[width];
476 for (x=0;x<width;x++) {
477 scanline[p++] = bitmap[width*y+x].a;
480 zs.next_in = scanline;
483 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
484 fprintf(stderr, "rfxswf: zlib compression failed");
487 if(zs.next_out != data) {
488 swf_SetBlock(tag, data, zs.next_out - data);
490 zs.avail_out = OUTBUFFER_SIZE;
499 int ret = deflate(&zs, Z_FINISH);
501 ret != Z_STREAM_END) {
502 fprintf(stderr, "rfxswf: zlib compression failed");
505 if(zs.next_out != data) {
506 swf_SetBlock(tag, data, zs.next_out - data);
508 zs.avail_out = OUTBUFFER_SIZE;
510 if (ret == Z_STREAM_END) {
521 #undef OUTBUFFER_SIZE