moved fix for short definebitslossless tags to ../rfxswf.c.
[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 void swf_GetJPEGSize(char * fname, int*width, int*height)
142 { struct jpeg_decompress_struct cinfo;
143   struct jpeg_error_mgr jerr;
144   FILE * fi;
145   *width = 0;
146   *height = 0;
147   cinfo.err = jpeg_std_error(&jerr);
148   jpeg_create_decompress(&cinfo); 
149   if ((fi=fopen(fname,"rb"))==NULL) {
150       fprintf(stderr, "rfxswf: file open error\n");
151       return;
152   }
153   jpeg_stdio_src(&cinfo, fi);
154   jpeg_read_header(&cinfo, TRUE);
155   *width = cinfo.image_width;
156   *height = cinfo.image_height;
157   jpeg_destroy_decompress(&cinfo);
158   fclose(fi);
159 }
160
161 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
162 { struct jpeg_decompress_struct cinfo;
163   struct jpeg_error_mgr jerr;
164   JPEGBITS * out;
165   FILE * f;
166   U8 * scanline;
167   
168   cinfo.err = jpeg_std_error(&jerr);
169   jpeg_create_decompress(&cinfo); 
170
171   if ((f=fopen(fname,"rb"))==NULL) {
172       fprintf(stderr, "rfxswf: file open error\n");
173       return -1;
174   }
175
176   jpeg_stdio_src(&cinfo,f);
177   jpeg_read_header(&cinfo, TRUE);
178   jpeg_start_decompress(&cinfo);
179
180   out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
181   scanline = (U8*)malloc(4*cinfo.output_width);
182   
183   if (scanline) 
184   { int y;
185     U8 * js = scanline;
186     if(cinfo.out_color_space == JCS_GRAYSCALE) {
187         for (y=0;y<cinfo.output_height;y++)
188         { int x;
189           jpeg_read_scanlines(&cinfo,&js,1);
190           for(x=cinfo.output_width-1;x>=0;x--) {
191               js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
192           }
193           swf_SetJPEGBitsLines(out,(U8**)&js,1);
194         }
195     }
196     else if(cinfo.out_color_space == JCS_RGB) 
197     {
198         for (y=0;y<cinfo.output_height;y++)
199         { jpeg_read_scanlines(&cinfo,&js,1);
200           swf_SetJPEGBitsLines(out,(U8**)&js,1);
201         }
202     }
203     else if(cinfo.out_color_space == JCS_YCCK) 
204     {
205         //FIXME
206         fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
207         return -1;
208     }
209     else if(cinfo.out_color_space == JCS_YCbCr) 
210     {
211         for (y=0;y<cinfo.output_height;y++) {
212           int x;
213           for(x=0;x<cinfo.output_width;x++) {
214               int y = js[x*3+0];
215               int u = js[x*3+1];
216               int v = js[x*3+1];
217               js[x*3+0] = y + ((360*(v-128))>>8);
218               js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8);
219               js[x*3+2] = y + ((455 * (u-128))>>8);
220           }
221         }
222     }
223     else if(cinfo.out_color_space == JCS_CMYK) 
224     { 
225         for (y=0;y<cinfo.output_height;y++)
226         { int x;
227           jpeg_read_scanlines(&cinfo,&js,1);
228           /* This routine seems to work for now-
229              It's a mixture of 3 different
230              CMYK->RGB conversion routines I found in the
231              web. (which all produced garbage)
232              I'm happily accepting suggestions. (mk)*/
233           for(x=0;x<cinfo.output_width;x++) {
234                 int white = 255 - js[x*4+3];
235                 js[x*3+0] = white - ((js[x*4]*white)>>8);
236                 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
237                 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
238           }
239           swf_SetJPEGBitsLines(out,(U8**)&js,1);
240         }
241     }
242   }
243
244   swf_SetJPEGBitsFinish(out);
245   jpeg_finish_decompress(&cinfo);
246   fclose(f);
247   
248   return 0;
249 }
250
251 #endif // HAVE_JPEGLIB
252
253 // Lossless compression texture based on zlib
254
255 #ifdef HAVE_ZLIB
256
257 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
258
259   U8*data=malloc(OUTBUFFER_SIZE);
260   zs->next_out = data;
261   zs->avail_out = OUTBUFFER_SIZE;
262   while (1)
263   { int status = deflate(zs,Z_NO_FLUSH);
264
265     if (status!=Z_OK)
266     {
267 #ifdef DEBUG_RFXSWF
268       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
269 #endif
270       free(data);
271       return status;
272     }
273
274     if (zs->next_out!=data)
275     { swf_SetBlock(t,data,zs->next_out - data);
276       zs->next_out = data;
277       zs->avail_out = OUTBUFFER_SIZE;
278     }
279     
280     if (zs->avail_in==0)
281       break;
282   }
283
284   if(!finish) {
285       free(data);
286       return 0;
287   }
288
289   while(1) {
290     int status = deflate(zs,Z_FINISH);
291     if (status!=Z_OK && status!=Z_STREAM_END)
292     {
293 #ifdef DEBUG_RFXSWF
294       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
295 #endif
296       free(data);
297       return status;
298     }
299
300     if (zs->next_out!=data)
301     { 
302       swf_SetBlock(t,data,zs->next_out - data);
303       zs->next_out = data;
304       zs->avail_out = OUTBUFFER_SIZE;
305     }
306
307     if(status == Z_STREAM_END)
308         break;
309   }
310   free(data);
311   return 0;
312 }
313
314
315 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
316 { int res = 0;
317   int bps;
318   
319   switch (bitmap_flags)
320   { case BMF_8BIT:
321       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
322     case BMF_16BIT:
323       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
324       break;
325     case BMF_32BIT:
326       bps = width*4;
327       break;
328     default:
329       fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
330       return -1;
331   }
332
333   swf_SetU8(t,bitmap_flags);
334   swf_SetU16(t,width);
335   swf_SetU16(t,height);
336
337   { z_stream zs;
338       
339     memset(&zs,0x00,sizeof(z_stream));
340     zs.zalloc = Z_NULL;
341     zs.zfree  = Z_NULL;
342
343     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
344     { zs.avail_in         = bps*height;
345       zs.next_in          = bitmap;
346
347       if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
348       deflateEnd(&zs);
349       
350     } else res = -3; // zlib error
351   }
352   return res;
353 }
354
355 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
356 { RGBA * pal = palette;
357   int bps = BYTES_PER_SCANLINE(width);
358   int res = 0;
359     
360   if (!pal)     // create default palette for grayscale images
361   { int i;
362     pal = malloc(256*sizeof(RGBA));
363     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
364     ncolors = 256;
365   }
366   
367   if ((ncolors<2)||(ncolors>256)||(!t)) {
368       fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
369       return -1; // parameter error
370   }
371
372   swf_SetU8(t,BMF_8BIT);
373   swf_SetU16(t,width);
374   swf_SetU16(t,height);
375   swf_SetU8(t,ncolors-1); // number of pal entries
376
377   { z_stream zs;
378
379     memset(&zs,0x00,sizeof(z_stream));
380     zs.zalloc = Z_NULL;
381     zs.zfree  = Z_NULL;
382
383     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
384     { U8 * zpal;                    // compress palette
385       if ((zpal = malloc(ncolors*4)))
386       { U8 * pp = zpal;
387         int i;
388
389     /* be careful with ST_DEFINEBITSLOSSLESS2, because
390        the Flash player produces great bugs if you use too many
391        alpha colors in your palette. The only sensible result that
392        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
393        make transparent parts in sprites. That's the cause why alpha
394        handling is implemented in lossless routines of rfxswf.
395
396        Indeed: I haven't understood yet how flash player handles
397        alpha values different from 0 and 0xff in lossless bitmaps...
398     */
399
400         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
401         { for (i=0;i<ncolors;i++)         
402           { pp[0] = pal[i].r;
403             pp[1] = pal[i].g;
404             pp[2] = pal[i].b;
405             pp[3] = pal[i].a;
406             pp+=4; 
407           }
408           zs.avail_in = 4*ncolors;
409         }
410         else
411         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
412           { pp[0] = pal[i].r;
413             pp[1] = pal[i].g;
414             pp[2] = pal[i].b;
415             pp+=3;
416           }
417           zs.avail_in         = 3*ncolors;
418         }
419
420         zs.next_in          = zpal;
421
422         if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
423
424                                     // compress bitmap
425         zs.next_in = bitmap;
426         zs.avail_in = (bps*height*sizeof(U8));
427
428         if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
429
430         deflateEnd(&zs);
431
432         free(zpal);
433       } else res = -2; // memory error
434     } else res = -3; // zlib error
435   }
436   
437   if (!palette) free(pal);
438
439   return res;
440 }
441
442 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
443 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
444 }
445
446
447 #endif // HAVE_ZLIB
448
449 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
450 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
451 {
452   JPEGBITS* jpeg;
453   int y;
454   int pos;
455   int res = 0;
456   U8 * data;
457   z_stream zs;
458   
459   pos = tag->len;
460   swf_SetU32(tag, 0); //placeholder
461   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
462   for (y=0;y<height;y++)
463   { U8 scanline[3*width];
464     int x,p = 0;
465     for (x=0;x<width;x++) 
466     { scanline[p++] = bitmap[width*y+x].r;
467       scanline[p++] = bitmap[width*y+x].g;
468       scanline[p++] = bitmap[width*y+x].b;
469     }
470     swf_SetJPEGBitsLine(jpeg,scanline);
471   }
472   swf_SetJPEGBitsFinish(jpeg);
473   PUT32(&tag->data[pos], tag->len - pos - 4);
474
475   data=malloc(OUTBUFFER_SIZE);
476   memset(&zs,0x00,sizeof(z_stream));
477
478   if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
479       fprintf(stderr, "rfxswf: zlib compression failed");
480       return -3;
481   }
482     
483   zs.next_out         = data;
484   zs.avail_out        = OUTBUFFER_SIZE;
485
486   for (y=0;y<height;y++)
487   { U8 scanline[width];
488     int x,p = 0;
489     for (x=0;x<width;x++) {
490       scanline[p++] = bitmap[width*y+x].a;
491     }
492     zs.avail_in         = width;
493     zs.next_in          = scanline;
494
495     while(1) {
496         if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
497             fprintf(stderr, "rfxswf: zlib compression failed");
498             return -4;
499         }
500         if(zs.next_out != data) {
501             swf_SetBlock(tag, data, zs.next_out - data);
502             zs.next_out = data;
503             zs.avail_out = OUTBUFFER_SIZE;
504         }
505         if(!zs.avail_in) {
506             break;
507         }
508     }
509   }
510
511   while(1) {
512       int ret = deflate(&zs, Z_FINISH);
513       if (ret != Z_OK &&
514           ret != Z_STREAM_END)  {
515           fprintf(stderr, "rfxswf: zlib compression failed");
516           return -5;
517       }
518       if(zs.next_out != data) {
519           swf_SetBlock(tag, data, zs.next_out - data);
520           zs.next_out = data;
521           zs.avail_out = OUTBUFFER_SIZE;
522       }
523       if (ret == Z_STREAM_END) {
524           break;
525       }
526   }
527
528   deflateEnd(&zs);
529   free(data);
530   return 0;
531 }
532 #endif
533
534 #undef OUTBUFFER_SIZE
535