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