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