d6e59fc0dc6b4d9729f08f080b12851432492171
[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   cinfo.out_color_space = JCS_RGB; //automatically convert grayscale images
128   jpeg_start_decompress(&cinfo);
129
130   out = swf_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       swf_SetJPEGBitsLines(out,(U8**)&js,1);
139     }
140   }
141
142   swf_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 RFXSWF_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     { swf_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   return 0;
179 }
180
181 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
182 { int res = 0;
183   int bps;
184   U8 * data;
185   
186   switch (bitmap_flags)
187   { case BMF_8BIT:
188       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
189     case BMF_16BIT:
190       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
191       break;
192     case BMF_32BIT:
193       bps = width*4;
194       break;
195     default:
196       return -1;
197   }
198   
199   swf_SetU8(t,bitmap_flags);
200   swf_SetU16(t,width);
201   swf_SetU16(t,height);
202
203   if (data=malloc(OUTBUFFER_SIZE))
204   { z_stream zs;
205       
206     memset(&zs,0x00,sizeof(z_stream));
207     zs.zalloc = Z_NULL;
208     zs.zfree  = Z_NULL;
209
210     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
211     { zs.avail_in         = bps*height;
212       zs.next_in          = bitmap;
213       zs.next_out         = data;
214       zs.avail_out        = OUTBUFFER_SIZE;
215
216       if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
217       if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
218
219       deflateEnd(&zs);
220       
221       
222     } else res = -3; // zlib error
223     free(data);
224   } else res = -2; // memory error
225   
226   return res;
227 }
228
229 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
230 { RGBA * pal = palette;
231   int bps = BYTES_PER_SCANLINE(width);
232   U8 * data;
233   int res = 0;
234     
235   if (!pal)     // create default palette for grayscale images
236   { int i;
237     pal = malloc(256*sizeof(RGBA));
238     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
239     ncolors = 256;
240   }
241   
242   if ((ncolors<2)||(ncolors>256)||(!t)) return -1; // parameter error
243
244   swf_SetU8(t,BMF_8BIT);
245   swf_SetU16(t,width);
246   swf_SetU16(t,height);
247   swf_SetU8(t,ncolors-1); // number of pal entries
248
249   if (data=malloc(OUTBUFFER_SIZE))
250   { z_stream zs;
251
252     memset(&zs,0x00,sizeof(z_stream));
253     zs.zalloc = Z_NULL;
254     zs.zfree  = Z_NULL;
255
256     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
257     { U8 * zpal;                    // compress palette
258       if (zpal = malloc(ncolors*4))
259       { U8 * pp = zpal;
260         int i;
261
262     /* be careful with ST_DEFINEBITSLOSSLESS2, because
263        the Flash player produces great bugs if you use too many
264        alpha colors in your palette. The only sensible result that
265        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
266        make transparent parts in sprites. That's the cause why alpha
267        handling is implemented in lossless routines of rfxswf.
268
269        Indeed: I haven't understood yet how flash player handles
270        alpha values different from 0 and 0xff in lossless bitmaps...
271     */
272
273         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
274         { for (i=0;i<ncolors;i++)         
275           { pp[0] = pal[i].r;
276             pp[1] = pal[i].g;
277             pp[2] = pal[i].b;
278             pp[3] = pal[i].a;
279             pp+=4; 
280           }
281           zs.avail_in = 4*ncolors;
282         }
283         else
284         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
285           { pp[0] = pal[i].r;
286             pp[1] = pal[i].g;
287             pp[2] = pal[i].b;
288             pp+=3;
289           }
290           zs.avail_in         = 3*ncolors;
291         }
292
293         zs.next_in          = zpal;
294         zs.next_out         = data;
295         zs.avail_out        = OUTBUFFER_SIZE;
296
297         if (RFXSWF_deflate_wraper(t,&zs,data,FALSE)<0) res = -3;
298
299                                     // compress bitmap
300         zs.next_in = bitmap;
301         zs.avail_in = (bps*height*sizeof(U8));
302
303         if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
304
305         deflateEnd(&zs);
306
307         if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
308
309         free(zpal);
310       } else res = -2; // memory error
311     } else res = -3; // zlib error
312     free(data);
313   } else res = -2;
314   
315   if (!palette) free(pal);
316
317   return res;
318 }
319
320 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
321 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
322 }
323
324
325 #endif // _ZLIB_INCLUDED_
326
327 #undef OUTBUFFER_SIZE
328
329