bf88c948b912b9f097fb170a7ae70672de2ee46f
[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 HAVE_JPEGLIB
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 // HAVE_JPEGLIB
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
252 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
253 { int res = 0;
254   int bps;
255   U8 * data;
256   
257   switch (bitmap_flags)
258   { case BMF_8BIT:
259       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
260     case BMF_16BIT:
261       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
262       break;
263     case BMF_32BIT:
264       bps = width*4;
265       break;
266     default:
267       return -1;
268   }
269   
270   swf_SetU8(t,bitmap_flags);
271   swf_SetU16(t,width);
272   swf_SetU16(t,height);
273
274   if ((data=malloc(OUTBUFFER_SIZE)))
275   { z_stream zs;
276       
277     memset(&zs,0x00,sizeof(z_stream));
278     zs.zalloc = Z_NULL;
279     zs.zfree  = Z_NULL;
280
281     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
282     { zs.avail_in         = bps*height;
283       zs.next_in          = bitmap;
284       zs.next_out         = data;
285       zs.avail_out        = OUTBUFFER_SIZE;
286
287       if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
288       if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
289
290       deflateEnd(&zs);
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 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
398 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
399 {
400   JPEGBITS* jpeg;
401   int y;
402   int pos;
403   int res = 0;
404   U8 * data;
405   z_stream zs;
406   
407   pos = tag->len;
408   swf_SetU32(tag, 0); //placeholder
409   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
410   for (y=0;y<height;y++)
411   { U8 scanline[3*width];
412     int x,p = 0;
413     for (x=0;x<width;x++) 
414     { scanline[p++] = bitmap[width*y+x].r;
415       scanline[p++] = bitmap[width*y+x].g;
416       scanline[p++] = bitmap[width*y+x].b;
417     }
418     swf_SetJPEGBitsLine(jpeg,scanline);
419   }
420   swf_SetJPEGBitsFinish(jpeg);
421   PUT32(&tag->data[pos], tag->len - pos - 4);
422
423   data=malloc(OUTBUFFER_SIZE);
424   memset(&zs,0x00,sizeof(z_stream));
425
426   if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
427       fprintf(stderr, "rfxswf: zlib compression failed");
428       return -3;
429   }
430     
431   zs.next_out         = data;
432   zs.avail_out        = OUTBUFFER_SIZE;
433
434   for (y=0;y<height;y++)
435   { U8 scanline[width];
436     int x,p = 0;
437     for (x=0;x<width;x++) {
438       scanline[p++] = bitmap[width*y+x].a;
439     }
440     zs.avail_in         = width;
441     zs.next_in          = scanline;
442
443     while(1) {
444         if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
445             fprintf(stderr, "rfxswf: zlib compression failed");
446             return -4;
447         }
448         if(zs.next_out != data) {
449             swf_SetBlock(tag, data, zs.next_out - data);
450             zs.next_out = data;
451             zs.avail_out = OUTBUFFER_SIZE;
452         }
453         if(!zs.avail_in) {
454             break;
455         }
456     }
457   }
458
459   while(1) {
460       int ret = deflate(&zs, Z_FINISH);
461       if (ret != Z_OK &&
462           ret != Z_STREAM_END)  {
463           fprintf(stderr, "rfxswf: zlib compression failed");
464           return -5;
465       }
466       if(zs.next_out != data) {
467           swf_SetBlock(tag, data, zs.next_out - data);
468           zs.next_out = data;
469           zs.avail_out = OUTBUFFER_SIZE;
470       }
471       if (ret == Z_STREAM_END) {
472           break;
473       }
474   }
475
476   deflateEnd(&zs);
477   free(data);
478   return 0;
479 }
480 #endif
481
482 #undef OUTBUFFER_SIZE
483