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
355 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
356 { RGBA * pal = palette;
357 int bps = BYTES_PER_SCANLINE(width);
360 if (!pal) // create default palette for grayscale images
362 pal = malloc(256*sizeof(RGBA));
363 for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
367 if ((ncolors<2)||(ncolors>256)||(!t)) {
368 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
369 return -1; // parameter error
372 swf_SetU8(t,BMF_8BIT);
374 swf_SetU16(t,height);
375 swf_SetU8(t,ncolors-1); // number of pal entries
379 memset(&zs,0x00,sizeof(z_stream));
383 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
384 { U8 * zpal; // compress palette
385 if ((zpal = malloc(ncolors*4)))
389 /* be careful with ST_DEFINEBITSLOSSLESS2, because
390 the Flash player produces great bugs if you use too many
391 alpha colors in your palette. The only sensible result that
392 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
393 make transparent parts in sprites. That's the cause why alpha
394 handling is implemented in lossless routines of rfxswf.
396 Indeed: I haven't understood yet how flash player handles
397 alpha values different from 0 and 0xff in lossless bitmaps...
400 if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2) // have alpha channel?
401 { for (i=0;i<ncolors;i++)
408 zs.avail_in = 4*ncolors;
411 { for (i=0;i<ncolors;i++) // pack RGBA structures to RGB
417 zs.avail_in = 3*ncolors;
422 if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
426 zs.avail_in = (bps*height*sizeof(U8));
428 if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
433 } else res = -2; // memory error
434 } else res = -3; // zlib error
437 if (!palette) free(pal);
442 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
443 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
449 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
450 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
460 swf_SetU32(tag, 0); //placeholder
461 jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
462 for (y=0;y<height;y++)
463 { U8 scanline[3*width];
465 for (x=0;x<width;x++)
466 { scanline[p++] = bitmap[width*y+x].r;
467 scanline[p++] = bitmap[width*y+x].g;
468 scanline[p++] = bitmap[width*y+x].b;
470 swf_SetJPEGBitsLine(jpeg,scanline);
472 swf_SetJPEGBitsFinish(jpeg);
473 PUT32(&tag->data[pos], tag->len - pos - 4);
475 data=malloc(OUTBUFFER_SIZE);
476 memset(&zs,0x00,sizeof(z_stream));
478 if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
479 fprintf(stderr, "rfxswf: zlib compression failed");
484 zs.avail_out = OUTBUFFER_SIZE;
486 for (y=0;y<height;y++)
487 { U8 scanline[width];
489 for (x=0;x<width;x++) {
490 scanline[p++] = bitmap[width*y+x].a;
493 zs.next_in = scanline;
496 if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
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;
512 int ret = deflate(&zs, Z_FINISH);
514 ret != Z_STREAM_END) {
515 fprintf(stderr, "rfxswf: zlib compression failed");
518 if(zs.next_out != data) {
519 swf_SetBlock(tag, data, zs.next_out - data);
521 zs.avail_out = OUTBUFFER_SIZE;
523 if (ret == Z_STREAM_END) {
534 #undef OUTBUFFER_SIZE