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