70a7729562fdfa3c20f49e891cc4c06a912a556e
[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 program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 #define OUTBUFFER_SIZE 0x8000
25
26 #ifdef HAVE_JPEGLIB
27
28 typedef struct _JPEGDESTMGR
29 { struct jpeg_destination_mgr mgr;
30   TAG *  t;
31   JOCTET * buffer;
32   struct jpeg_compress_struct cinfo;
33   struct jpeg_error_mgr jerr;
34 } JPEGDESTMGR, * LPJPEGDESTMGR;
35
36 // Destination manager callbacks
37
38 void RFXSWF_init_destination(j_compress_ptr cinfo) 
39 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
40   dmgr->buffer = (JOCTET*)malloc(OUTBUFFER_SIZE);
41   dmgr->mgr.next_output_byte = dmgr->buffer;
42   dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
43 }
44
45 boolean RFXSWF_empty_output_buffer(j_compress_ptr cinfo)
46 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
47   swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE);
48   dmgr->mgr.next_output_byte = dmgr->buffer;
49   dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
50   return TRUE;
51 }
52
53 void RFXSWF_term_destination(j_compress_ptr cinfo) 
54 { JPEGDESTMGR * dmgr = (JPEGDESTMGR *)cinfo->dest;
55   swf_SetBlock(dmgr->t,(U8*)dmgr->buffer,OUTBUFFER_SIZE-dmgr->mgr.free_in_buffer);
56   free(dmgr->buffer);
57   dmgr->mgr.free_in_buffer = 0;
58 }
59
60 JPEGBITS * swf_SetJPEGBitsStart(TAG * t,int width,int height,int quality)
61 {
62   JPEGDESTMGR * jpeg;
63         
64   // redirect compression lib output to local SWF Tag structure
65   
66   jpeg = (JPEGDESTMGR *)malloc(sizeof(JPEGDESTMGR));
67   if (!jpeg) return NULL;
68   
69   memset(jpeg,0x00,sizeof(JPEGDESTMGR));
70   jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
71
72   jpeg_create_compress(&jpeg->cinfo);
73
74   jpeg->mgr.init_destination = RFXSWF_init_destination;
75   jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer;
76   jpeg->mgr.term_destination = RFXSWF_term_destination;
77       
78   jpeg->t = t;
79
80   jpeg->cinfo.dest = (struct jpeg_destination_mgr *)jpeg;
81
82   // init compression
83   
84   jpeg->cinfo.image_width  = width;
85   jpeg->cinfo.image_height = height;
86   jpeg->cinfo.input_components = 3;
87   jpeg->cinfo.in_color_space = JCS_RGB;
88
89   jpeg_set_defaults(&jpeg->cinfo);
90   jpeg_set_quality(&jpeg->cinfo,quality,TRUE);
91
92   // write tables to SWF
93   
94   jpeg_write_tables(&jpeg->cinfo);
95
96   // compess image to SWF
97    
98   jpeg_suppress_tables(&jpeg->cinfo, TRUE);
99   jpeg_start_compress(&jpeg->cinfo, FALSE);
100
101   return (JPEGBITS *)jpeg;
102 }
103
104 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits,U8 ** data,int n)
105 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
106   if (!jpeg) return -1;
107   jpeg_write_scanlines(&jpeg->cinfo,data,n);
108   return 0;
109 }
110
111 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data)
112 { return swf_SetJPEGBitsLines(jpegbits,&data,1);
113 }
114
115 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
116 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
117   if (!jpeg) return -1;
118   jpeg_finish_compress(&jpeg->cinfo);
119   free(jpeg);
120   return 0;
121 }
122
123 void swf_SetJPEGBits2(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
124 {
125   JPEGBITS* jpeg;
126   int y;
127   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
128   for (y=0;y<height;y++)
129   { U8 scanline[3*width];
130     int x,p = 0;
131     for (x=0;x<width;x++) 
132     { scanline[p++] = bitmap[width*y+x].r;
133       scanline[p++] = bitmap[width*y+x].g;
134       scanline[p++] = bitmap[width*y+x].b;
135     }
136     swf_SetJPEGBitsLine(jpeg,scanline);
137   }
138   swf_SetJPEGBitsFinish(jpeg);
139 }
140
141 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
142 { struct jpeg_decompress_struct cinfo;
143   struct jpeg_error_mgr jerr;
144   JPEGBITS * out;
145   FILE * f;
146   U8 * scanline;
147   
148   cinfo.err = jpeg_std_error(&jerr);
149   jpeg_create_decompress(&cinfo); 
150
151   if ((f=fopen(fname,"rb"))==NULL) {
152       fprintf(stderr, "rfxswf: file open error\n");
153       return -1;
154   }
155
156   jpeg_stdio_src(&cinfo,f);
157   jpeg_read_header(&cinfo, TRUE);
158   jpeg_start_decompress(&cinfo);
159
160   out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
161   scanline = (U8*)malloc(4*cinfo.output_width);
162   
163   if (scanline) 
164   { int y;
165     U8 * js = scanline;
166     if(cinfo.out_color_space == JCS_GRAYSCALE) {
167         for (y=0;y<cinfo.output_height;y++)
168         { int x;
169           jpeg_read_scanlines(&cinfo,&js,1);
170           for(x=cinfo.output_width-1;x>=0;x--) {
171               js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
172           }
173           swf_SetJPEGBitsLines(out,(U8**)&js,1);
174         }
175     }
176     else if(cinfo.out_color_space == JCS_RGB) 
177     {
178         for (y=0;y<cinfo.output_height;y++)
179         { jpeg_read_scanlines(&cinfo,&js,1);
180           swf_SetJPEGBitsLines(out,(U8**)&js,1);
181         }
182     }
183     else if(cinfo.out_color_space == JCS_YCCK) 
184     {
185         //FIXME
186         fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
187         return -1;
188     }
189     else if(cinfo.out_color_space == JCS_YCbCr) 
190     {
191         for (y=0;y<cinfo.output_height;y++) {
192           int x;
193           for(x=0;x<cinfo.output_width;x++) {
194               int y = js[x*3+0];
195               int u = js[x*3+1];
196               int v = js[x*3+1];
197               js[x*3+0] = y + ((360*(v-128))>>8);
198               js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8);
199               js[x*3+2] = y + ((455 * (u-128))>>8);
200           }
201         }
202     }
203     else if(cinfo.out_color_space == JCS_CMYK) 
204     { 
205         for (y=0;y<cinfo.output_height;y++)
206         { int x;
207           jpeg_read_scanlines(&cinfo,&js,1);
208           /* This routine seems to work for now-
209              It's a mixture of 3 different
210              CMYK->RGB conversion routines I found in the
211              web. (which all produced garbage)
212              I'm happily accepting suggestions. (mk)*/
213           for(x=0;x<cinfo.output_width;x++) {
214                 int white = 255 - js[x*4+3];
215                 js[x*3+0] = white - ((js[x*4]*white)>>8);
216                 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
217                 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
218           }
219           swf_SetJPEGBitsLines(out,(U8**)&js,1);
220         }
221     }
222   }
223
224   swf_SetJPEGBitsFinish(out);
225   jpeg_finish_decompress(&cinfo);
226   fclose(f);
227   
228   return 0;
229 }
230
231 #endif // HAVE_JPEGLIB
232
233 // Lossless compression texture based on zlib
234
235 #ifdef HAVE_ZLIB
236
237 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
238
239   U8*data=malloc(OUTBUFFER_SIZE);
240   zs->next_out = data;
241   zs->avail_out = OUTBUFFER_SIZE;
242   while (1)
243   { int status = deflate(zs,Z_NO_FLUSH);
244
245     if (status!=Z_OK)
246     {
247 #ifdef DEBUG_RFXSWF
248       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
249 #endif
250       free(data);
251       return status;
252     }
253
254     if (zs->next_out!=data)
255     { swf_SetBlock(t,data,zs->next_out - data);
256       zs->next_out = data;
257       zs->avail_out = OUTBUFFER_SIZE;
258     }
259     
260     if (zs->avail_in==0)
261       break;
262   }
263
264   if(!finish) {
265       free(data);
266       return 0;
267   }
268
269   while(1) {
270     int status = deflate(zs,Z_FINISH);
271     if (status!=Z_OK && status!=Z_STREAM_END)
272     {
273 #ifdef DEBUG_RFXSWF
274       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
275 #endif
276       free(data);
277       return status;
278     }
279
280     if (zs->next_out!=data)
281     { 
282       swf_SetBlock(t,data,zs->next_out - data);
283       zs->next_out = data;
284       zs->avail_out = OUTBUFFER_SIZE;
285     }
286
287     if(status == Z_STREAM_END)
288         break;
289   }
290   free(data);
291   return 0;
292 }
293
294
295 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
296 { int res = 0;
297   int bps;
298   
299   switch (bitmap_flags)
300   { case BMF_8BIT:
301       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
302     case BMF_16BIT:
303       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
304       break;
305     case BMF_32BIT:
306       bps = width*4;
307       break;
308     default:
309       fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
310       return -1;
311   }
312
313   swf_SetU8(t,bitmap_flags);
314   swf_SetU16(t,width);
315   swf_SetU16(t,height);
316
317   { z_stream zs;
318       
319     memset(&zs,0x00,sizeof(z_stream));
320     zs.zalloc = Z_NULL;
321     zs.zfree  = Z_NULL;
322
323     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
324     { zs.avail_in         = bps*height;
325       zs.next_in          = bitmap;
326
327       if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
328       deflateEnd(&zs);
329       
330     } else res = -3; // zlib error
331   }
332
333   while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
334
335       /* Flash players up to MX crash or do strange things if they encounter a 
336          DefineLossless Tag with a payload of less than 63 bytes. They also
337          substitute the whole bitmap by a red rectangle.
338
339          This loop fills up the tag with zeroes so that this doesn't happen.
340       */
341       swf_SetU8(t, 0);
342   }
343   
344   return res;
345 }
346
347 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
348 { RGBA * pal = palette;
349   int bps = BYTES_PER_SCANLINE(width);
350   int res = 0;
351     
352   if (!pal)     // create default palette for grayscale images
353   { int i;
354     pal = malloc(256*sizeof(RGBA));
355     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
356     ncolors = 256;
357   }
358   
359   if ((ncolors<2)||(ncolors>256)||(!t)) {
360       fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
361       return -1; // parameter error
362   }
363
364   swf_SetU8(t,BMF_8BIT);
365   swf_SetU16(t,width);
366   swf_SetU16(t,height);
367   swf_SetU8(t,ncolors-1); // number of pal entries
368
369   { z_stream zs;
370
371     memset(&zs,0x00,sizeof(z_stream));
372     zs.zalloc = Z_NULL;
373     zs.zfree  = Z_NULL;
374
375     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
376     { U8 * zpal;                    // compress palette
377       if ((zpal = malloc(ncolors*4)))
378       { U8 * pp = zpal;
379         int i;
380
381     /* be careful with ST_DEFINEBITSLOSSLESS2, because
382        the Flash player produces great bugs if you use too many
383        alpha colors in your palette. The only sensible result that
384        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
385        make transparent parts in sprites. That's the cause why alpha
386        handling is implemented in lossless routines of rfxswf.
387
388        Indeed: I haven't understood yet how flash player handles
389        alpha values different from 0 and 0xff in lossless bitmaps...
390     */
391
392         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
393         { for (i=0;i<ncolors;i++)         
394           { pp[0] = pal[i].r;
395             pp[1] = pal[i].g;
396             pp[2] = pal[i].b;
397             pp[3] = pal[i].a;
398             pp+=4; 
399           }
400           zs.avail_in = 4*ncolors;
401         }
402         else
403         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
404           { pp[0] = pal[i].r;
405             pp[1] = pal[i].g;
406             pp[2] = pal[i].b;
407             pp+=3;
408           }
409           zs.avail_in         = 3*ncolors;
410         }
411
412         zs.next_in          = zpal;
413
414         if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
415
416                                     // compress bitmap
417         zs.next_in = bitmap;
418         zs.avail_in = (bps*height*sizeof(U8));
419
420         if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
421
422         deflateEnd(&zs);
423
424         free(zpal);
425       } else res = -2; // memory error
426     } else res = -3; // zlib error
427   }
428   
429   if (!palette) free(pal);
430
431   while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
432
433       /* Flash players up to MX crash or do strange things if they encounter a 
434          DefineLossless(2) Tag with a payload of less than 63 bytes. They also
435          substitute the whole bitmap by a red rectangle.
436
437          This loop fills up the tag with zeroes so that this doesn't happen.
438       */
439       swf_SetU8(t, 0);
440   }
441   
442   return res;
443 }
444
445 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
446 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
447 }
448
449
450 #endif // HAVE_ZLIB
451
452 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
453 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
454 {
455   JPEGBITS* jpeg;
456   int y;
457   int pos;
458   int res = 0;
459   U8 * data;
460   z_stream zs;
461   
462   pos = tag->len;
463   swf_SetU32(tag, 0); //placeholder
464   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
465   for (y=0;y<height;y++)
466   { U8 scanline[3*width];
467     int x,p = 0;
468     for (x=0;x<width;x++) 
469     { scanline[p++] = bitmap[width*y+x].r;
470       scanline[p++] = bitmap[width*y+x].g;
471       scanline[p++] = bitmap[width*y+x].b;
472     }
473     swf_SetJPEGBitsLine(jpeg,scanline);
474   }
475   swf_SetJPEGBitsFinish(jpeg);
476   PUT32(&tag->data[pos], tag->len - pos - 4);
477
478   data=malloc(OUTBUFFER_SIZE);
479   memset(&zs,0x00,sizeof(z_stream));
480
481   if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
482       fprintf(stderr, "rfxswf: zlib compression failed");
483       return -3;
484   }
485     
486   zs.next_out         = data;
487   zs.avail_out        = OUTBUFFER_SIZE;
488
489   for (y=0;y<height;y++)
490   { U8 scanline[width];
491     int x,p = 0;
492     for (x=0;x<width;x++) {
493       scanline[p++] = bitmap[width*y+x].a;
494     }
495     zs.avail_in         = width;
496     zs.next_in          = scanline;
497
498     while(1) {
499         if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
500             fprintf(stderr, "rfxswf: zlib compression failed");
501             return -4;
502         }
503         if(zs.next_out != data) {
504             swf_SetBlock(tag, data, zs.next_out - data);
505             zs.next_out = data;
506             zs.avail_out = OUTBUFFER_SIZE;
507         }
508         if(!zs.avail_in) {
509             break;
510         }
511     }
512   }
513
514   while(1) {
515       int ret = deflate(&zs, Z_FINISH);
516       if (ret != Z_OK &&
517           ret != Z_STREAM_END)  {
518           fprintf(stderr, "rfxswf: zlib compression failed");
519           return -5;
520       }
521       if(zs.next_out != data) {
522           swf_SetBlock(tag, data, zs.next_out - data);
523           zs.next_out = data;
524           zs.avail_out = OUTBUFFER_SIZE;
525       }
526       if (ret == Z_STREAM_END) {
527           break;
528       }
529   }
530
531   deflateEnd(&zs);
532   free(data);
533   return 0;
534 }
535 #endif
536
537 #undef OUTBUFFER_SIZE
538