0454f10c736fae3b3a406d496ca78703b4770155
[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   while(t->len < 64) { /* actually, 63 and above is o.k., but let's stay on the safe side */
422
423       /* Flash players up to MX crash or do strange things if they encounter a 
424          DefineLossless(2) Tag with a payload of less than 63 bytes. They also
425          substitute the whole bitmap by a red rectangle.
426
427          This loop fills up the tag with zeroes so that this doesn't happen.
428       */
429       swf_SetU8(t, 0);
430   }
431   
432   return res;
433 }
434
435 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
436 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
437 }
438
439
440 #endif // HAVE_ZLIB
441
442 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
443 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
444 {
445   JPEGBITS* jpeg;
446   int y;
447   int pos;
448   int res = 0;
449   U8 * data;
450   z_stream zs;
451   
452   pos = tag->len;
453   swf_SetU32(tag, 0); //placeholder
454   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
455   for (y=0;y<height;y++)
456   { U8 scanline[3*width];
457     int x,p = 0;
458     for (x=0;x<width;x++) 
459     { scanline[p++] = bitmap[width*y+x].r;
460       scanline[p++] = bitmap[width*y+x].g;
461       scanline[p++] = bitmap[width*y+x].b;
462     }
463     swf_SetJPEGBitsLine(jpeg,scanline);
464   }
465   swf_SetJPEGBitsFinish(jpeg);
466   PUT32(&tag->data[pos], tag->len - pos - 4);
467
468   data=malloc(OUTBUFFER_SIZE);
469   memset(&zs,0x00,sizeof(z_stream));
470
471   if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
472       fprintf(stderr, "rfxswf: zlib compression failed");
473       return -3;
474   }
475     
476   zs.next_out         = data;
477   zs.avail_out        = OUTBUFFER_SIZE;
478
479   for (y=0;y<height;y++)
480   { U8 scanline[width];
481     int x,p = 0;
482     for (x=0;x<width;x++) {
483       scanline[p++] = bitmap[width*y+x].a;
484     }
485     zs.avail_in         = width;
486     zs.next_in          = scanline;
487
488     while(1) {
489         if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
490             fprintf(stderr, "rfxswf: zlib compression failed");
491             return -4;
492         }
493         if(zs.next_out != data) {
494             swf_SetBlock(tag, data, zs.next_out - data);
495             zs.next_out = data;
496             zs.avail_out = OUTBUFFER_SIZE;
497         }
498         if(!zs.avail_in) {
499             break;
500         }
501     }
502   }
503
504   while(1) {
505       int ret = deflate(&zs, Z_FINISH);
506       if (ret != Z_OK &&
507           ret != Z_STREAM_END)  {
508           fprintf(stderr, "rfxswf: zlib compression failed");
509           return -5;
510       }
511       if(zs.next_out != data) {
512           swf_SetBlock(tag, data, zs.next_out - data);
513           zs.next_out = data;
514           zs.avail_out = OUTBUFFER_SIZE;
515       }
516       if (ret == Z_STREAM_END) {
517           break;
518       }
519   }
520
521   deflateEnd(&zs);
522   free(data);
523   return 0;
524 }
525 #endif
526
527 #undef OUTBUFFER_SIZE
528