new lossless image functions (alpha channel still buggy, but I suspect the flash...
[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 swf_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 swf_empty_output_buffer(j_compress_ptr cinfo)
36 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
37   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 swf_term_destination(j_compress_ptr cinfo) 
44 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
45   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 * 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 =  swf_init_destination;
65   jpeg->mgr.empty_output_buffer =       swf_empty_output_buffer;
66   jpeg->mgr.term_destination =  swf_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 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 SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data)
102 { return SetJPEGBitsLines(jpegbits,&data,1);
103 }
104
105 int 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 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
126   jpeg_stdio_src(&cinfo,f);
127   jpeg_read_header(&cinfo, TRUE);
128   jpeg_start_decompress(&cinfo);
129
130   out = SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
131   scanline = (U8*)malloc(4*cinfo.output_width);
132   
133   if (scanline)
134   { int y;
135     U8 * js = scanline;
136     for (y=0;y<cinfo.output_height;y++)
137     { jpeg_read_scanlines(&cinfo,&js,1);
138       SetJPEGBitsLines(out,(U8**)&js,1);
139     }
140   }
141
142   SetJPEGBitsFinish(out);
143   jpeg_finish_decompress(&cinfo);
144   fclose(f);
145   
146   return 0;
147 }
148
149 #endif // _JPEGLIB_INCLUDED_
150
151 // Lossless compression texture based on zlib
152
153 #ifdef _ZLIB_INCLUDED_
154
155 int swf_deflate_wraper(TAG * t,z_stream * zs,U8 * data,boolean finish)
156 { while (1)
157   { int status = deflate(zs,Z_SYNC_FLUSH);
158
159     if (zs->avail_out == 0)
160     { SetBlock(t,data,zs->next_out-data);
161       zs->next_out = data;
162       zs->avail_out = OUTBUFFER_SIZE;
163     }
164     
165     if (zs->avail_in==0)
166     { if (finish) deflate(zs,Z_FINISH);
167       break;
168     }
169
170     if (status!=Z_OK)
171     {
172 #ifdef DEBUG_RFXSWF
173       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
174 #endif
175       return status;
176     }
177
178   }
179   return 0;
180 }
181
182 int SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
183 { int res = 0;
184   int bps;
185   U8 * data;
186   
187   switch (bitmap_flags)
188   { case BMF_8BIT:
189       return SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
190     case BMF_16BIT:
191       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
192       break;
193     case BMF_32BIT:
194       bps = width*4;
195       break;
196     default:
197       return -1;
198   }
199   
200   SetU8(t,bitmap_flags);
201   SetU16(t,width);
202   SetU16(t,height);
203
204   if (data=malloc(OUTBUFFER_SIZE))
205   { z_stream zs;
206       
207     memset(&zs,0x00,sizeof(z_stream));
208     zs.zalloc = Z_NULL;
209     zs.zfree  = Z_NULL;
210
211     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
212     { zs.avail_in         = bps*height;
213       zs.next_in          = bitmap;
214       zs.next_out         = data;
215       zs.avail_out        = OUTBUFFER_SIZE;
216
217       if (swf_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
218       if (zs.next_out>data) SetBlock(t,data,zs.next_out-data);
219
220       deflateEnd(&zs);
221       
222       
223     } else res = -3; // zlib error
224     free(data);
225   } else res = -2; // memory error
226   
227   return res;
228 }
229
230 int SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
231 { RGBA * pal = palette;
232   int bps = BYTES_PER_SCANLINE(width);
233   U8 * data;
234   int res = 0;
235     
236   if (!pal)     // create default palette for grayscale images
237   { int i;
238     pal = malloc(256*sizeof(RGBA));
239     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
240     ncolors = 256;
241   }
242   
243   if ((ncolors<2)||(ncolors>256)||(!t)) return -1; // parameter error
244
245   SetU8(t,BMF_8BIT);
246   SetU16(t,width);
247   SetU16(t,height);
248   SetU8(t,ncolors-1); // number of pal entries
249
250   if (data=malloc(OUTBUFFER_SIZE))
251   { z_stream zs;
252
253     memset(&zs,0x00,sizeof(z_stream));
254     zs.zalloc = Z_NULL;
255     zs.zfree  = Z_NULL;
256
257     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
258     { U8 * zpal;                    // compress palette
259       if (zpal = malloc(ncolors*4))
260       { U8 * pp = zpal;
261         int i;
262
263     /* be careful with ST_DEFINEBITSLOSSLESS2, because
264        the Flash player produces great bugs if you use too many
265        alpha colors in your palette. The only sensible result that
266        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
267        make transparent parts in sprites. That's the cause why alpha
268        handling is implemented in lossless routines of rfxswf.
269
270        Indeed: I haven't understood yet how flash player handles
271        alpha values different from 0 and 0xff in lossless bitmaps...
272     */
273
274         if (GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
275         { for (i=0;i<ncolors;i++)         
276           { pp[0] = pal[i].r;
277             pp[1] = pal[i].g;
278             pp[2] = pal[i].b;
279             pp[3] = pal[i].a;
280             pp+=4; 
281           }
282           zs.avail_in = 4*ncolors;
283         }
284         else
285         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
286           { pp[0] = pal[i].r;
287             pp[1] = pal[i].g;
288             pp[2] = pal[i].b;
289             pp+=3;
290           }
291           zs.avail_in         = 3*ncolors;
292         }
293
294         zs.next_in          = zpal;
295         zs.next_out         = data;
296         zs.avail_out        = OUTBUFFER_SIZE;
297
298         if (swf_deflate_wraper(t,&zs,data,FALSE)<0) res = -3;
299
300                                     // compress bitmap
301         zs.next_in = bitmap;
302         zs.avail_in = (bps*height*sizeof(U8));
303
304         if (swf_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
305
306         deflateEnd(&zs);
307
308         if (zs.next_out>data) SetBlock(t,data,zs.next_out-data);
309
310         free(zpal);
311       } else res = -2; // memory error
312     } else res = -3; // zlib error
313     free(data);
314   } else res = -2;
315   
316   if (!palette) free(pal);
317
318   return res;
319 }
320
321 int SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
322 { return SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
323 }
324
325
326 #endif // _ZLIB_INCLUDED_
327
328 #undef OUTBUFFER_SIZE
329
330