added swf_ExtractImage() function.
[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 static 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 static 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 static 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 /*  jpeg_source_mgr functions */
252 static void tag_init_source(struct jpeg_decompress_struct *cinfo)
253 {
254     TAG*tag = (TAG*)cinfo->client_data;
255     swf_SetTagPos(tag, 2);
256     cinfo->src->bytes_in_buffer = 0;
257 }
258 static boolean tag_fill_input_buffer(struct jpeg_decompress_struct *cinfo)
259 {
260     TAG*tag = (TAG*)cinfo->client_data;
261     if(tag->data[tag->pos+0] == 0xff &&
262        tag->data[tag->pos+1] == 0xd9 &&
263        tag->data[tag->pos+2] == 0xff &&
264        tag->data[tag->pos+3] == 0xd8) {
265         printf("Skip\n");
266         tag->pos += 4;
267     }
268     if(tag->pos >= tag->len) {
269         cinfo->src->next_input_byte = 0;
270         cinfo->src->bytes_in_buffer = 0;
271         return 0;
272     }
273     cinfo->src->next_input_byte = &tag->data[tag->pos];
274     cinfo->src->bytes_in_buffer = 1;//tag->len - tag->pos;
275     tag->pos += 1;
276     return 1;
277 }
278 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
279 {
280     TAG*tag = (TAG*)cinfo->client_data;
281     cinfo->src->next_input_byte = 0;
282     cinfo->src->bytes_in_buffer = 0;
283     tag->pos += count;
284 }
285 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
286 {
287     return jpeg_resync_to_restart(cinfo, desired);
288 }
289 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
290 {
291     TAG*tag = (TAG*)cinfo->client_data;
292 }
293 RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height)
294 {
295     struct jpeg_decompress_struct cinfo;
296     struct jpeg_error_mgr jerr;
297     struct jpeg_source_mgr mgr;
298     RGBA * dest;
299     *width = 0;
300     *height = 0;
301
302     if(tag->id == ST_DEFINEBITSJPEG) {
303         fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported");
304         return 0;
305     }
306     if(tag->id == ST_DEFINEBITSJPEG3) {
307         fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not yet supported");
308         return 0;
309     }
310
311     cinfo.err = jpeg_std_error(&jerr);
312     jpeg_create_decompress(&cinfo); 
313
314     cinfo.client_data = (void*)tag;
315     cinfo.src = &mgr;
316     cinfo.src->init_source = tag_init_source;
317     cinfo.src->fill_input_buffer = tag_fill_input_buffer;
318     cinfo.src->skip_input_data = tag_skip_input_data;
319     cinfo.src->resync_to_restart = jpeg_resync_to_restart;
320     cinfo.src->term_source = tag_term_source;
321     cinfo.out_color_space = JCS_RGB;
322
323     jpeg_read_header(&cinfo, TRUE);
324     *width = cinfo.image_width;
325     *height = cinfo.image_height;
326     dest = malloc(sizeof(RGBA)*cinfo.image_width*cinfo.image_height);
327     
328     jpeg_start_decompress(&cinfo);
329     int y;
330     for (y=0;y<cinfo.output_height;y++) {
331         RGBA* line = &dest[y*cinfo.image_width];
332         U8* to = (U8*)line;
333         int x;
334         jpeg_read_scanlines(&cinfo,&to,1);
335         for(x=cinfo.output_width-1;x>=0;--x) {
336             int b = to[x*3+0];
337             int g = to[x*3+1];
338             int r = to[x*3+2];
339             line[x].r = r;
340             line[x].g = g;
341             line[x].b = b;
342             line[x].a = 255;
343         }
344     }
345
346     jpeg_finish_decompress(&cinfo);
347
348     jpeg_destroy_decompress(&cinfo);
349     return dest;
350 }
351
352
353 #endif // HAVE_JPEGLIB
354
355 // Lossless compression texture based on zlib
356
357 #ifdef HAVE_ZLIB
358
359 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
360
361   U8*data=malloc(OUTBUFFER_SIZE);
362   zs->next_out = data;
363   zs->avail_out = OUTBUFFER_SIZE;
364   while (1)
365   { int status = deflate(zs,Z_NO_FLUSH);
366
367     if (status!=Z_OK)
368     {
369 #ifdef DEBUG_RFXSWF
370       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
371 #endif
372       free(data);
373       return status;
374     }
375
376     if (zs->next_out!=data)
377     { swf_SetBlock(t,data,zs->next_out - data);
378       zs->next_out = data;
379       zs->avail_out = OUTBUFFER_SIZE;
380     }
381     
382     if (zs->avail_in==0)
383       break;
384   }
385
386   if(!finish) {
387       free(data);
388       return 0;
389   }
390
391   while(1) {
392     int status = deflate(zs,Z_FINISH);
393     if (status!=Z_OK && status!=Z_STREAM_END)
394     {
395 #ifdef DEBUG_RFXSWF
396       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
397 #endif
398       free(data);
399       return status;
400     }
401
402     if (zs->next_out!=data)
403     { 
404       swf_SetBlock(t,data,zs->next_out - data);
405       zs->next_out = data;
406       zs->avail_out = OUTBUFFER_SIZE;
407     }
408
409     if(status == Z_STREAM_END)
410         break;
411   }
412   free(data);
413   return 0;
414 }
415
416
417 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
418 { int res = 0;
419   int bps;
420   
421   switch (bitmap_flags)
422   { case BMF_8BIT:
423       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
424     case BMF_16BIT:
425       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
426       break;
427     case BMF_32BIT:
428       bps = width*4;
429       break;
430     default:
431       fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
432       return -1;
433   }
434
435   swf_SetU8(t,bitmap_flags);
436   swf_SetU16(t,width);
437   swf_SetU16(t,height);
438
439   { z_stream zs;
440       
441     memset(&zs,0x00,sizeof(z_stream));
442     zs.zalloc = Z_NULL;
443     zs.zfree  = Z_NULL;
444
445     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
446     { zs.avail_in         = bps*height;
447       zs.next_in          = bitmap;
448
449       if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
450       deflateEnd(&zs);
451       
452     } else res = -3; // zlib error
453   }
454   return res;
455 }
456
457 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
458 { RGBA * pal = palette;
459   int bps = BYTES_PER_SCANLINE(width);
460   int res = 0;
461     
462   if (!pal)     // create default palette for grayscale images
463   { int i;
464     pal = malloc(256*sizeof(RGBA));
465     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
466     ncolors = 256;
467   }
468   
469   if ((ncolors<2)||(ncolors>256)||(!t)) {
470       fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
471       return -1; // parameter error
472   }
473
474   swf_SetU8(t,BMF_8BIT);
475   swf_SetU16(t,width);
476   swf_SetU16(t,height);
477   swf_SetU8(t,ncolors-1); // number of pal entries
478
479   { z_stream zs;
480
481     memset(&zs,0x00,sizeof(z_stream));
482     zs.zalloc = Z_NULL;
483     zs.zfree  = Z_NULL;
484
485     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
486     { U8 * zpal;                    // compress palette
487       if ((zpal = malloc(ncolors*4)))
488       { U8 * pp = zpal;
489         int i;
490
491     /* be careful with ST_DEFINEBITSLOSSLESS2, because
492        the Flash player produces great bugs if you use too many
493        alpha colors in your palette. The only sensible result that
494        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
495        make transparent parts in sprites. That's the cause why alpha
496        handling is implemented in lossless routines of rfxswf.
497
498        Indeed: I haven't understood yet how flash player handles
499        alpha values different from 0 and 0xff in lossless bitmaps...
500     */
501
502         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
503         { for (i=0;i<ncolors;i++)         
504           { pp[0] = pal[i].r;
505             pp[1] = pal[i].g;
506             pp[2] = pal[i].b;
507             pp[3] = pal[i].a;
508             pp+=4; 
509           }
510           zs.avail_in = 4*ncolors;
511         }
512         else
513         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
514           { pp[0] = pal[i].r;
515             pp[1] = pal[i].g;
516             pp[2] = pal[i].b;
517             pp+=3;
518           }
519           zs.avail_in         = 3*ncolors;
520         }
521
522         zs.next_in          = zpal;
523
524         if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
525
526                                     // compress bitmap
527         zs.next_in = bitmap;
528         zs.avail_in = (bps*height*sizeof(U8));
529
530         if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
531
532         deflateEnd(&zs);
533
534         free(zpal);
535       } else res = -2; // memory error
536     } else res = -3; // zlib error
537   }
538   
539   if (!palette) free(pal);
540
541   return res;
542 }
543
544 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
545 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
546 }
547
548 RGBA* swf_DefineLosslessBitsTagToImage(TAG*tag, int*dwidth, int*dheight)
549 {
550     int id,format,height, width, pos;
551     U32 datalen, datalen2;
552     int error;
553     int bpp=1;
554     int cols=0;
555     int pos2=0;
556     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
557     int t,x,y;
558     RGBA*palette;
559     U8*data,*data2;
560     RGBA*dest;
561     if(tag->id != ST_DEFINEBITSLOSSLESS &&
562        tag->id != ST_DEFINEBITSLOSSLESS2) {
563         fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",GET16(tag->data));
564         return 0;
565     }
566     swf_SetTagPos(tag, 0);
567     id =swf_GetU16(tag);
568     format = swf_GetU8(tag);
569     if(format == 3) bpp = 8;
570     if(format == 4) bpp = 16;
571     if(format == 5) bpp = 32;
572     if(format!=3 && format!=5) {
573         if(format==4)
574             fprintf(stderr, "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",id);
575         else 
576             fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", format, id);
577         return 0;
578     }
579     *dwidth = width = swf_GetU16(tag);
580     *dheight = height = swf_GetU16(tag);
581     
582     dest = malloc(sizeof(RGBA)*width*height);
583
584     if(format == 3) cols = swf_GetU8(tag) + 1;
585     else            cols = 0;
586
587     data = 0;
588     datalen = (width*height*bpp/8+cols*8);
589     do {
590         if(data)
591             free(data);
592         datalen+=4096;
593         data = malloc(datalen);
594         error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
595     } while(error == Z_BUF_ERROR);
596     if(error != Z_OK) {
597         fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
598         return 0;
599     }
600     pos = 0;
601     
602     palette = (RGBA*)malloc(cols*sizeof(RGBA));
603     for(t=0;t<cols;t++) {
604         palette[t].r = data[pos++];
605         palette[t].g = data[pos++];
606         palette[t].b = data[pos++];
607         if(alpha) {
608             palette[t].a = data[pos++];
609         }
610     }
611
612     for(y=0;y<height;y++) {
613        int srcwidth = width * (bpp/8);
614        if(bpp==32) {
615            if(!alpha) {
616                // 32 bit to 24 bit "conversion"
617                for(x=0;x<width;x++) {
618                    dest[pos2].r=data[pos+1];
619                    dest[pos2].g=data[pos+2];
620                    dest[pos2].b=data[pos+3];
621                    dest[pos2].a=255;
622                    pos2++;
623                    pos+=4; //ignore padding byte
624                }
625            } else {
626                for(x=0;x<width;x++) {
627                    dest[pos2].r=data[pos+1];
628                    dest[pos2].g=data[pos+2];
629                    dest[pos2].b=data[pos+3];
630                    dest[pos2].a=data[pos+0]; //alpha
631                    pos2++;
632                    pos+=4;
633                }
634            }
635        } else {
636            for(x=0;x<srcwidth;x++) {
637                dest[pos2]=palette[data[pos++]];
638                pos2++;
639            }
640        }
641        pos+=((srcwidth+3)&~3)-srcwidth; //align
642     }
643     if(palette)
644         free(palette);
645     free(data);
646     return dest;
647 }
648
649 #endif // HAVE_ZLIB
650
651 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
652 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
653 {
654   JPEGBITS* jpeg;
655   int y;
656   int pos;
657   int res = 0;
658   U8 * data;
659   z_stream zs;
660   
661   pos = tag->len;
662   swf_SetU32(tag, 0); //placeholder
663   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
664   for (y=0;y<height;y++)
665   { U8 scanline[3*width];
666     int x,p = 0;
667     for (x=0;x<width;x++) 
668     { scanline[p++] = bitmap[width*y+x].r;
669       scanline[p++] = bitmap[width*y+x].g;
670       scanline[p++] = bitmap[width*y+x].b;
671     }
672     swf_SetJPEGBitsLine(jpeg,scanline);
673   }
674   swf_SetJPEGBitsFinish(jpeg);
675   PUT32(&tag->data[pos], tag->len - pos - 4);
676
677   data=malloc(OUTBUFFER_SIZE);
678   memset(&zs,0x00,sizeof(z_stream));
679
680   if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
681       fprintf(stderr, "rfxswf: zlib compression failed");
682       return -3;
683   }
684     
685   zs.next_out         = data;
686   zs.avail_out        = OUTBUFFER_SIZE;
687
688   for (y=0;y<height;y++)
689   { U8 scanline[width];
690     int x,p = 0;
691     for (x=0;x<width;x++) {
692       scanline[p++] = bitmap[width*y+x].a;
693     }
694     zs.avail_in         = width;
695     zs.next_in          = scanline;
696
697     while(1) {
698         if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
699             fprintf(stderr, "rfxswf: zlib compression failed");
700             return -4;
701         }
702         if(zs.next_out != data) {
703             swf_SetBlock(tag, data, zs.next_out - data);
704             zs.next_out = data;
705             zs.avail_out = OUTBUFFER_SIZE;
706         }
707         if(!zs.avail_in) {
708             break;
709         }
710     }
711   }
712
713   while(1) {
714       int ret = deflate(&zs, Z_FINISH);
715       if (ret != Z_OK &&
716           ret != Z_STREAM_END)  {
717           fprintf(stderr, "rfxswf: zlib compression failed");
718           return -5;
719       }
720       if(zs.next_out != data) {
721           swf_SetBlock(tag, data, zs.next_out - data);
722           zs.next_out = data;
723           zs.avail_out = OUTBUFFER_SIZE;
724       }
725       if (ret == Z_STREAM_END) {
726           break;
727       }
728   }
729
730   deflateEnd(&zs);
731   free(data);
732   return 0;
733 }
734 #endif
735
736 RGBA* swf_ExtractImage(TAG*tag, int*dwidth, int*dheight)
737 {
738     RGBA*img;
739     if(tag->id == ST_DEFINEBITSJPEG ||
740        tag->id == ST_DEFINEBITSJPEG2 ||
741        tag->id == ST_DEFINEBITSJPEG3) {
742 #ifdef HAVE_JPEGLIB
743         return swf_JPEG2TagToImage(tag, dwidth, dheight);
744 #else
745         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
746         return 0;
747 #endif
748     }
749     if(tag->id == ST_DEFINEBITSLOSSLESS ||
750        tag->id == ST_DEFINEBITSLOSSLESS2) {
751 #ifdef HAVE_ZLIB
752         return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight);
753 #else
754         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
755         return 0;
756 #endif
757     }
758     fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id, swf_TagGetName(tag));
759     return 0;
760 }
761
762 #undef OUTBUFFER_SIZE
763