some versions of jpeglib don't support color conversion.
[swftools.git] / lib / modules / swfbits.c
1 /* swfbits.c
2
3    Bitmap functions (needs libjpeg) 
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9  
10    This file is distributed under the GPL, see file COPYING for details 
11
12 */
13
14 #define OUTBUFFER_SIZE 0x8000
15
16 #ifdef _JPEGLIB_INCLUDED_
17
18 typedef struct _JPEGDESTMGR
19 { struct jpeg_destination_mgr mgr;
20   TAG *  t;
21   JOCTET * buffer;
22   struct jpeg_compress_struct cinfo;
23   struct jpeg_error_mgr jerr;
24 } JPEGDESTMGR, * LPJPEGDESTMGR;
25
26 // Destination manager callbacks
27
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;
33 }
34
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;
40   return TRUE;
41 }
42
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);
46   free(dmgr->buffer);
47   dmgr->mgr.free_in_buffer = 0;
48 }
49
50 JPEGBITS * swf_SetJPEGBitsStart(TAG * t,int width,int height,int quality)
51 {
52   JPEGDESTMGR * jpeg;
53         
54   // redirect compression lib output to local SWF Tag structure
55   
56   jpeg = (JPEGDESTMGR *)malloc(sizeof(JPEGDESTMGR));
57   if (!jpeg) return NULL;
58   
59   memset(jpeg,0x00,sizeof(JPEGDESTMGR));
60   jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
61
62   jpeg_create_compress(&jpeg->cinfo);
63
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;
67       
68   jpeg->t = t;
69
70   jpeg->cinfo.dest = (struct jpeg_destination_mgr *)jpeg;
71
72   // init compression
73   
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;
78
79   jpeg_set_defaults(&jpeg->cinfo);
80   jpeg_set_quality(&jpeg->cinfo,quality,TRUE);
81
82   // write tables to SWF
83   
84   jpeg_write_tables(&jpeg->cinfo);
85
86   // compess image to SWF
87    
88   jpeg_suppress_tables(&jpeg->cinfo, TRUE);
89   jpeg_start_compress(&jpeg->cinfo, FALSE);
90
91   return (JPEGBITS *)jpeg;
92 }
93
94 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits,U8 ** data,int n)
95 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
96   if (!jpeg) return -1;
97   jpeg_write_scanlines(&jpeg->cinfo,data,n);
98   return 0;
99 }
100
101 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data)
102 { return swf_SetJPEGBitsLines(jpegbits,&data,1);
103 }
104
105 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
106 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
107   if (!jpeg) return -1;
108   jpeg_finish_compress(&jpeg->cinfo);
109   free(jpeg);
110   return 0;
111 }
112
113 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
114 { struct jpeg_decompress_struct cinfo;
115   struct jpeg_error_mgr jerr;
116   JPEGBITS * out;
117   FILE * f;
118   U8 * scanline;
119   
120   cinfo.err = jpeg_std_error(&jerr);
121   jpeg_create_decompress(&cinfo); 
122
123   if ((f=fopen(fname,"rb"))==NULL) return -1;
124
125   jpeg_stdio_src(&cinfo,f);
126   jpeg_read_header(&cinfo, TRUE);
127   if(JPEG_LIB_VERSION>=62) /* jpeglib Version 6b is required for grayscale-> color conversion */
128     cinfo.out_color_space = JCS_RGB; //automatically convert grayscale images
129   jpeg_start_decompress(&cinfo);
130
131   out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
132   scanline = (U8*)malloc(4*cinfo.output_width);
133   
134   if (scanline) 
135   { int y;
136     U8 * js = scanline;
137     if(cinfo.out_color_space == JCS_GRAYSCALE) {
138         /* happens only if JPEG_LIB_VERSION above
139            was too small - let's do the conversion ourselves */
140         for (y=0;y<cinfo.output_height;y++)
141         { int x;
142           jpeg_read_scanlines(&cinfo,&js,1);
143           for(x=cinfo.output_width-1;x>=0;x--) {
144               js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
145           }
146           swf_SetJPEGBitsLines(out,(U8**)&js,1);
147         }
148     }
149     else {
150         for (y=0;y<cinfo.output_height;y++)
151         { jpeg_read_scanlines(&cinfo,&js,1);
152           swf_SetJPEGBitsLines(out,(U8**)&js,1);
153         }
154     }
155   }
156
157   swf_SetJPEGBitsFinish(out);
158   jpeg_finish_decompress(&cinfo);
159   fclose(f);
160   
161   return 0;
162 }
163
164 #endif // _JPEGLIB_INCLUDED_
165
166 // Lossless compression texture based on zlib
167
168 #ifdef _ZLIB_INCLUDED_
169
170 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,U8 * data,boolean finish)
171 { while (1)
172   { int status = deflate(zs,Z_SYNC_FLUSH);
173
174     if (zs->avail_out == 0)
175     { swf_SetBlock(t,data,zs->next_out-data);
176       zs->next_out = data;
177       zs->avail_out = OUTBUFFER_SIZE;
178     }
179     
180     if (zs->avail_in==0)
181     { if (finish) deflate(zs,Z_FINISH);
182       break;
183     }
184
185     if (status!=Z_OK)
186     {
187 #ifdef DEBUG_RFXSWF
188       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
189 #endif
190       return status;
191     }
192   }
193   return 0;
194 }
195
196 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
197 { int res = 0;
198   int bps;
199   U8 * data;
200   
201   switch (bitmap_flags)
202   { case BMF_8BIT:
203       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
204     case BMF_16BIT:
205       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
206       break;
207     case BMF_32BIT:
208       bps = width*4;
209       break;
210     default:
211       return -1;
212   }
213   
214   swf_SetU8(t,bitmap_flags);
215   swf_SetU16(t,width);
216   swf_SetU16(t,height);
217
218   if (data=malloc(OUTBUFFER_SIZE))
219   { z_stream zs;
220       
221     memset(&zs,0x00,sizeof(z_stream));
222     zs.zalloc = Z_NULL;
223     zs.zfree  = Z_NULL;
224
225     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
226     { zs.avail_in         = bps*height;
227       zs.next_in          = bitmap;
228       zs.next_out         = data;
229       zs.avail_out        = OUTBUFFER_SIZE;
230
231       if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
232       if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
233
234       deflateEnd(&zs);
235       
236       
237     } else res = -3; // zlib error
238     free(data);
239   } else res = -2; // memory error
240   
241   return res;
242 }
243
244 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
245 { RGBA * pal = palette;
246   int bps = BYTES_PER_SCANLINE(width);
247   U8 * data;
248   int res = 0;
249     
250   if (!pal)     // create default palette for grayscale images
251   { int i;
252     pal = malloc(256*sizeof(RGBA));
253     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
254     ncolors = 256;
255   }
256   
257   if ((ncolors<2)||(ncolors>256)||(!t)) return -1; // parameter error
258
259   swf_SetU8(t,BMF_8BIT);
260   swf_SetU16(t,width);
261   swf_SetU16(t,height);
262   swf_SetU8(t,ncolors-1); // number of pal entries
263
264   if (data=malloc(OUTBUFFER_SIZE))
265   { z_stream zs;
266
267     memset(&zs,0x00,sizeof(z_stream));
268     zs.zalloc = Z_NULL;
269     zs.zfree  = Z_NULL;
270
271     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
272     { U8 * zpal;                    // compress palette
273       if (zpal = malloc(ncolors*4))
274       { U8 * pp = zpal;
275         int i;
276
277     /* be careful with ST_DEFINEBITSLOSSLESS2, because
278        the Flash player produces great bugs if you use too many
279        alpha colors in your palette. The only sensible result that
280        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
281        make transparent parts in sprites. That's the cause why alpha
282        handling is implemented in lossless routines of rfxswf.
283
284        Indeed: I haven't understood yet how flash player handles
285        alpha values different from 0 and 0xff in lossless bitmaps...
286     */
287
288         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
289         { for (i=0;i<ncolors;i++)         
290           { pp[0] = pal[i].r;
291             pp[1] = pal[i].g;
292             pp[2] = pal[i].b;
293             pp[3] = pal[i].a;
294             pp+=4; 
295           }
296           zs.avail_in = 4*ncolors;
297         }
298         else
299         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
300           { pp[0] = pal[i].r;
301             pp[1] = pal[i].g;
302             pp[2] = pal[i].b;
303             pp+=3;
304           }
305           zs.avail_in         = 3*ncolors;
306         }
307
308         zs.next_in          = zpal;
309         zs.next_out         = data;
310         zs.avail_out        = OUTBUFFER_SIZE;
311
312         if (RFXSWF_deflate_wraper(t,&zs,data,FALSE)<0) res = -3;
313
314                                     // compress bitmap
315         zs.next_in = bitmap;
316         zs.avail_in = (bps*height*sizeof(U8));
317
318         if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
319
320         deflateEnd(&zs);
321
322         if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
323
324         free(zpal);
325       } else res = -2; // memory error
326     } else res = -3; // zlib error
327     free(data);
328   } else res = -2;
329   
330   if (!palette) free(pal);
331
332   return res;
333 }
334
335 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
336 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
337 }
338
339
340 #endif // _ZLIB_INCLUDED_
341
342 #undef OUTBUFFER_SIZE
343
344