added jpeg3 extraction support to swfextract
[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 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
27 #include "../../config.h"
28 #ifdef HAVE_ZLIB
29 #include <zconf.h>
30 #include <zlib.h>
31 #endif
32 #include <fcntl.h>
33 #include <ctype.h>
34
35 #ifdef HAVE_JPEGLIB
36 #define HAVE_BOOLEAN
37 #ifdef __cplusplus
38 extern "C" {
39 #endif
40 #include <jpeglib.h>
41 #ifdef __cplusplus
42 }
43 #endif
44 #endif // HAVE_JPEGLIB
45
46 #include "../rfxswf.h"
47
48 #define OUTBUFFER_SIZE 0x8000
49
50 int swf_ImageHasAlpha(RGBA*img, int width, int height)
51 {
52     int len = width*height;
53     int t;
54     int hasalpha=0;
55     for(t=0;t<len;t++) {
56         if(img[t].a >= 4 && img[t].a < 0xfc)
57             return 2;
58         if(img[t].a < 4)
59             hasalpha=1;
60     }
61     return hasalpha;
62 }
63
64 /*int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette)
65 {
66     int len = width*height;
67     int t;
68     int palsize = 0;
69     RGBA pal[512];
70     U32*pal32=(U32*)pal;
71     int palette_overflow = 0;
72     U32 lastcol32 = 0;
73
74     if(sizeof(RGBA)!=sizeof(U32))
75         fprintf(stderr, "rfxswf: sizeof(RGBA)!=sizeof(U32))");
76
77     lastcol32 = pal32[palsize++] = *(U32*)&img[0];
78
79     for(t=1;t<len;t++) {
80         RGBA col = img[t];
81         U32 col32 = *(U32*)&img[t];
82         int i;
83         if(col32==lastcol32)
84             continue;
85         for(i=0;i<palsize;i++) {
86             if(col32 == pal32[i])
87                 break;
88         }
89         if(i==palsize) {
90             if(palsize==512) {
91                 palette_overflow = 1;
92                 break;
93             }
94             pal32[palsize++] = col32;
95         }
96         lastcol32 = col32;
97     }
98     if(palette_overflow)
99         return width*height;
100     if(palette)
101         memcpy(palette, pal, palsize*sizeof(RGBA));
102     return palsize;
103 }*/
104
105 int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette)
106 {
107     int len = width*height;
108     int t;
109     int palsize = 0;
110     U32* pal;
111     int size[256];
112     int palette_overflow = 0;
113     U32 lastcol32 = 0;
114
115     pal = (U32*)malloc(65536*sizeof(U32));
116
117     memset(size, 0, sizeof(size));
118
119     if(sizeof(RGBA)!=sizeof(U32))
120         fprintf(stderr, "rfxswf: sizeof(RGBA)!=sizeof(U32))");
121
122     lastcol32 = (*(U32*)&img[0])^0xffffffff; // don't match
123
124     for(t=0;t<len;t++) {
125         RGBA col = img[t];
126         U32 col32 = *(U32*)&img[t];
127         int i;
128         int csize;
129         U32 hash;
130         U32* cpal;
131         if(col32 == lastcol32)
132             continue;
133         hash = (col32 >> 17) ^ col32;
134         hash ^= ((hash>>8) + 1) ^ hash;
135         hash &= 255;
136
137         csize = size[hash];
138         cpal = &pal[hash*256];
139         for(i=0;i<csize;i++) {
140             if(col32 == cpal[i])
141                 break;
142         }
143         if(i==csize) {
144             if(palsize==256) {
145                 palette_overflow = 1;
146                 break;
147             }
148             cpal[size[hash]++] = col32;
149             palsize++;
150         }
151         lastcol32 = col32;
152     }
153     if(palette_overflow) {
154         free(pal);
155         return width*height;
156     }
157     if(palette) {
158         int i = 0;
159         for(t=0;t<256;t++) {
160             int s;
161             int csize = size[t];
162             U32* cpal = &pal[t*256];
163             for(s=0;s<csize;s++)
164                 palette[i++] = *(RGBA*)(&cpal[s]);
165         }
166     }
167     free(pal);
168     return palsize;
169 }
170
171
172
173 #ifdef HAVE_JPEGLIB
174
175 typedef struct _JPEGDESTMGR {
176     struct jpeg_destination_mgr mgr;
177     TAG *t;
178     JOCTET *buffer;
179     struct jpeg_compress_struct cinfo;
180     struct jpeg_error_mgr jerr;
181 } JPEGDESTMGR, *LPJPEGDESTMGR;
182
183 // Destination manager callbacks
184
185 static void RFXSWF_init_destination(j_compress_ptr cinfo)
186 {
187     JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest;
188     dmgr->buffer = (JOCTET *) rfx_alloc(OUTBUFFER_SIZE);
189     dmgr->mgr.next_output_byte = dmgr->buffer;
190     dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
191 }
192
193 static boolean RFXSWF_empty_output_buffer(j_compress_ptr cinfo)
194 {
195     JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest;
196     swf_SetBlock(dmgr->t, (U8 *) dmgr->buffer, OUTBUFFER_SIZE);
197     dmgr->mgr.next_output_byte = dmgr->buffer;
198     dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
199     return TRUE;
200 }
201
202 static void RFXSWF_term_destination(j_compress_ptr cinfo)
203 {
204     JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest;
205     swf_SetBlock(dmgr->t, (U8 *) dmgr->buffer,
206                  OUTBUFFER_SIZE - dmgr->mgr.free_in_buffer);
207     rfx_free(dmgr->buffer);
208     dmgr->mgr.free_in_buffer = 0;
209 }
210
211 JPEGBITS *swf_SetJPEGBitsStart(TAG * t, int width, int height, int quality)
212 {
213     JPEGDESTMGR *jpeg;
214
215     // redirect compression lib output to local SWF Tag structure
216
217     jpeg = (JPEGDESTMGR *) rfx_calloc(sizeof(JPEGDESTMGR));
218
219     jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
220
221     jpeg_create_compress(&jpeg->cinfo);
222
223     jpeg->mgr.init_destination = RFXSWF_init_destination;
224     jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer;
225     jpeg->mgr.term_destination = RFXSWF_term_destination;
226
227     jpeg->t = t;
228
229     jpeg->cinfo.dest = (struct jpeg_destination_mgr *) jpeg;
230
231     // init compression
232
233     jpeg->cinfo.image_width = width;
234     jpeg->cinfo.image_height = height;
235     jpeg->cinfo.input_components = 3;
236     jpeg->cinfo.in_color_space = JCS_RGB;
237
238     jpeg_set_defaults(&jpeg->cinfo);
239     jpeg_set_quality(&jpeg->cinfo, quality, TRUE);
240
241     // write tables to SWF
242
243     jpeg_write_tables(&jpeg->cinfo);
244
245     // compess image to SWF
246
247     jpeg_suppress_tables(&jpeg->cinfo, TRUE);
248     jpeg_start_compress(&jpeg->cinfo, FALSE);
249
250     return (JPEGBITS *) jpeg;
251 }
252
253 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits, U8 ** data, int n)
254 {
255     JPEGDESTMGR *jpeg = (JPEGDESTMGR *) jpegbits;
256     if (!jpeg)
257         return -1;
258     jpeg_write_scanlines(&jpeg->cinfo, data, n);
259     return 0;
260 }
261
262 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits, U8 * data)
263 {
264     return swf_SetJPEGBitsLines(jpegbits, &data, 1);
265 }
266
267 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
268 {
269     JPEGDESTMGR *jpeg = (JPEGDESTMGR *) jpegbits;
270     if (!jpeg)
271         return -1;
272     jpeg_finish_compress(&jpeg->cinfo);
273     jpeg_destroy_compress(&jpeg->cinfo);
274     rfx_free(jpeg);
275     return 0;
276 }
277
278 #if defined(HAVE_JPEGLIB)
279 void swf_SetJPEGBits2(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
280 {
281     JPEGBITS *jpeg;
282     int y;
283     jpeg = swf_SetJPEGBitsStart(tag, width, height, quality);
284         U8 *scanline = (U8*)rfx_alloc(3 * width);
285     for (y = 0; y < height; y++) {
286         int x, p = 0;
287         for (x = 0; x < width; x++) {
288             scanline[p++] = bitmap[width * y + x].r;
289             scanline[p++] = bitmap[width * y + x].g;
290             scanline[p++] = bitmap[width * y + x].b;
291         }
292         swf_SetJPEGBitsLine(jpeg, scanline);
293     }
294     rfx_free(scanline);
295     swf_SetJPEGBitsFinish(jpeg);
296 }
297 #else
298 void swf_SetJPEGBits2(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
299 {
300     fprintf(stderr, "Error: swftools compiled without jpeglib\n");
301     return -1;
302 }
303 #endif
304
305 void swf_GetJPEGSize(const char *fname, int *width, int *height)
306 {
307     struct jpeg_decompress_struct cinfo;
308     struct jpeg_error_mgr jerr;
309     FILE *fi;
310     *width = 0;
311     *height = 0;
312     cinfo.err = jpeg_std_error(&jerr);
313     jpeg_create_decompress(&cinfo);
314     if ((fi = fopen(fname, "rb")) == NULL) {
315         fprintf(stderr, "rfxswf: file open error\n");
316         return;
317     }
318     jpeg_stdio_src(&cinfo, fi);
319     jpeg_read_header(&cinfo, TRUE);
320     *width = cinfo.image_width;
321     *height = cinfo.image_height;
322     jpeg_destroy_decompress(&cinfo);
323     fclose(fi);
324 }
325
326 int swf_SetJPEGBits(TAG * t, const char *fname, int quality)
327 {
328     struct jpeg_decompress_struct cinfo;
329     struct jpeg_error_mgr jerr;
330     JPEGBITS *out;
331     FILE *f;
332     U8 *scanline;
333
334     cinfo.err = jpeg_std_error(&jerr);
335     jpeg_create_decompress(&cinfo);
336
337     if ((f = fopen(fname, "rb")) == NULL) {
338         fprintf(stderr, "rfxswf: file open error\n");
339         return -1;
340     }
341
342     jpeg_stdio_src(&cinfo, f);
343     jpeg_read_header(&cinfo, TRUE);
344     jpeg_start_decompress(&cinfo);
345
346     out =
347         swf_SetJPEGBitsStart(t, cinfo.output_width, cinfo.output_height,
348                              quality);
349     scanline = (U8 *) rfx_alloc(4 * cinfo.output_width);
350
351     if (scanline) {
352         int y;
353         U8 *js = scanline;
354         if (cinfo.out_color_space == JCS_GRAYSCALE) {
355             for (y = 0; y < cinfo.output_height; y++) {
356                 int x;
357                 jpeg_read_scanlines(&cinfo, &js, 1);
358                 for (x = cinfo.output_width - 1; x >= 0; x--) {
359                     js[x * 3] = js[x * 3 + 1] = js[x * 3 + 2] = js[x];
360                 }
361                 swf_SetJPEGBitsLines(out, (U8 **) & js, 1);
362             }
363         } else if (cinfo.out_color_space == JCS_RGB) {
364             for (y = 0; y < cinfo.output_height; y++) {
365                 jpeg_read_scanlines(&cinfo, &js, 1);
366                 swf_SetJPEGBitsLines(out, (U8 **) & js, 1);
367             }
368         } else if (cinfo.out_color_space == JCS_YCCK) {
369             //FIXME
370             fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
371             return -1;
372         } else if (cinfo.out_color_space == JCS_YCbCr) {
373             for (y = 0; y < cinfo.output_height; y++) {
374                 int x;
375                 for (x = 0; x < cinfo.output_width; x++) {
376                     int y = js[x * 3 + 0];
377                     int u = js[x * 3 + 1];
378                     int v = js[x * 3 + 1];
379                     js[x * 3 + 0] = y + ((360 * (v - 128)) >> 8);
380                     js[x * 3 + 1] =
381                         y - ((88 * (u - 128) + 183 * (v - 128)) >> 8);
382                     js[x * 3 + 2] = y + ((455 * (u - 128)) >> 8);
383                 }
384             }
385         } else if (cinfo.out_color_space == JCS_CMYK) {
386             for (y = 0; y < cinfo.output_height; y++) {
387                 int x;
388                 jpeg_read_scanlines(&cinfo, &js, 1);
389                 /* This routine seems to work for now-
390                    It's a mixture of 3 different
391                    CMYK->RGB conversion routines I found in the
392                    web. (which all produced garbage)
393                    I'm happily accepting suggestions. (mk) */
394                 for (x = 0; x < cinfo.output_width; x++) {
395                     int white = 255 - js[x * 4 + 3];
396                     js[x * 3 + 0] = white - ((js[x * 4] * white) >> 8);
397                     js[x * 3 + 1] = white - ((js[x * 4 + 1] * white) >> 8);
398                     js[x * 3 + 2] = white - ((js[x * 4 + 2] * white) >> 8);
399                 }
400                 swf_SetJPEGBitsLines(out, (U8 **) & js, 1);
401             }
402         }
403     }
404
405     rfx_free(scanline);
406     swf_SetJPEGBitsFinish(out);
407     jpeg_finish_decompress(&cinfo);
408     fclose(f);
409
410     return 0;
411 }
412
413 typedef struct _JPEGFILEMGR {
414     struct jpeg_destination_mgr mgr;
415     JOCTET *buffer;
416     struct jpeg_compress_struct* cinfo;
417     struct jpeg_error_mgr* jerr;
418     FILE*fi;
419 } JPEGFILEMGR;
420
421 static void file_init_destination(j_compress_ptr cinfo) 
422
423     JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest);
424     struct jpeg_destination_mgr*dmgr = &fmgr->mgr;
425
426     fmgr->buffer = (JOCTET*)rfx_alloc(OUTBUFFER_SIZE);
427     if(!fmgr->buffer) {
428         perror("malloc");
429         fprintf(stderr, "Out of memory!\n");
430         exit(1);
431     }
432
433     dmgr->next_output_byte = fmgr->buffer;
434     dmgr->free_in_buffer = OUTBUFFER_SIZE;
435 }
436
437 static boolean file_empty_output_buffer(j_compress_ptr cinfo)
438
439     JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest);
440     struct jpeg_destination_mgr*dmgr = &fmgr->mgr;
441
442     if(fmgr->fi)
443         fwrite(fmgr->buffer, OUTBUFFER_SIZE, 1, fmgr->fi);
444
445     dmgr->next_output_byte = fmgr->buffer;
446     dmgr->free_in_buffer = OUTBUFFER_SIZE;
447     return 1;
448 }
449
450 static void file_term_destination(j_compress_ptr cinfo) 
451
452     JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest);
453     struct jpeg_destination_mgr*dmgr = &fmgr->mgr;
454
455     if(fmgr->fi)
456         fwrite(fmgr->buffer, OUTBUFFER_SIZE-dmgr->free_in_buffer, 1, fmgr->fi);
457
458     rfx_free(fmgr->buffer);
459     fmgr->buffer = 0;
460     dmgr->free_in_buffer = 0;
461     dmgr->next_output_byte = 0;
462 }
463
464 void swf_SaveJPEG(char*filename, RGBA*pixels, int width, int height, int quality)
465 {
466     JPEGFILEMGR fmgr;
467     struct jpeg_compress_struct cinfo;
468     struct jpeg_error_mgr jerr;
469     unsigned char*data2 = 0;
470     int y;
471
472     FILE*fi = fopen(filename, "wb");
473     if(!fi) {
474         char buf[256];
475         sprintf(buf, "rfxswf: Couldn't create %s", filename);
476         perror(buf);
477         return;
478     }
479     data2 = (unsigned char *)rfx_calloc(width*3);
480
481     memset(&cinfo, 0, sizeof(cinfo));
482     memset(&jerr, 0, sizeof(jerr));
483     memset(&fmgr, 0, sizeof(fmgr));
484     cinfo.err = jpeg_std_error(&jerr);
485     jpeg_create_compress(&cinfo);
486
487     fmgr.mgr.init_destination = file_init_destination;
488     fmgr.mgr.empty_output_buffer = file_empty_output_buffer;
489     fmgr.mgr.term_destination = file_term_destination;
490     fmgr.fi = fi;
491     fmgr.cinfo = &cinfo;
492     fmgr.jerr = &jerr;
493     cinfo.dest = (struct jpeg_destination_mgr*)&fmgr;
494
495     // init compression
496
497     cinfo.image_width  = width;
498     cinfo.image_height = height;
499     cinfo.input_components = 3;
500     cinfo.in_color_space = JCS_RGB;
501     jpeg_set_defaults(&cinfo);
502     cinfo.dct_method = JDCT_IFAST;
503     jpeg_set_quality(&cinfo,quality,TRUE);
504
505     //jpeg_write_tables(&cinfo);
506     //jpeg_suppress_tables(&cinfo, TRUE);
507     jpeg_start_compress(&cinfo, FALSE);
508
509     for(y=0;y<height;y++) {
510         int x;
511         RGBA*src = &pixels[y*width];
512         for(x=0;x<width;x++) {
513             data2[x*3+0] = src[x].r;
514             data2[x*3+1] = src[x].g;
515             data2[x*3+2] = src[x].b;
516         }
517         jpeg_write_scanlines(&cinfo, &data2, 1);
518     }
519     rfx_free(data2);
520     jpeg_finish_compress(&cinfo);
521     jpeg_destroy_compress(&cinfo);
522
523     fclose(fi);
524 }
525
526 /*  jpeg_source_mgr functions */
527 static void tag_init_source(struct jpeg_decompress_struct *cinfo)
528 {
529     TAG *tag = (TAG *) cinfo->client_data;
530     if (tag->id == ST_DEFINEBITSJPEG3) {
531         swf_SetTagPos(tag, 6);
532     } else {
533         swf_SetTagPos(tag, 2);
534     }
535     cinfo->src->bytes_in_buffer = 0;
536 }
537 static boolean tag_fill_input_buffer(struct jpeg_decompress_struct *cinfo)
538 {
539     TAG *tag = (TAG *) cinfo->client_data;
540     if (tag->pos + 4 <= tag->len &&
541         tag->data[tag->pos + 0] == 0xff &&
542         tag->data[tag->pos + 1] == 0xd9 &&
543         tag->data[tag->pos + 2] == 0xff &&
544         tag->data[tag->pos + 3] == 0xd8) {
545         tag->pos += 4;
546     }
547     if (tag->pos >= tag->len) {
548         cinfo->src->next_input_byte = 0;
549         cinfo->src->bytes_in_buffer = 0;
550         return 0;
551     }
552     cinfo->src->next_input_byte = &tag->data[tag->pos];
553     cinfo->src->bytes_in_buffer = 1;    //tag->len - tag->pos;
554     tag->pos += 1;
555     return 1;
556 }
557 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
558 {
559     TAG *tag = (TAG *) cinfo->client_data;
560     cinfo->src->next_input_byte = 0;
561     cinfo->src->bytes_in_buffer = 0;
562     tag->pos += count;
563 }
564 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
565 {
566     return jpeg_resync_to_restart(cinfo, desired);
567 }
568 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
569 {
570     TAG *tag = (TAG *) cinfo->client_data;
571 }
572 RGBA *swf_JPEG2TagToImage(TAG * tag, int *width, int *height)
573 {
574     struct jpeg_decompress_struct cinfo;
575     struct jpeg_error_mgr jerr;
576     struct jpeg_source_mgr mgr;
577     RGBA *dest;
578     int y;
579     int offset = 0;
580     int oldtaglen = 0;
581     *width = 0;
582     *height = 0;
583
584     if (tag->id == ST_DEFINEBITSJPEG) {
585         fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported\n");
586         return 0;
587     }
588     if (tag->id == ST_DEFINEBITSJPEG3) {
589 #ifdef HAVE_ZLIB
590         offset = swf_GetU32(tag);
591         oldtaglen = tag->len;
592         tag->len = offset+6;
593 #else
594         fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not possible: no zlib\n");
595         return 0;
596 #endif
597     }
598
599     cinfo.err = jpeg_std_error(&jerr);
600     jpeg_create_decompress(&cinfo);
601
602     cinfo.client_data = (void *) tag;
603     cinfo.src = &mgr;
604     cinfo.src->init_source = tag_init_source;
605     cinfo.src->fill_input_buffer = tag_fill_input_buffer;
606     cinfo.src->skip_input_data = tag_skip_input_data;
607     cinfo.src->resync_to_restart = jpeg_resync_to_restart;
608     cinfo.src->term_source = tag_term_source;
609     cinfo.out_color_space = JCS_RGB;
610
611     jpeg_read_header(&cinfo, TRUE);
612     *width = cinfo.image_width;
613     *height = cinfo.image_height;
614     dest = (RGBA*)
615         rfx_alloc(sizeof(RGBA) * cinfo.image_width * cinfo.image_height);
616
617     jpeg_start_decompress(&cinfo);
618     for (y = 0; y < cinfo.output_height; y++) {
619         RGBA *line = &dest[y * cinfo.image_width];
620         U8 *to = (U8 *) line;
621         int x;
622         jpeg_read_scanlines(&cinfo, &to, 1);
623         for (x = cinfo.output_width - 1; x >= 0; --x) {
624             int r = to[x * 3 + 0];
625             int g = to[x * 3 + 1];
626             int b = to[x * 3 + 2];
627             line[x].r = r;
628             line[x].g = g;
629             line[x].b = b;
630             line[x].a = 255;
631         }
632     }
633
634     jpeg_finish_decompress(&cinfo);
635
636     jpeg_destroy_decompress(&cinfo);
637
638 #ifdef HAVE_ZLIB
639     if(offset) {
640         uLongf datalen = cinfo.output_width*cinfo.output_height;
641         U8* alphadata = (U8*)rfx_alloc(datalen);
642         int error;
643         tag->len = oldtaglen;
644         swf_SetTagPos(tag, 6+offset);
645         error = uncompress(alphadata, &datalen, &tag->data[tag->pos], tag->len - tag->pos);
646         if (error != Z_OK) {
647             fprintf(stderr, "rfxswf: Zlib error %d while extracting definejpeg3\n", error);
648             return 0;
649         }
650         for(y=0;y<cinfo.output_height;y++) {
651             RGBA*line = &dest[y*cinfo.output_width];
652             U8*aline = &alphadata[y*cinfo.output_width];
653             int x;
654             for(x=0;x<cinfo.output_width;x++) {
655                 line[x].a = aline[x];
656             }
657         }
658         free(alphadata);
659     }
660 #endif
661     return dest;
662 }
663
664 #endif                          // HAVE_JPEGLIB
665
666 // Lossless compression texture based on zlib
667
668 #ifdef HAVE_ZLIB
669
670 int RFXSWF_deflate_wraper(TAG * t, z_stream * zs, boolean finish)
671 {
672     U8 *data = (U8*)rfx_alloc(OUTBUFFER_SIZE);
673     zs->next_out = data;
674     zs->avail_out = OUTBUFFER_SIZE;
675     while (1) {
676         int status = deflate(zs, Z_NO_FLUSH);
677
678         if (status != Z_OK) {
679             fprintf(stderr, "rfxswf: zlib compression error (%i)\n", status);
680             rfx_free(data);
681             return status;
682         }
683
684         if (zs->next_out != data) {
685             swf_SetBlock(t, data, zs->next_out - data);
686             zs->next_out = data;
687             zs->avail_out = OUTBUFFER_SIZE;
688         }
689
690         if (zs->avail_in == 0)
691             break;
692     }
693
694     if (!finish) {
695         rfx_free(data);
696         return 0;
697     }
698
699     while (1) {
700         int status = deflate(zs, Z_FINISH);
701         if (status != Z_OK && status != Z_STREAM_END) {
702             fprintf(stderr, "rfxswf: zlib compression error (%i)\n", status);
703             rfx_free(data);
704             return status;
705         }
706
707         if (zs->next_out != data) {
708             swf_SetBlock(t, data, zs->next_out - data);
709             zs->next_out = data;
710             zs->avail_out = OUTBUFFER_SIZE;
711         }
712
713         if (status == Z_STREAM_END)
714             break;
715     }
716     rfx_free(data);
717     return 0;
718 }
719
720
721 int swf_SetLosslessBits(TAG * t, U16 width, U16 height, void *bitmap, U8 bitmap_flags)
722 {
723     int res = 0;
724     int bps;
725
726     switch (bitmap_flags) {
727     case BMF_8BIT:
728         return swf_SetLosslessBitsIndexed(t, width, height, (U8*)bitmap, NULL, 256);
729     case BMF_16BIT:
730         bps = BYTES_PER_SCANLINE(sizeof(U16) * width);
731         break;
732     case BMF_32BIT:
733         bps = width * 4;
734         break;
735     default:
736         fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
737         return -1;
738     }
739
740     swf_SetU8(t, bitmap_flags);
741     swf_SetU16(t, width);
742     swf_SetU16(t, height);
743
744     {
745         z_stream zs;
746
747         memset(&zs, 0x00, sizeof(z_stream));
748         zs.zalloc = Z_NULL;
749         zs.zfree = Z_NULL;
750
751         if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) == Z_OK) {
752             zs.avail_in = bps * height;
753             zs.next_in = (Bytef *)bitmap;
754
755             if (RFXSWF_deflate_wraper(t, &zs, TRUE) < 0)
756                 res = -3;
757             deflateEnd(&zs);
758
759         } else
760             res = -3;           // zlib error
761     }
762     return res;
763 }
764
765 int swf_SetLosslessBitsIndexed(TAG * t, U16 width, U16 height, U8 * bitmap, RGBA * palette, U16 ncolors)
766 {
767     RGBA *pal = palette;
768     int bps = BYTES_PER_SCANLINE(width);
769     int res = 0;
770
771     if (!pal)                   // create default palette for grayscale images
772     {
773         int i;
774         pal = (RGBA*)rfx_alloc(256 * sizeof(RGBA));
775         for (i = 0; i < 256; i++) {
776             pal[i].r = pal[i].g = pal[i].b = i;
777             pal[i].a = 0xff;
778         }
779         ncolors = 256;
780     }
781
782     if ((ncolors < 2) || (ncolors > 256) || (!t)) {
783         fprintf(stderr, "rfxswf: unsupported number of colors: %d\n",
784                 ncolors);
785         return -1;              // parameter error
786     }
787
788     swf_SetU8(t, BMF_8BIT);
789     swf_SetU16(t, width);
790     swf_SetU16(t, height);
791     swf_SetU8(t, ncolors - 1);  // number of pal entries
792
793     {
794         z_stream zs;
795
796         memset(&zs, 0x00, sizeof(z_stream));
797         zs.zalloc = Z_NULL;
798         zs.zfree = Z_NULL;
799
800         if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) == Z_OK) {
801             U8 *zpal;           // compress palette
802             if ((zpal = (U8*)rfx_alloc(ncolors * 4))) {
803                 U8 *pp = zpal;
804                 int i;
805
806                 /* be careful with ST_DEFINEBITSLOSSLESS2, because
807                    the Flash player produces great bugs if you use too many
808                    alpha colors in your palette. The only sensible result that
809                    can be archeived is setting one color to r=0,b=0,g=0,a=0 to
810                    make transparent parts in sprites. That's the cause why alpha
811                    handling is implemented in lossless routines of rfxswf.
812
813                    Indeed: I haven't understood yet how flash player handles
814                    alpha values different from 0 and 0xff in lossless bitmaps...
815                  */
816
817                 if (swf_GetTagID(t) == ST_DEFINEBITSLOSSLESS2)  // have alpha channel?
818                 {
819                     for (i = 0; i < ncolors; i++) {
820                         pp[0] = pal[i].r;
821                         pp[1] = pal[i].g;
822                         pp[2] = pal[i].b;
823                         pp[3] = pal[i].a;
824                         pp += 4;
825                     }
826                     zs.avail_in = 4 * ncolors;
827                 } else {
828                     for (i = 0; i < ncolors; i++)       // pack RGBA structures to RGB 
829                     {
830                         pp[0] = pal[i].r;
831                         pp[1] = pal[i].g;
832                         pp[2] = pal[i].b;
833                         pp += 3;
834                     }
835                     zs.avail_in = 3 * ncolors;
836                 }
837
838                 zs.next_in = zpal;
839
840                 if (RFXSWF_deflate_wraper(t, &zs, FALSE) < 0)
841                     res = -3;
842
843                 // compress bitmap
844                 zs.next_in = bitmap;
845                 zs.avail_in = (bps * height * sizeof(U8));
846
847                 if (RFXSWF_deflate_wraper(t, &zs, TRUE) < 0)
848                     res = -3;
849
850                 deflateEnd(&zs);
851
852                 rfx_free(zpal);
853             } else
854                 res = -2;       // memory error
855         } else
856             res = -3;           // zlib error
857     }
858
859     if (!palette)
860         rfx_free(pal);
861
862     return res;
863 }
864
865 int swf_SetLosslessBitsGrayscale(TAG * t, U16 width, U16 height, U8 * bitmap)
866 {
867     return swf_SetLosslessBitsIndexed(t, width, height, bitmap, NULL, 256);
868 }
869
870 void swf_PreMultiplyAlpha(RGBA*data, int width, int height)
871 {
872     int num = width*height;
873     int t;
874     for(t=0;t<num;t++) {
875         data[t].r = ((int)data[t].r*data[t].a)/255;
876         data[t].g = ((int)data[t].g*data[t].a)/255;
877         data[t].b = ((int)data[t].b*data[t].a)/255;
878     }
879 }
880
881 /* expects mem to be non-premultiplied */
882 void swf_SetLosslessImage(TAG*tag, RGBA*data, int width, int height)
883 {
884     int hasalpha = swf_ImageHasAlpha(data, width, height);
885     int num;
886     if(!hasalpha) {
887         tag->id = ST_DEFINEBITSLOSSLESS;
888     } else {
889         tag->id = ST_DEFINEBITSLOSSLESS2;
890         /* FIXME: we're destroying the callers data here */
891         swf_PreMultiplyAlpha(data, width, height);
892     }
893     num = swf_ImageGetNumberOfPaletteEntries(data, width, height, 0);
894     if(num>1 && num<=256) {
895         RGBA*palette = (RGBA*)malloc(sizeof(RGBA)*num);
896         int width2 = BYTES_PER_SCANLINE(width);
897         U8*data2 = (U8*)malloc(width2*height);
898         int len = width*height;
899         int x,y;
900         int r;
901         swf_ImageGetNumberOfPaletteEntries(data, width, height, palette);
902         for(y=0;y<height;y++) {
903             RGBA*src = &data[width*y];
904             U8*dest = &data2[width2*y];
905             for(x=0;x<width;x++) {
906                 RGBA col = src[x];
907                 for(r=0;r<num;r++) {
908                     if(*(U32*)&col == *(U32*)&palette[r]) {
909                         dest[x] = r;
910                         break;
911                     }
912                 }
913                 if(r==num) {
914                     fprintf(stderr, "Internal error: Couldn't find color %02x%02x%02x%02x in palette (%d entries)\n",
915                             col.r, col.g, col.b, col.a, num);
916                     dest[x] = 0;
917                 }
918             }
919         }
920         swf_SetLosslessBitsIndexed(tag, width, height, data2, palette, num);
921         free(data2);
922         free(palette);
923     } else {
924         swf_SetLosslessBits(tag, width, height, data, BMF_32BIT);
925     }
926 }
927
928 RGBA *swf_DefineLosslessBitsTagToImage(TAG * tag, int *dwidth, int *dheight)
929 {
930     int id, format, height, width, pos;
931     uLongf datalen, datalen2;
932     int error;
933     int bpp = 1;
934     int cols = 0;
935     int pos2 = 0;
936     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
937     int t, x, y;
938     RGBA *palette = 0;
939     U8 *data, *data2;
940     RGBA *dest;
941     if (tag->id != ST_DEFINEBITSLOSSLESS &&
942         tag->id != ST_DEFINEBITSLOSSLESS2) {
943         fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",
944                 GET16(tag->data));
945         return 0;
946     }
947     swf_SetTagPos(tag, 0);
948     id = swf_GetU16(tag);
949     format = swf_GetU8(tag);
950     if (format == 3)
951         bpp = 8;
952     if (format == 4)
953         bpp = 16;
954     if (format == 5)
955         bpp = 32;
956     if (format != 3 && format != 5) {
957         if (format == 4)
958             fprintf(stderr,
959                     "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",
960                     id);
961         else
962             fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n",
963                     format, id);
964         return 0;
965     }
966     *dwidth = width = swf_GetU16(tag);
967     *dheight = height = swf_GetU16(tag);
968
969     dest = (RGBA*)rfx_alloc(sizeof(RGBA) * width * height);
970
971     if (format == 3)
972         cols = swf_GetU8(tag) + 1;
973     else
974         cols = 0;
975
976     data = 0;
977     datalen = (width * height * bpp / 8 + cols * 8);
978     do {
979         if (data)
980             rfx_free(data);
981         datalen += 4096;
982         data = (U8*)rfx_alloc(datalen);
983         error =
984             uncompress(data, &datalen, &tag->data[tag->pos],
985                        tag->len - tag->pos);
986     } while (error == Z_BUF_ERROR);
987     if (error != Z_OK) {
988         fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
989         return 0;
990     }
991     pos = 0;
992
993     if (cols) {
994         palette = (RGBA *) rfx_alloc(cols * sizeof(RGBA));
995         for (t = 0; t < cols; t++) {
996             palette[t].r = data[pos++];
997             palette[t].g = data[pos++];
998             palette[t].b = data[pos++];
999             if (alpha) {
1000                 palette[t].a = data[pos++];
1001             } else {
1002                 palette[t].a = 255;
1003             }
1004         }
1005     }
1006
1007     for (y = 0; y < height; y++) {
1008         int srcwidth = width * (bpp / 8);
1009         if (bpp == 32) {
1010             if (!alpha) {
1011                 // 32 bit to 24 bit "conversion"
1012                 for (x = 0; x < width; x++) {
1013                     dest[pos2].r = data[pos + 1];
1014                     dest[pos2].g = data[pos + 2];
1015                     dest[pos2].b = data[pos + 3];
1016                     dest[pos2].a = 255;
1017                     pos2++;
1018                     pos += 4;   //ignore padding byte
1019                 }
1020             } else {
1021                 for (x = 0; x < width; x++) {
1022                     /* remove premultiplication */
1023                     int alpha = data[pos+0];
1024                     if(alpha)
1025                         alpha = 0xff0000/alpha;
1026                     dest[pos2].r = (data[pos + 1]*alpha)>>16;
1027                     dest[pos2].g = (data[pos + 2]*alpha)>>16;
1028                     dest[pos2].b = (data[pos + 3]*alpha)>>16;
1029                     dest[pos2].a = data[pos + 0];       //alpha
1030                     pos2++;
1031                     pos += 4;
1032                 }
1033             }
1034         } else {
1035             for (x = 0; x < srcwidth; x++) {
1036                 dest[pos2] = palette[data[pos++]];
1037                 pos2++;
1038             }
1039         }
1040         pos += ((srcwidth + 3) & ~3) - srcwidth;        //align
1041     }
1042     if (palette)
1043         rfx_free(palette);
1044     rfx_free(data);
1045     return dest;
1046 }
1047
1048 #endif                          // HAVE_ZLIB
1049
1050 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
1051
1052 /* expects bitmap to be non-premultiplied */
1053 int swf_SetJPEGBits3(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
1054 {
1055     JPEGBITS *jpeg;
1056     int y;
1057     int pos;
1058     int res = 0;
1059     U8 *data;
1060     z_stream zs;
1061
1062     pos = tag->len;
1063     swf_SetU32(tag, 0);         //placeholder
1064     jpeg = swf_SetJPEGBitsStart(tag, width, height, quality);
1065         U8 *scanline = (U8*)rfx_alloc(3 * width);
1066     for (y = 0; y < height; y++) {
1067         int x, p = 0;
1068         for (x = 0; x < width; x++) {
1069             //int ia = bitmap[width*y+x].a;
1070             //if(ia) {
1071             //    /* remove premultiplication */
1072             //    ia = 0xff0000/ia;
1073             //}
1074             //scanline[p++] = (bitmap[width * y + x].r*ia)>>16;
1075             //scanline[p++] = (bitmap[width * y + x].g*ia)>>16;
1076             //scanline[p++] = (bitmap[width * y + x].b*ia)>>16;
1077             scanline[p++] = bitmap[width * y + x].r;
1078             scanline[p++] = bitmap[width * y + x].g;
1079             scanline[p++] = bitmap[width * y + x].b;
1080         }
1081         swf_SetJPEGBitsLine(jpeg, scanline);
1082     }
1083     rfx_free(scanline);
1084     swf_SetJPEGBitsFinish(jpeg);
1085     PUT32(&tag->data[pos], tag->len - pos - 4);
1086
1087     data = (U8*)rfx_alloc(OUTBUFFER_SIZE);
1088     memset(&zs, 0x00, sizeof(z_stream));
1089
1090     if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
1091         fprintf(stderr, "rfxswf: zlib compression failed");
1092         return -3;
1093     }
1094
1095     zs.next_out = data;
1096     zs.avail_out = OUTBUFFER_SIZE;
1097
1098         scanline = (U8*)rfx_alloc(width);
1099     for (y = 0; y < height; y++) {
1100         int x, p = 0;
1101         for (x = 0; x < width; x++) {
1102             scanline[p++] = bitmap[width * y + x].a;
1103         }
1104         zs.avail_in = width;
1105         zs.next_in = scanline;
1106
1107         while (1) {
1108             if (deflate(&zs, Z_NO_FLUSH) != Z_OK) {
1109                 fprintf(stderr, "rfxswf: zlib compression failed");
1110                 return -4;
1111             }
1112             if (zs.next_out != data) {
1113                 swf_SetBlock(tag, data, zs.next_out - data);
1114                 zs.next_out = data;
1115                 zs.avail_out = OUTBUFFER_SIZE;
1116             }
1117             if (!zs.avail_in) {
1118                 break;
1119             }
1120         }
1121     }
1122
1123     rfx_free(scanline);
1124
1125     while (1) {
1126         int ret = deflate(&zs, Z_FINISH);
1127         if (ret != Z_OK && ret != Z_STREAM_END) {
1128             fprintf(stderr, "rfxswf: zlib compression failed");
1129             return -5;
1130         }
1131         if (zs.next_out != data) {
1132             swf_SetBlock(tag, data, zs.next_out - data);
1133             zs.next_out = data;
1134             zs.avail_out = OUTBUFFER_SIZE;
1135         }
1136         if (ret == Z_STREAM_END) {
1137             break;
1138         }
1139     }
1140
1141     deflateEnd(&zs);
1142     rfx_free(data);
1143     return 0;
1144 }
1145
1146 #else
1147 int swf_SetJPEGBits3(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
1148 {
1149     fprintf(stderr, "Error: swftools compiled without jpeglib\n");
1150     return -1;
1151 }
1152 #endif
1153
1154
1155 #define NO_LOSSLESS
1156 /* expects mem to be non-premultiplied */
1157 TAG* swf_AddImage(TAG*tag, int bitid, RGBA*mem, int width, int height, int quality)
1158 {
1159     TAG *tag1 = 0, *tag2 = 0;
1160     int has_alpha = swf_ImageHasAlpha(mem,width,height);
1161
1162     /* try lossless image */
1163
1164 #ifdef NO_LOSSLESS
1165     tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0);
1166     tag1->len = 0x7fffffff;
1167 #else
1168     tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0);
1169     swf_SetU16(tag1, bitid);
1170     swf_SetLosslessImage(tag1, mem, width, height);
1171 #endif
1172
1173 #if defined(HAVE_JPEGLIB)
1174     /* try jpeg image. Notice that if (and only if) we tried the lossless compression
1175        above, the data will now be premultiplied with alpha. */
1176     if(has_alpha) {
1177         tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG3);
1178         swf_SetU16(tag2, bitid);
1179         swf_SetJPEGBits3(tag2, width, height, mem, quality);
1180     } else {
1181         tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG2);
1182         swf_SetU16(tag2, bitid);
1183         swf_SetJPEGBits2(tag2, width, height, mem, quality);
1184     }
1185 #endif
1186
1187     if(quality>100 || !tag2 || (tag1 && tag1->len < tag2->len)) {
1188         /* use the zlib version- it's smaller */
1189         tag1->prev = tag;
1190         if(tag) tag->next = tag1;
1191         tag = tag1;
1192         swf_DeleteTag(0, tag2);
1193     } else {
1194         /* use the jpeg version- it's smaller */
1195         tag2->prev = tag;
1196         if(tag) tag->next = tag2;
1197         tag = tag2;
1198         swf_DeleteTag(0, tag1);
1199     }
1200     return tag;
1201 }
1202
1203 RGBA *swf_ExtractImage(TAG * tag, int *dwidth, int *dheight)
1204 {
1205     RGBA *img;
1206     
1207     swf_SetTagPos(tag, 2); // id is 2 bytes
1208
1209     if (tag->id == ST_DEFINEBITSJPEG ||
1210         tag->id == ST_DEFINEBITSJPEG2 || tag->id == ST_DEFINEBITSJPEG3) {
1211 #ifdef HAVE_JPEGLIB
1212         return swf_JPEG2TagToImage(tag, dwidth, dheight);
1213 #else
1214         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
1215         return 0;
1216 #endif
1217     }
1218     if (tag->id == ST_DEFINEBITSLOSSLESS ||
1219         tag->id == ST_DEFINEBITSLOSSLESS2) {
1220 #ifdef HAVE_ZLIB
1221         return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight);
1222 #else
1223         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
1224         return 0;
1225 #endif
1226     }
1227     fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id,
1228             swf_TagGetName(tag));
1229     return 0;
1230 }
1231
1232 #undef OUTBUFFER_SIZE
1233
1234
1235 void swf_RemoveJPEGTables(SWF * swf)
1236 {
1237     TAG *tag = swf->firstTag;
1238     TAG *tables_tag = 0;
1239     while (tag) {
1240         if (tag->id == ST_JPEGTABLES) {
1241             tables_tag = tag;
1242         }
1243         tag = tag->next;
1244     }
1245
1246     if (!tables_tag)
1247         return;
1248
1249     tag = swf->firstTag;
1250     while (tag) {
1251         if (tag->id == ST_DEFINEBITSJPEG) {
1252             int len = tag->len;
1253             void *data = rfx_alloc(len);
1254             swf_GetBlock(tag, (U8*)data, tag->len);
1255             swf_ResetTag(tag, ST_DEFINEBITSJPEG2);
1256             swf_SetBlock(tag, &((U8*)data)[0], 2); //id
1257             swf_SetBlock(tag, tables_tag->data, tables_tag->len);
1258             swf_SetBlock(tag, &((U8*)data)[2], len-2);
1259             free(data);
1260         }
1261         tag = tag->next;
1262     }
1263     if (swf->firstTag == tables_tag)
1264         swf->firstTag = tables_tag->next;
1265     swf_DeleteTag(swf, tables_tag);
1266 }
1267