c8cb96b04267e9856ada51cba5a097ee448db214
[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,U8 * data,boolean finish)
229 { while (1)
230   { int status = deflate(zs,Z_SYNC_FLUSH);
231
232     if (zs->avail_out == 0)
233     { swf_SetBlock(t,data,zs->next_out-data);
234       zs->next_out = data;
235       zs->avail_out = OUTBUFFER_SIZE;
236     }
237     
238     if (zs->avail_in==0)
239     { if (finish) deflate(zs,Z_FINISH);
240       break;
241     }
242
243     if (status!=Z_OK)
244     {
245 #ifdef DEBUG_RFXSWF
246       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
247 #endif
248       return status;
249     }
250   }
251   return 0;
252 }
253
254
255 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
256 { int res = 0;
257   int bps;
258   U8 * data;
259   
260   switch (bitmap_flags)
261   { case BMF_8BIT:
262       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
263     case BMF_16BIT:
264       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
265       break;
266     case BMF_32BIT:
267       bps = width*4;
268       break;
269     default:
270       fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
271       return -1;
272   }
273   
274   swf_SetU8(t,bitmap_flags);
275   swf_SetU16(t,width);
276   swf_SetU16(t,height);
277
278   if ((data=malloc(OUTBUFFER_SIZE)))
279   { z_stream zs;
280       
281     memset(&zs,0x00,sizeof(z_stream));
282     zs.zalloc = Z_NULL;
283     zs.zfree  = Z_NULL;
284
285     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
286     { zs.avail_in         = bps*height;
287       zs.next_in          = bitmap;
288       zs.next_out         = data;
289       zs.avail_out        = OUTBUFFER_SIZE;
290
291       if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
292       if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
293
294       deflateEnd(&zs);
295       
296     } else res = -3; // zlib error
297     free(data);
298   } else res = -2; // memory error
299   
300   return res;
301 }
302
303 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
304 { RGBA * pal = palette;
305   int bps = BYTES_PER_SCANLINE(width);
306   U8 * data;
307   int res = 0;
308     
309   if (!pal)     // create default palette for grayscale images
310   { int i;
311     pal = malloc(256*sizeof(RGBA));
312     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
313     ncolors = 256;
314   }
315   
316   if ((ncolors<2)||(ncolors>256)||(!t)) {
317       fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
318       return -1; // parameter error
319   }
320
321   swf_SetU8(t,BMF_8BIT);
322   swf_SetU16(t,width);
323   swf_SetU16(t,height);
324   swf_SetU8(t,ncolors-1); // number of pal entries
325
326   if ((data=malloc(OUTBUFFER_SIZE)))
327   { z_stream zs;
328
329     memset(&zs,0x00,sizeof(z_stream));
330     zs.zalloc = Z_NULL;
331     zs.zfree  = Z_NULL;
332
333     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
334     { U8 * zpal;                    // compress palette
335       if ((zpal = malloc(ncolors*4)))
336       { U8 * pp = zpal;
337         int i;
338
339     /* be careful with ST_DEFINEBITSLOSSLESS2, because
340        the Flash player produces great bugs if you use too many
341        alpha colors in your palette. The only sensible result that
342        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
343        make transparent parts in sprites. That's the cause why alpha
344        handling is implemented in lossless routines of rfxswf.
345
346        Indeed: I haven't understood yet how flash player handles
347        alpha values different from 0 and 0xff in lossless bitmaps...
348     */
349
350         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
351         { for (i=0;i<ncolors;i++)         
352           { pp[0] = pal[i].r;
353             pp[1] = pal[i].g;
354             pp[2] = pal[i].b;
355             pp[3] = pal[i].a;
356             pp+=4; 
357           }
358           zs.avail_in = 4*ncolors;
359         }
360         else
361         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
362           { pp[0] = pal[i].r;
363             pp[1] = pal[i].g;
364             pp[2] = pal[i].b;
365             pp+=3;
366           }
367           zs.avail_in         = 3*ncolors;
368         }
369
370         zs.next_in          = zpal;
371         zs.next_out         = data;
372         zs.avail_out        = OUTBUFFER_SIZE;
373
374         if (RFXSWF_deflate_wraper(t,&zs,data,FALSE)<0) res = -3;
375
376                                     // compress bitmap
377         zs.next_in = bitmap;
378         zs.avail_in = (bps*height*sizeof(U8));
379
380         if (RFXSWF_deflate_wraper(t,&zs,data,TRUE)<0) res = -3;
381
382         deflateEnd(&zs);
383
384         if (zs.next_out>data) swf_SetBlock(t,data,zs.next_out-data);
385
386         free(zpal);
387       } else res = -2; // memory error
388     } else res = -3; // zlib error
389     free(data);
390   } else res = -2;
391   
392   if (!palette) free(pal);
393
394   return res;
395 }
396
397 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
398 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
399 }
400
401
402 #endif // HAVE_ZLIB
403
404 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
405 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
406 {
407   JPEGBITS* jpeg;
408   int y;
409   int pos;
410   int res = 0;
411   U8 * data;
412   z_stream zs;
413   
414   pos = tag->len;
415   swf_SetU32(tag, 0); //placeholder
416   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
417   for (y=0;y<height;y++)
418   { U8 scanline[3*width];
419     int x,p = 0;
420     for (x=0;x<width;x++) 
421     { scanline[p++] = bitmap[width*y+x].r;
422       scanline[p++] = bitmap[width*y+x].g;
423       scanline[p++] = bitmap[width*y+x].b;
424     }
425     swf_SetJPEGBitsLine(jpeg,scanline);
426   }
427   swf_SetJPEGBitsFinish(jpeg);
428   PUT32(&tag->data[pos], tag->len - pos - 4);
429
430   data=malloc(OUTBUFFER_SIZE);
431   memset(&zs,0x00,sizeof(z_stream));
432
433   if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
434       fprintf(stderr, "rfxswf: zlib compression failed");
435       return -3;
436   }
437     
438   zs.next_out         = data;
439   zs.avail_out        = OUTBUFFER_SIZE;
440
441   for (y=0;y<height;y++)
442   { U8 scanline[width];
443     int x,p = 0;
444     for (x=0;x<width;x++) {
445       scanline[p++] = bitmap[width*y+x].a;
446     }
447     zs.avail_in         = width;
448     zs.next_in          = scanline;
449
450     while(1) {
451         if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
452             fprintf(stderr, "rfxswf: zlib compression failed");
453             return -4;
454         }
455         if(zs.next_out != data) {
456             swf_SetBlock(tag, data, zs.next_out - data);
457             zs.next_out = data;
458             zs.avail_out = OUTBUFFER_SIZE;
459         }
460         if(!zs.avail_in) {
461             break;
462         }
463     }
464   }
465
466   while(1) {
467       int ret = deflate(&zs, Z_FINISH);
468       if (ret != Z_OK &&
469           ret != Z_STREAM_END)  {
470           fprintf(stderr, "rfxswf: zlib compression failed");
471           return -5;
472       }
473       if(zs.next_out != data) {
474           swf_SetBlock(tag, data, zs.next_out - data);
475           zs.next_out = data;
476           zs.avail_out = OUTBUFFER_SIZE;
477       }
478       if (ret == Z_STREAM_END) {
479           break;
480       }
481   }
482
483   deflateEnd(&zs);
484   free(data);
485   return 0;
486 }
487 #endif
488
489 #undef OUTBUFFER_SIZE
490