11aac11fa8fe8c833cb8bb0b32efcf8eb6c33302
[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         tag->pos += 4;
266     }
267     if(tag->pos >= tag->len) {
268         cinfo->src->next_input_byte = 0;
269         cinfo->src->bytes_in_buffer = 0;
270         return 0;
271     }
272     cinfo->src->next_input_byte = &tag->data[tag->pos];
273     cinfo->src->bytes_in_buffer = 1;//tag->len - tag->pos;
274     tag->pos += 1;
275     return 1;
276 }
277 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
278 {
279     TAG*tag = (TAG*)cinfo->client_data;
280     cinfo->src->next_input_byte = 0;
281     cinfo->src->bytes_in_buffer = 0;
282     tag->pos += count;
283 }
284 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
285 {
286     return jpeg_resync_to_restart(cinfo, desired);
287 }
288 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
289 {
290     TAG*tag = (TAG*)cinfo->client_data;
291 }
292 RGBA* swf_JPEG2TagToImage(TAG*tag, int*width, int*height)
293 {
294     struct jpeg_decompress_struct cinfo;
295     struct jpeg_error_mgr jerr;
296     struct jpeg_source_mgr mgr;
297     RGBA * dest;
298     *width = 0;
299     *height = 0;
300
301     if(tag->id == ST_DEFINEBITSJPEG) {
302         fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported");
303         return 0;
304     }
305     if(tag->id == ST_DEFINEBITSJPEG3) {
306         fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not yet supported");
307         return 0;
308     }
309
310     cinfo.err = jpeg_std_error(&jerr);
311     jpeg_create_decompress(&cinfo); 
312
313     cinfo.client_data = (void*)tag;
314     cinfo.src = &mgr;
315     cinfo.src->init_source = tag_init_source;
316     cinfo.src->fill_input_buffer = tag_fill_input_buffer;
317     cinfo.src->skip_input_data = tag_skip_input_data;
318     cinfo.src->resync_to_restart = jpeg_resync_to_restart;
319     cinfo.src->term_source = tag_term_source;
320     cinfo.out_color_space = JCS_RGB;
321
322     jpeg_read_header(&cinfo, TRUE);
323     *width = cinfo.image_width;
324     *height = cinfo.image_height;
325     dest = malloc(sizeof(RGBA)*cinfo.image_width*cinfo.image_height);
326     
327     jpeg_start_decompress(&cinfo);
328     int y;
329     for (y=0;y<cinfo.output_height;y++) {
330         RGBA* line = &dest[y*cinfo.image_width];
331         U8* to = (U8*)line;
332         int x;
333         jpeg_read_scanlines(&cinfo,&to,1);
334         for(x=cinfo.output_width-1;x>=0;--x) {
335             int r = to[x*3+0];
336             int g = to[x*3+1];
337             int b = to[x*3+2];
338             line[x].r = r;
339             line[x].g = g;
340             line[x].b = b;
341             line[x].a = 255;
342         }
343     }
344
345     jpeg_finish_decompress(&cinfo);
346
347     jpeg_destroy_decompress(&cinfo);
348     return dest;
349 }
350
351
352 #endif // HAVE_JPEGLIB
353
354 // Lossless compression texture based on zlib
355
356 #ifdef HAVE_ZLIB
357
358 int RFXSWF_deflate_wraper(TAG * t,z_stream * zs,boolean finish)
359
360   U8*data=malloc(OUTBUFFER_SIZE);
361   zs->next_out = data;
362   zs->avail_out = OUTBUFFER_SIZE;
363   while (1)
364   { int status = deflate(zs,Z_NO_FLUSH);
365
366     if (status!=Z_OK)
367     {
368 #ifdef DEBUG_RFXSWF
369       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
370 #endif
371       free(data);
372       return status;
373     }
374
375     if (zs->next_out!=data)
376     { swf_SetBlock(t,data,zs->next_out - data);
377       zs->next_out = data;
378       zs->avail_out = OUTBUFFER_SIZE;
379     }
380     
381     if (zs->avail_in==0)
382       break;
383   }
384
385   if(!finish) {
386       free(data);
387       return 0;
388   }
389
390   while(1) {
391     int status = deflate(zs,Z_FINISH);
392     if (status!=Z_OK && status!=Z_STREAM_END)
393     {
394 #ifdef DEBUG_RFXSWF
395       fprintf(stderr,"rfxswf: zlib compression error (%i)\n",status);
396 #endif
397       free(data);
398       return status;
399     }
400
401     if (zs->next_out!=data)
402     { 
403       swf_SetBlock(t,data,zs->next_out - data);
404       zs->next_out = data;
405       zs->avail_out = OUTBUFFER_SIZE;
406     }
407
408     if(status == Z_STREAM_END)
409         break;
410   }
411   free(data);
412   return 0;
413 }
414
415
416 int swf_SetLosslessBits(TAG * t,U16 width,U16 height,void * bitmap,U8 bitmap_flags)
417 { int res = 0;
418   int bps;
419   
420   switch (bitmap_flags)
421   { case BMF_8BIT:
422       return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
423     case BMF_16BIT:
424       bps = BYTES_PER_SCANLINE(sizeof(U16)*width);
425       break;
426     case BMF_32BIT:
427       bps = width*4;
428       break;
429     default:
430       fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
431       return -1;
432   }
433
434   swf_SetU8(t,bitmap_flags);
435   swf_SetU16(t,width);
436   swf_SetU16(t,height);
437
438   { z_stream zs;
439       
440     memset(&zs,0x00,sizeof(z_stream));
441     zs.zalloc = Z_NULL;
442     zs.zfree  = Z_NULL;
443
444     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
445     { zs.avail_in         = bps*height;
446       zs.next_in          = bitmap;
447
448       if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
449       deflateEnd(&zs);
450       
451     } else res = -3; // zlib error
452   }
453   return res;
454 }
455
456 int swf_SetLosslessBitsIndexed(TAG * t,U16 width,U16 height,U8 * bitmap,RGBA * palette,U16 ncolors)
457 { RGBA * pal = palette;
458   int bps = BYTES_PER_SCANLINE(width);
459   int res = 0;
460     
461   if (!pal)     // create default palette for grayscale images
462   { int i;
463     pal = malloc(256*sizeof(RGBA));
464     for (i=0;i<256;i++) { pal[i].r = pal[i].g = pal[i].b = i; pal[i].a = 0xff;}
465     ncolors = 256;
466   }
467   
468   if ((ncolors<2)||(ncolors>256)||(!t)) {
469       fprintf(stderr, "rfxswf: unsupported number of colors: %d\n", ncolors);
470       return -1; // parameter error
471   }
472
473   swf_SetU8(t,BMF_8BIT);
474   swf_SetU16(t,width);
475   swf_SetU16(t,height);
476   swf_SetU8(t,ncolors-1); // number of pal entries
477
478   { z_stream zs;
479
480     memset(&zs,0x00,sizeof(z_stream));
481     zs.zalloc = Z_NULL;
482     zs.zfree  = Z_NULL;
483
484     if (deflateInit(&zs,Z_DEFAULT_COMPRESSION)==Z_OK)
485     { U8 * zpal;                    // compress palette
486       if ((zpal = malloc(ncolors*4)))
487       { U8 * pp = zpal;
488         int i;
489
490     /* be careful with ST_DEFINEBITSLOSSLESS2, because
491        the Flash player produces great bugs if you use too many
492        alpha colors in your palette. The only sensible result that
493        can be archeived is setting one color to r=0,b=0,g=0,a=0 to
494        make transparent parts in sprites. That's the cause why alpha
495        handling is implemented in lossless routines of rfxswf.
496
497        Indeed: I haven't understood yet how flash player handles
498        alpha values different from 0 and 0xff in lossless bitmaps...
499     */
500
501         if (swf_GetTagID(t)==ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
502         { for (i=0;i<ncolors;i++)         
503           { pp[0] = pal[i].r;
504             pp[1] = pal[i].g;
505             pp[2] = pal[i].b;
506             pp[3] = pal[i].a;
507             pp+=4; 
508           }
509           zs.avail_in = 4*ncolors;
510         }
511         else
512         { for (i=0;i<ncolors;i++)         // pack RGBA structures to RGB 
513           { pp[0] = pal[i].r;
514             pp[1] = pal[i].g;
515             pp[2] = pal[i].b;
516             pp+=3;
517           }
518           zs.avail_in         = 3*ncolors;
519         }
520
521         zs.next_in          = zpal;
522
523         if (RFXSWF_deflate_wraper(t,&zs,FALSE)<0) res = -3;
524
525                                     // compress bitmap
526         zs.next_in = bitmap;
527         zs.avail_in = (bps*height*sizeof(U8));
528
529         if (RFXSWF_deflate_wraper(t,&zs,TRUE)<0) res = -3;
530
531         deflateEnd(&zs);
532
533         free(zpal);
534       } else res = -2; // memory error
535     } else res = -3; // zlib error
536   }
537   
538   if (!palette) free(pal);
539
540   return res;
541 }
542
543 int swf_SetLosslessBitsGrayscale(TAG * t,U16 width,U16 height,U8 * bitmap)
544 { return swf_SetLosslessBitsIndexed(t,width,height,bitmap,NULL,256);
545 }
546
547 RGBA* swf_DefineLosslessBitsTagToImage(TAG*tag, int*dwidth, int*dheight)
548 {
549     int id,format,height, width, pos;
550     U32 datalen, datalen2;
551     int error;
552     int bpp=1;
553     int cols=0;
554     int pos2=0;
555     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
556     int t,x,y;
557     RGBA*palette;
558     U8*data,*data2;
559     RGBA*dest;
560     if(tag->id != ST_DEFINEBITSLOSSLESS &&
561        tag->id != ST_DEFINEBITSLOSSLESS2) {
562         fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",GET16(tag->data));
563         return 0;
564     }
565     swf_SetTagPos(tag, 0);
566     id =swf_GetU16(tag);
567     format = swf_GetU8(tag);
568     if(format == 3) bpp = 8;
569     if(format == 4) bpp = 16;
570     if(format == 5) bpp = 32;
571     if(format!=3 && format!=5) {
572         if(format==4)
573             fprintf(stderr, "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",id);
574         else 
575             fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n", format, id);
576         return 0;
577     }
578     *dwidth = width = swf_GetU16(tag);
579     *dheight = height = swf_GetU16(tag);
580     
581     dest = malloc(sizeof(RGBA)*width*height);
582
583     if(format == 3) cols = swf_GetU8(tag) + 1;
584     else            cols = 0;
585
586     data = 0;
587     datalen = (width*height*bpp/8+cols*8);
588     do {
589         if(data)
590             free(data);
591         datalen+=4096;
592         data = malloc(datalen);
593         error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
594     } while(error == Z_BUF_ERROR);
595     if(error != Z_OK) {
596         fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
597         return 0;
598     }
599     pos = 0;
600     
601     palette = (RGBA*)malloc(cols*sizeof(RGBA));
602     for(t=0;t<cols;t++) {
603         palette[t].r = data[pos++];
604         palette[t].g = data[pos++];
605         palette[t].b = data[pos++];
606         if(alpha) {
607             palette[t].a = data[pos++];
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         free(palette);
644     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=malloc(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   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