new function swf_RemoveJPEGTables()
[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*)rfx_alloc(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   rfx_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 *)rfx_calloc(sizeof(JPEGDESTMGR));
67   
68   jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
69
70   jpeg_create_compress(&jpeg->cinfo);
71
72   jpeg->mgr.init_destination = RFXSWF_init_destination;
73   jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer;
74   jpeg->mgr.term_destination = RFXSWF_term_destination;
75       
76   jpeg->t = t;
77
78   jpeg->cinfo.dest = (struct jpeg_destination_mgr *)jpeg;
79
80   // init compression
81   
82   jpeg->cinfo.image_width  = width;
83   jpeg->cinfo.image_height = height;
84   jpeg->cinfo.input_components = 3;
85   jpeg->cinfo.in_color_space = JCS_RGB;
86
87   jpeg_set_defaults(&jpeg->cinfo);
88   jpeg_set_quality(&jpeg->cinfo,quality,TRUE);
89
90   // write tables to SWF
91   
92   jpeg_write_tables(&jpeg->cinfo);
93
94   // compess image to SWF
95    
96   jpeg_suppress_tables(&jpeg->cinfo, TRUE);
97   jpeg_start_compress(&jpeg->cinfo, FALSE);
98
99   return (JPEGBITS *)jpeg;
100 }
101
102 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits,U8 ** data,int n)
103 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
104   if (!jpeg) return -1;
105   jpeg_write_scanlines(&jpeg->cinfo,data,n);
106   return 0;
107 }
108
109 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits,U8 * data)
110 { return swf_SetJPEGBitsLines(jpegbits,&data,1);
111 }
112
113 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
114 { JPEGDESTMGR * jpeg = (JPEGDESTMGR *)jpegbits;
115   if (!jpeg) return -1;
116   jpeg_finish_compress(&jpeg->cinfo);
117   rfx_free(jpeg);
118   return 0;
119 }
120
121 void swf_SetJPEGBits2(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
122 {
123   JPEGBITS* jpeg;
124   int y;
125   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
126   for (y=0;y<height;y++)
127   { U8 scanline[3*width];
128     int x,p = 0;
129     for (x=0;x<width;x++) 
130     { scanline[p++] = bitmap[width*y+x].r;
131       scanline[p++] = bitmap[width*y+x].g;
132       scanline[p++] = bitmap[width*y+x].b;
133     }
134     swf_SetJPEGBitsLine(jpeg,scanline);
135   }
136   swf_SetJPEGBitsFinish(jpeg);
137 }
138
139 void swf_GetJPEGSize(char * fname, int*width, int*height)
140 { struct jpeg_decompress_struct cinfo;
141   struct jpeg_error_mgr jerr;
142   FILE * fi;
143   *width = 0;
144   *height = 0;
145   cinfo.err = jpeg_std_error(&jerr);
146   jpeg_create_decompress(&cinfo); 
147   if ((fi=fopen(fname,"rb"))==NULL) {
148       fprintf(stderr, "rfxswf: file open error\n");
149       return;
150   }
151   jpeg_stdio_src(&cinfo, fi);
152   jpeg_read_header(&cinfo, TRUE);
153   *width = cinfo.image_width;
154   *height = cinfo.image_height;
155   jpeg_destroy_decompress(&cinfo);
156   fclose(fi);
157 }
158
159 int swf_SetJPEGBits(TAG * t,char * fname,int quality)
160 { struct jpeg_decompress_struct cinfo;
161   struct jpeg_error_mgr jerr;
162   JPEGBITS * out;
163   FILE * f;
164   U8 * scanline;
165   
166   cinfo.err = jpeg_std_error(&jerr);
167   jpeg_create_decompress(&cinfo); 
168
169   if ((f=fopen(fname,"rb"))==NULL) {
170       fprintf(stderr, "rfxswf: file open error\n");
171       return -1;
172   }
173
174   jpeg_stdio_src(&cinfo,f);
175   jpeg_read_header(&cinfo, TRUE);
176   jpeg_start_decompress(&cinfo);
177
178   out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
179   scanline = (U8*)rfx_alloc(4*cinfo.output_width);
180   
181   if (scanline) 
182   { int y;
183     U8 * js = scanline;
184     if(cinfo.out_color_space == JCS_GRAYSCALE) {
185         for (y=0;y<cinfo.output_height;y++)
186         { int x;
187           jpeg_read_scanlines(&cinfo,&js,1);
188           for(x=cinfo.output_width-1;x>=0;x--) {
189               js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
190           }
191           swf_SetJPEGBitsLines(out,(U8**)&js,1);
192         }
193     }
194     else if(cinfo.out_color_space == JCS_RGB) 
195     {
196         for (y=0;y<cinfo.output_height;y++)
197         { jpeg_read_scanlines(&cinfo,&js,1);
198           swf_SetJPEGBitsLines(out,(U8**)&js,1);
199         }
200     }
201     else if(cinfo.out_color_space == JCS_YCCK) 
202     {
203         //FIXME
204         fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
205         return -1;
206     }
207     else if(cinfo.out_color_space == JCS_YCbCr) 
208     {
209         for (y=0;y<cinfo.output_height;y++) {
210           int x;
211           for(x=0;x<cinfo.output_width;x++) {
212               int y = js[x*3+0];
213               int u = js[x*3+1];
214               int v = js[x*3+1];
215               js[x*3+0] = y + ((360*(v-128))>>8);
216               js[x*3+1] = y - ((88*(u-128)+183*(v-128))>>8);
217               js[x*3+2] = y + ((455 * (u-128))>>8);
218           }
219         }
220     }
221     else if(cinfo.out_color_space == JCS_CMYK) 
222     { 
223         for (y=0;y<cinfo.output_height;y++)
224         { int x;
225           jpeg_read_scanlines(&cinfo,&js,1);
226           /* This routine seems to work for now-
227              It's a mixture of 3 different
228              CMYK->RGB conversion routines I found in the
229              web. (which all produced garbage)
230              I'm happily accepting suggestions. (mk)*/
231           for(x=0;x<cinfo.output_width;x++) {
232                 int white = 255 - js[x*4+3];
233                 js[x*3+0] = white - ((js[x*4]*white)>>8);
234                 js[x*3+1] = white - ((js[x*4+1]*white)>>8);
235                 js[x*3+2] = white - ((js[x*4+2]*white)>>8);
236           }
237           swf_SetJPEGBitsLines(out,(U8**)&js,1);
238         }
239     }
240   }
241
242   swf_SetJPEGBitsFinish(out);
243   jpeg_finish_decompress(&cinfo);
244   fclose(f);
245   
246   return 0;
247 }
248
249 /*  jpeg_source_mgr functions */
250 static void tag_init_source(struct jpeg_decompress_struct *cinfo)
251 {
252     TAG*tag = (TAG*)cinfo->client_data;
253     swf_SetTagPos(tag, 2);
254     cinfo->src->bytes_in_buffer = 0;
255 }
256 static boolean tag_fill_input_buffer(struct jpeg_decompress_struct *cinfo)
257 {
258     TAG*tag = (TAG*)cinfo->client_data;
259     if(tag->data[tag->pos+0] == 0xff &&
260        tag->data[tag->pos+1] == 0xd9 &&
261        tag->data[tag->pos+2] == 0xff &&
262        tag->data[tag->pos+3] == 0xd8) {
263         tag->pos += 4;
264     }
265     if(tag->pos >= tag->len) {
266         cinfo->src->next_input_byte = 0;
267         cinfo->src->bytes_in_buffer = 0;
268         return 0;
269     }
270     cinfo->src->next_input_byte = &tag->data[tag->pos];
271     cinfo->src->bytes_in_buffer = 1;//tag->len - tag->pos;
272     tag->pos += 1;
273     return 1;
274 }
275 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
276 {
277     TAG*tag = (TAG*)cinfo->client_data;
278     cinfo->src->next_input_byte = 0;
279     cinfo->src->bytes_in_buffer = 0;
280     tag->pos += count;
281 }
282 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
283 {
284     return jpeg_resync_to_restart(cinfo, desired);
285 }
286 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
287 {
288     TAG*tag = (TAG*)cinfo->client_data;
289 }
290 RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height)
291 {
292     struct jpeg_decompress_struct cinfo;
293     struct jpeg_error_mgr jerr;
294     struct jpeg_source_mgr mgr;
295     RGBA * dest;
296     int y;
297     *width = 0;
298     *height = 0;
299
300     if(tag->id == ST_DEFINEBITSJPEG) {
301         fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported");
302         return 0;
303     }
304     if(tag->id == ST_DEFINEBITSJPEG3) {
305         fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not yet supported");
306         return 0;
307     }
308
309     cinfo.err = jpeg_std_error(&jerr);
310     jpeg_create_decompress(&cinfo); 
311
312     cinfo.client_data = (void*)tag;
313     cinfo.src = &mgr;
314     cinfo.src->init_source = tag_init_source;
315     cinfo.src->fill_input_buffer = tag_fill_input_buffer;
316     cinfo.src->skip_input_data = tag_skip_input_data;
317     cinfo.src->resync_to_restart = jpeg_resync_to_restart;
318     cinfo.src->term_source = tag_term_source;
319     cinfo.out_color_space = JCS_RGB;
320
321     jpeg_read_header(&cinfo, TRUE);
322     *width = cinfo.image_width;
323     *height = cinfo.image_height;
324     dest = rfx_alloc(sizeof(RGBA)*cinfo.image_width*cinfo.image_height);
325     
326     jpeg_start_decompress(&cinfo);
327     for (y=0;y<cinfo.output_height;y++) {
328         RGBA* line = &dest[y*cinfo.image_width];
329         U8* to = (U8*)line;
330         int x;
331         jpeg_read_scanlines(&cinfo,&to,1);
332         for(x=cinfo.output_width-1;x>=0;--x) {
333             int r = to[x*3+0];
334             int g = to[x*3+1];
335             int b = to[x*3+2];
336             line[x].r = r;
337             line[x].g = g;
338             line[x].b = b;
339             line[x].a = 255;
340         }
341     }
342
343     jpeg_finish_decompress(&cinfo);
344
345     jpeg_destroy_decompress(&cinfo);
346     return dest;
347 }
348
349
350 #endif // HAVE_JPEGLIB
351
352 // Lossless compression texture based on zlib
353
354 #ifdef HAVE_ZLIB
355
356 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
357
358   U8*data=rfx_alloc(OUTBUFFER_SIZE);
359   zs->next_out = data;
360   zs->avail_out = OUTBUFFER_SIZE;
361   while (1)
362   { int status = deflate(zs,Z_NO_FLUSH);
363
364     if (status!=Z_OK)
365     {
366 #ifdef DEBUG_RFXSWF
367       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
368 #endif
369       rfx_free(data);
370       return status;
371     }
372
373     if (zs->next_out!=data)
374     { swf_SetBlock(t,data,zs->next_out - data);
375       zs->next_out = data;
376       zs->avail_out = OUTBUFFER_SIZE;
377     }
378     
379     if (zs->avail_in==0)
380       break;
381   }
382
383   if(!finish) {
384       rfx_free(data);
385       return 0;
386   }
387
388   while(1) {
389     int status = deflate(zs,Z_FINISH);
390     if (status!=Z_OK && status!=Z_STREAM_END)
391     {
392 #ifdef DEBUG_RFXSWF
393       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
394 #endif
395       rfx_free(data);
396       return status;
397     }
398
399     if (zs->next_out!=data)
400     { 
401       swf_SetBlock(t,data,zs->next_out - data);
402       zs->next_out = data;
403       zs->avail_out = OUTBUFFER_SIZE;
404     }
405
406     if(status == Z_STREAM_END)
407         break;
408   }
409   rfx_free(data);
410   return 0;
411 }
412
413
414 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
415 { int res = 0;
416   int bps;
417   
418   switch (bitmap_flags)
419   { case BMF_8BIT:
420       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
421     case BMF_16BIT:
422       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
423       break;
424     case BMF_32BIT:
425       bps = width*4;
426       break;
427     default:
428       fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
429       return -1;
430   }
431
432   swf_SetU8(t,bitmap_flags);
433   swf_SetU16(t,width);
434   swf_SetU16(t,height);
435
436   { z_stream zs;
437       
438     memset(&zs,0x00,sizeof(z_stream));
439     zs.zalloc = Z_NULL;
440     zs.zfree  = Z_NULL;
441
442     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
443     { zs.avail_in         = bps*height;
444       zs.next_in          = bitmap;
445
446       if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
447       deflateEnd(&zs);
448       
449     } else res = -3; // zlib error
450   }
451   return res;
452 }
453
454 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
455 { RGBA * pal = palette;
456   int bps = BYTES_PER_SCANLINE(width);
457   int res = 0;
458     
459   if (!pal)     // create default palette for grayscale images
460   { int i;
461     pal = rfx_alloc(256*sizeof(RGBA));
462     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
463     ncolors = 256;
464   }
465   
466   if ((ncolors<2)||(ncolors>256)||(!t)) {
467       fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
468       return -1; // parameter error
469   }
470
471   swf_SetU8(t,BMF_8BIT);
472   swf_SetU16(t,width);
473   swf_SetU16(t,height);
474   swf_SetU8(t,ncolors-1); // number of pal entries
475
476   { z_stream zs;
477
478     memset(&zs,0x00,sizeof(z_stream));
479     zs.zalloc = Z_NULL;
480     zs.zfree  = Z_NULL;
481
482     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
483     { U8 * zpal;                    // compress palette
484       if ((zpal = rfx_alloc(ncolors*4)))
485       { U8 * pp = zpal;
486         int i;
487
488     /* be careful with ST_DEFINEBITSLOSSLESS2, because
489        the Flash player produces great bugs if you use too many
490        alpha colors in your palette. The only sensible result that
491        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
492        make transparent parts in sprites. That's the cause why alpha
493        handling is implemented in lossless routines of rfxswf.
494
495        Indeed: I haven't understood yet how flash player handles
496        alpha values different from 0 and 0xff in lossless bitmaps...
497     */
498
499         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
500         { for (i=0;i<ncolors;i++)         
501           { pp[0] = pal[i].r;
502             pp[1] = pal[i].g;
503             pp[2] = pal[i].b;
504             pp[3] = pal[i].a;
505             pp+=4; 
506           }
507           zs.avail_in = 4*ncolors;
508         }
509         else
510         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
511           { pp[0] = pal[i].r;
512             pp[1] = pal[i].g;
513             pp[2] = pal[i].b;
514             pp+=3;
515           }
516           zs.avail_in         = 3*ncolors;
517         }
518
519         zs.next_in          = zpal;
520
521         if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
522
523                                     // compress bitmap
524         zs.next_in = bitmap;
525         zs.avail_in = (bps*height*sizeof(U8));
526
527         if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
528
529         deflateEnd(&zs);
530
531         rfx_free(zpal);
532       } else res = -2; // memory error
533     } else res = -3; // zlib error
534   }
535   
536   if (!palette) rfx_free(pal);
537
538   return res;
539 }
540
541 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
542 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
543 }
544
545 RGBA* swf_DefineLosslessBitsTagToImage(TAG*tag, int*dwidth, int*dheight)
546 {
547     int id,format,height, width, pos;
548     U32 datalen, datalen2;
549     int error;
550     int bpp=1;
551     int cols=0;
552     int pos2=0;
553     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
554     int t,x,y;
555     RGBA*palette = 0;
556     U8*data,*data2;
557     RGBA*dest;
558     if(tag->id != ST_DEFINEBITSLOSSLESS &&
559        tag->id != ST_DEFINEBITSLOSSLESS2) {
560         fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",GET16(tag->data));
561         return 0;
562     }
563     swf_SetTagPos(tag, 0);
564     id =swf_GetU16(tag);
565     format = swf_GetU8(tag);
566     if(format == 3) bpp = 8;
567     if(format == 4) bpp = 16;
568     if(format == 5) bpp = 32;
569     if(format!=3 && format!=5) {
570         if(format==4)
571             fprintf(stderr, "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",id);
572         else 
573             fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", format, id);
574         return 0;
575     }
576     *dwidth = width = swf_GetU16(tag);
577     *dheight = height = swf_GetU16(tag);
578     
579     dest = rfx_alloc(sizeof(RGBA)*width*height);
580
581     if(format == 3) cols = swf_GetU8(tag) + 1;
582     else            cols = 0;
583
584     data = 0;
585     datalen = (width*height*bpp/8+cols*8);
586     do {
587         if(data)
588             rfx_free(data);
589         datalen+=4096;
590         data = rfx_alloc(datalen);
591         error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
592     } while(error == Z_BUF_ERROR);
593     if(error != Z_OK) {
594         fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
595         return 0;
596     }
597     pos = 0;
598    
599     if(cols) {
600         palette = (RGBA*)rfx_alloc(cols*sizeof(RGBA));
601         for(t=0;t<cols;t++) {
602             palette[t].r = data[pos++];
603             palette[t].g = data[pos++];
604             palette[t].b = data[pos++];
605             if(alpha) {
606                 palette[t].a = data[pos++];
607             }
608         }
609     }
610
611     for(y=0;y<height;y++) {
612        int srcwidth = width * (bpp/8);
613        if(bpp==32) {
614            if(!alpha) {
615                // 32 bit to 24 bit "conversion"
616                for(x=0;x<width;x++) {
617                    dest[pos2].r=data[pos+1];
618                    dest[pos2].g=data[pos+2];
619                    dest[pos2].b=data[pos+3];
620                    dest[pos2].a=255;
621                    pos2++;
622                    pos+=4; //ignore padding byte
623                }
624            } else {
625                for(x=0;x<width;x++) {
626                    dest[pos2].r=data[pos+1];
627                    dest[pos2].g=data[pos+2];
628                    dest[pos2].b=data[pos+3];
629                    dest[pos2].a=data[pos+0]; //alpha
630                    pos2++;
631                    pos+=4;
632                }
633            }
634        } else {
635            for(x=0;x<srcwidth;x++) {
636                dest[pos2]=palette[data[pos++]];
637                pos2++;
638            }
639        }
640        pos+=((srcwidth+3)&~3)-srcwidth; //align
641     }
642     if(palette)
643         rfx_free(palette);
644     rfx_free(data);
645     return dest;
646 }
647
648 #endif // HAVE_ZLIB
649
650 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
651 int swf_SetJPEGBits3(TAG * tag,U16 width,U16 height,RGBA* bitmap, int quality)
652 {
653   JPEGBITS* jpeg;
654   int y;
655   int pos;
656   int res = 0;
657   U8 * data;
658   z_stream zs;
659   
660   pos = tag->len;
661   swf_SetU32(tag, 0); //placeholder
662   jpeg = swf_SetJPEGBitsStart(tag,width,height,quality);
663   for (y=0;y<height;y++)
664   { U8 scanline[3*width];
665     int x,p = 0;
666     for (x=0;x<width;x++) 
667     { scanline[p++] = bitmap[width*y+x].r;
668       scanline[p++] = bitmap[width*y+x].g;
669       scanline[p++] = bitmap[width*y+x].b;
670     }
671     swf_SetJPEGBitsLine(jpeg,scanline);
672   }
673   swf_SetJPEGBitsFinish(jpeg);
674   PUT32(&tag->data[pos], tag->len - pos - 4);
675
676   data=rfx_alloc(OUTBUFFER_SIZE);
677   memset(&zs,0x00,sizeof(z_stream));
678
679   if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)!=Z_OK) {
680       fprintf(stderr, "rfxswf: zlib compression failed");
681       return -3;
682   }
683     
684   zs.next_out         = data;
685   zs.avail_out        = OUTBUFFER_SIZE;
686
687   for (y=0;y<height;y++)
688   { U8 scanline[width];
689     int x,p = 0;
690     for (x=0;x<width;x++) {
691       scanline[p++] = bitmap[width*y+x].a;
692     }
693     zs.avail_in         = width;
694     zs.next_in          = scanline;
695
696     while(1) {
697         if(deflate(&zs, Z_NO_FLUSH) != Z_OK) {
698             fprintf(stderr, "rfxswf: zlib compression failed");
699             return -4;
700         }
701         if(zs.next_out != data) {
702             swf_SetBlock(tag, data, zs.next_out - data);
703             zs.next_out = data;
704             zs.avail_out = OUTBUFFER_SIZE;
705         }
706         if(!zs.avail_in) {
707             break;
708         }
709     }
710   }
711
712   while(1) {
713       int ret = deflate(&zs, Z_FINISH);
714       if (ret != Z_OK &&
715           ret != Z_STREAM_END)  {
716           fprintf(stderr, "rfxswf: zlib compression failed");
717           return -5;
718       }
719       if(zs.next_out != data) {
720           swf_SetBlock(tag, data, zs.next_out - data);
721           zs.next_out = data;
722           zs.avail_out = OUTBUFFER_SIZE;
723       }
724       if (ret == Z_STREAM_END) {
725           break;
726       }
727   }
728
729   deflateEnd(&zs);
730   rfx_free(data);
731   return 0;
732 }
733 #endif
734
735 RGBA* swf_ExtractImage(TAG*tag, int*dwidth, int*dheight)
736 {
737     RGBA*img;
738     if(tag->id == ST_DEFINEBITSJPEG ||
739        tag->id == ST_DEFINEBITSJPEG2 ||
740        tag->id == ST_DEFINEBITSJPEG3) {
741 #ifdef HAVE_JPEGLIB
742         return swf_JPEG2TagToImage(tag, dwidth, dheight);
743 #else
744         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
745         return 0;
746 #endif
747     }
748     if(tag->id == ST_DEFINEBITSLOSSLESS ||
749        tag->id == ST_DEFINEBITSLOSSLESS2) {
750 #ifdef HAVE_ZLIB
751         return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight);
752 #else
753         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
754         return 0;
755 #endif
756     }
757     fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id, swf_TagGetName(tag));
758     return 0;
759 }
760
761 #undef OUTBUFFER_SIZE
762
763
764 void swf_RemoveJPEGTables(SWF*swf)
765 {
766     TAG* tag = swf->firstTag; 
767     TAG* tables_tag = 0;
768     while(tag) {
769         if(tag->id == ST_JPEGTABLES) {
770             tables_tag = tag;
771         }
772         tag = tag->next;
773     }
774     
775     if(!tables_tag)
776         return;
777    
778     tag = swf->firstTag;
779     while(tag) {
780         if(tag->id == ST_DEFINEBITSJPEG) {
781             void*data = rfx_alloc(tag->len);
782             swf_GetBlock(tag, data, tag->len);
783             swf_ResetTag(tag, ST_DEFINEBITSJPEG2);
784             swf_SetBlock(tag, tables_tag->data, tables_tag->len);
785             swf_SetBlock(tag, data, tag->len);
786             free(data);
787         }
788         tag = tag->next;
789     }
790     if(swf->firstTag == tables_tag)
791         swf->firstTag = tables_tag->next;
792     swf_DeleteTag(tables_tag);
793 }
794
795