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