* zlib is now optional
[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 void swf_SetJPEGBits2(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
114 {
115   JPEGBITS* jpeg;
116   int y;
117   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
118   for (y=0;y<height;y++)
119   { U8 scanline[3*width];
120     int x,p = 0;
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;
125     }
126     swf_SetJPEGBitsLine(jpeg,scanline);
127   }
128   swf_SetJPEGBitsFinish(jpeg);
129 }
130
131 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
132 { struct jpeg_decompress_struct cinfo;
133   struct jpeg_error_mgr jerr;
134   JPEGBITS * out;
135   FILE * f;
136   U8 * scanline;
137   
138   cinfo.err = jpeg_std_error(&jerr);
139   jpeg_create_decompress(&cinfo); 
140
141   if ((f=fopen(fname,"rb"))==NULL) return -1;
142
143   jpeg_stdio_src(&cinfo,f);
144   jpeg_read_header(&cinfo, TRUE);
145   jpeg_start_decompress(&cinfo);
146
147   out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
148   scanline = (U8*)malloc(4*cinfo.output_width);
149   
150   if (scanline) 
151   { int y;
152     U8 * js = scanline;
153     if(cinfo.out_color_space == JCS_GRAYSCALE) {
154         for (y=0;y<cinfo.output_height;y++)
155         { int x;
156           jpeg_read_scanlines(&cinfo,&js,1);
157           for(x=cinfo.output_width-1;x>=0;x--) {
158               js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
159           }
160           swf_SetJPEGBitsLines(out,(U8**)&js,1);
161         }
162     }
163     else if(cinfo.out_color_space == JCS_RGB) 
164     {
165         for (y=0;y<cinfo.output_height;y++)
166         { jpeg_read_scanlines(&cinfo,&js,1);
167           swf_SetJPEGBitsLines(out,(U8**)&js,1);
168         }
169     }
170     else if(cinfo.out_color_space == JCS_YCCK) 
171     {
172         //FIXME
173         fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
174         return -1;
175     }
176     else if(cinfo.out_color_space == JCS_YCbCr) 
177     {
178         for (y=0;y<cinfo.output_height;y++) {
179           int x;
180           for(x=0;x<cinfo.output_width;x++) {
181               int y = js[x*3+0];
182               int u = js[x*3+1];
183               int v = js[x*3+1];
184               // untested:
185               js[x*3+0] = y + ((360*(v-128))>>8);
186               js[x*3+1] = y - ((88*(u-128)-183*(v-128))>>8);
187               js[x*3+2] = y + ((455 * (u-128))>>8);
188           }
189         }
190     }
191     else if(cinfo.out_color_space == JCS_CMYK) 
192     { 
193         for (y=0;y<cinfo.output_height;y++)
194         { int x;
195           jpeg_read_scanlines(&cinfo,&js,1);
196           /* This routine seems to work for now-
197              It's a mixture of 3 different
198              CMYK->RGB conversion routines I found in the
199              web. (which all produced garbage)
200              I'm happily accepting suggestions. (mk)*/
201           for(x=0;x<cinfo.output_width;x++) {
202                 int white = 255 - js[x*4+3];
203                 js[x*3+0] = white - ((js[x*4]*white)>>8);
204                 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
205                 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
206           }
207           swf_SetJPEGBitsLines(out,(U8**)&js,1);
208         }
209     }
210   }
211
212   swf_SetJPEGBitsFinish(out);
213   jpeg_finish_decompress(&cinfo);
214   fclose(f);
215   
216   return 0;
217 }
218
219 #endif // _JPEGLIB_INCLUDED_
220
221 // Lossless compression texture based on zlib
222
223 #ifdef HAVE_ZLIB
224
225 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,U8 * data,boolean finish)
226 { while (1)
227   { int status = deflate(zs,Z_SYNC_FLUSH);
228
229     if (zs->avail_out == 0)
230     { swf_SetBlock(t,data,zs->next_out-data);
231       zs->next_out = data;
232       zs->avail_out = OUTBUFFER_SIZE;
233     }
234     
235     if (zs->avail_in==0)
236     { if (finish) deflate(zs,Z_FINISH);
237       break;
238     }
239
240     if (status!=Z_OK)
241     {
242 #ifdef DEBUG_RFXSWF
243       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
244 #endif
245       return status;
246     }
247   }
248   return 0;
249 }
250
251 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
252 { int res = 0;
253   int bps;
254   U8 * data;
255   
256   switch (bitmap_flags)
257   { case BMF_8BIT:
258       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
259     case BMF_16BIT:
260       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
261       break;
262     case BMF_32BIT:
263       bps = width*4;
264       break;
265     default:
266       return -1;
267   }
268   
269   swf_SetU8(t,bitmap_flags);
270   swf_SetU16(t,width);
271   swf_SetU16(t,height);
272
273   if ((data=malloc(OUTBUFFER_SIZE)))
274   { z_stream zs;
275       
276     memset(&zs,0x00,sizeof(z_stream));
277     zs.zalloc = Z_NULL;
278     zs.zfree  = Z_NULL;
279
280     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
281     { zs.avail_in         = bps*height;
282       zs.next_in          = bitmap;
283       zs.next_out         = data;
284       zs.avail_out        = OUTBUFFER_SIZE;
285
286       if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
287       if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
288
289       deflateEnd(&zs);
290       
291       
292     } else res = -3; // zlib error
293     free(data);
294   } else res = -2; // memory error
295   
296   return res;
297 }
298
299 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
300 { RGBA * pal = palette;
301   int bps = BYTES_PER_SCANLINE(width);
302   U8 * data;
303   int res = 0;
304     
305   if (!pal)     // create default palette for grayscale images
306   { int i;
307     pal = malloc(256*sizeof(RGBA));
308     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
309     ncolors = 256;
310   }
311   
312   if ((ncolors<2)||(ncolors>256)||(!t)) return -1; // parameter error
313
314   swf_SetU8(t,BMF_8BIT);
315   swf_SetU16(t,width);
316   swf_SetU16(t,height);
317   swf_SetU8(t,ncolors-1); // number of pal entries
318
319   if ((data=malloc(OUTBUFFER_SIZE)))
320   { z_stream zs;
321
322     memset(&zs,0x00,sizeof(z_stream));
323     zs.zalloc = Z_NULL;
324     zs.zfree  = Z_NULL;
325
326     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
327     { U8 * zpal;                    // compress palette
328       if ((zpal = malloc(ncolors*4)))
329       { U8 * pp = zpal;
330         int i;
331
332     /* be careful with ST_DEFINEBITSLOSSLESS2, because
333        the Flash player produces great bugs if you use too many
334        alpha colors in your palette. The only sensible result that
335        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
336        make transparent parts in sprites. That's the cause why alpha
337        handling is implemented in lossless routines of rfxswf.
338
339        Indeed: I haven't understood yet how flash player handles
340        alpha values different from 0 and 0xff in lossless bitmaps...
341     */
342
343         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
344         { for (i=0;i<ncolors;i++)         
345           { pp[0] = pal[i].r;
346             pp[1] = pal[i].g;
347             pp[2] = pal[i].b;
348             pp[3] = pal[i].a;
349             pp+=4; 
350           }
351           zs.avail_in = 4*ncolors;
352         }
353         else
354         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
355           { pp[0] = pal[i].r;
356             pp[1] = pal[i].g;
357             pp[2] = pal[i].b;
358             pp+=3;
359           }
360           zs.avail_in         = 3*ncolors;
361         }
362
363         zs.next_in          = zpal;
364         zs.next_out         = data;
365         zs.avail_out        = OUTBUFFER_SIZE;
366
367         if (RFXSWF_deflate_wraper(t,&zs,data,FALSE)<0) res = -3;
368
369                                     // compress bitmap
370         zs.next_in = bitmap;
371         zs.avail_in = (bps*height*sizeof(U8));
372
373         if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
374
375         deflateEnd(&zs);
376
377         if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
378
379         free(zpal);
380       } else res = -2; // memory error
381     } else res = -3; // zlib error
382     free(data);
383   } else res = -2;
384   
385   if (!palette) free(pal);
386
387   return res;
388 }
389
390 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
391 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
392 }
393
394
395 #endif // HAVE_ZLIB
396
397 #undef OUTBUFFER_SIZE
398
399