1e32cf2ec7e17c5929b80a2dde9e43bdb976d654
[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         swf_PreMultiplyAlpha(data, width, height);
891     }
892     num = swf_ImageGetNumberOfPaletteEntries(data, width, height, 0);
893     if(num>1 && num<=256) {
894         RGBA*palette = (RGBA*)malloc(sizeof(RGBA)*num);
895         int width2 = BYTES_PER_SCANLINE(width);
896         U8*data2 = (U8*)malloc(width2*height);
897         int len = width*height;
898         int x,y;
899         int r;
900         swf_ImageGetNumberOfPaletteEntries(data, width, height, palette);
901         for(y=0;y<height;y++) {
902             RGBA*src = &data[width*y];
903             U8*dest = &data2[width2*y];
904             for(x=0;x<width;x++) {
905                 RGBA col = src[x];
906                 for(r=0;r<num;r++) {
907                     if(*(U32*)&col == *(U32*)&palette[r]) {
908                         dest[x] = r;
909                         break;
910                     }
911                 }
912                 if(r==num) {
913                     fprintf(stderr, "Internal error: Couldn't find color %02x%02x%02x%02x in palette (%d entries)\n",
914                             col.r, col.g, col.b, col.a, num);
915                     dest[x] = 0;
916                 }
917             }
918         }
919         swf_SetLosslessBitsIndexed(tag, width, height, data2, palette, num);
920         free(data2);
921         free(palette);
922     } else {
923         swf_SetLosslessBits(tag, width, height, data, BMF_32BIT);
924     }
925 }
926
927 RGBA *swf_DefineLosslessBitsTagToImage(TAG * tag, int *dwidth, int *dheight)
928 {
929     int id, format, height, width, pos;
930     uLongf datalen, datalen2;
931     int error;
932     int bpp = 1;
933     int cols = 0;
934     int pos2 = 0;
935     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
936     int t, x, y;
937     RGBA *palette = 0;
938     U8 *data, *data2;
939     RGBA *dest;
940     if (tag->id != ST_DEFINEBITSLOSSLESS &&
941         tag->id != ST_DEFINEBITSLOSSLESS2) {
942         fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",
943                 GET16(tag->data));
944         return 0;
945     }
946     swf_SetTagPos(tag, 0);
947     id = swf_GetU16(tag);
948     format = swf_GetU8(tag);
949     if (format == 3)
950         bpp = 8;
951     if (format == 4)
952         bpp = 16;
953     if (format == 5)
954         bpp = 32;
955     if (format != 3 && format != 5) {
956         if (format == 4)
957             fprintf(stderr,
958                     "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",
959                     id);
960         else
961             fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n",
962                     format, id);
963         return 0;
964     }
965     *dwidth = width = swf_GetU16(tag);
966     *dheight = height = swf_GetU16(tag);
967
968     dest = (RGBA*)rfx_alloc(sizeof(RGBA) * width * height);
969
970     if (format == 3)
971         cols = swf_GetU8(tag) + 1;
972     else
973         cols = 0;
974
975     data = 0;
976     datalen = (width * height * bpp / 8 + cols * 8);
977     do {
978         if (data)
979             rfx_free(data);
980         datalen += 4096;
981         data = (U8*)rfx_alloc(datalen);
982         error =
983             uncompress(data, &datalen, &tag->data[tag->pos],
984                        tag->len - tag->pos);
985     } while (error == Z_BUF_ERROR);
986     if (error != Z_OK) {
987         fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
988         return 0;
989     }
990     pos = 0;
991
992     if (cols) {
993         palette = (RGBA *) rfx_alloc(cols * sizeof(RGBA));
994         for (t = 0; t < cols; t++) {
995             palette[t].r = data[pos++];
996             palette[t].g = data[pos++];
997             palette[t].b = data[pos++];
998             if (alpha) {
999                 palette[t].a = data[pos++];
1000             } else {
1001                 palette[t].a = 255;
1002             }
1003         }
1004     }
1005
1006     for (y = 0; y < height; y++) {
1007         int srcwidth = width * (bpp / 8);
1008         if (bpp == 32) {
1009             if (!alpha) {
1010                 // 32 bit to 24 bit "conversion"
1011                 for (x = 0; x < width; x++) {
1012                     dest[pos2].r = data[pos + 1];
1013                     dest[pos2].g = data[pos + 2];
1014                     dest[pos2].b = data[pos + 3];
1015                     dest[pos2].a = 255;
1016                     pos2++;
1017                     pos += 4;   //ignore padding byte
1018                 }
1019             } else {
1020                 for (x = 0; x < width; x++) {
1021                     /* remove premultiplication */
1022                     int alpha = data[pos+0];
1023                     if(alpha)
1024                         alpha = 0xff0000/alpha;
1025                     dest[pos2].r = (data[pos + 1]*alpha)>>16;
1026                     dest[pos2].g = (data[pos + 2]*alpha)>>16;
1027                     dest[pos2].b = (data[pos + 3]*alpha)>>16;
1028                     dest[pos2].a = data[pos + 0];       //alpha
1029                     pos2++;
1030                     pos += 4;
1031                 }
1032             }
1033         } else {
1034             for (x = 0; x < srcwidth; x++) {
1035                 dest[pos2] = palette[data[pos++]];
1036                 pos2++;
1037             }
1038         }
1039         pos += ((srcwidth + 3) & ~3) - srcwidth;        //align
1040     }
1041     if (palette)
1042         rfx_free(palette);
1043     rfx_free(data);
1044     return dest;
1045 }
1046
1047 #endif                          // HAVE_ZLIB
1048
1049 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
1050
1051 /* expects bitmap to be non-premultiplied */
1052 int swf_SetJPEGBits3(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
1053 {
1054     JPEGBITS *jpeg;
1055     int y;
1056     int pos;
1057     int res = 0;
1058     U8 *data;
1059     z_stream zs;
1060
1061     pos = tag->len;
1062     swf_SetU32(tag, 0);         //placeholder
1063     jpeg = swf_SetJPEGBitsStart(tag, width, height, quality);
1064         U8 *scanline = (U8*)rfx_alloc(3 * width);
1065     for (y = 0; y < height; y++) {
1066         int x, p = 0;
1067         for (x = 0; x < width; x++) {
1068             //int ia = bitmap[width*y+x].a;
1069             //if(ia) {
1070             //    /* remove premultiplication */
1071             //    ia = 0xff0000/ia;
1072             //}
1073             //scanline[p++] = (bitmap[width * y + x].r*ia)>>16;
1074             //scanline[p++] = (bitmap[width * y + x].g*ia)>>16;
1075             //scanline[p++] = (bitmap[width * y + x].b*ia)>>16;
1076             scanline[p++] = bitmap[width * y + x].r;
1077             scanline[p++] = bitmap[width * y + x].g;
1078             scanline[p++] = bitmap[width * y + x].b;
1079         }
1080         swf_SetJPEGBitsLine(jpeg, scanline);
1081     }
1082     rfx_free(scanline);
1083     swf_SetJPEGBitsFinish(jpeg);
1084     PUT32(&tag->data[pos], tag->len - pos - 4);
1085
1086     data = (U8*)rfx_alloc(OUTBUFFER_SIZE);
1087     memset(&zs, 0x00, sizeof(z_stream));
1088
1089     if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
1090         fprintf(stderr, "rfxswf: zlib compression failed");
1091         return -3;
1092     }
1093
1094     zs.next_out = data;
1095     zs.avail_out = OUTBUFFER_SIZE;
1096
1097         scanline = (U8*)rfx_alloc(width);
1098     for (y = 0; y < height; y++) {
1099         int x, p = 0;
1100         for (x = 0; x < width; x++) {
1101             scanline[p++] = bitmap[width * y + x].a;
1102         }
1103         zs.avail_in = width;
1104         zs.next_in = scanline;
1105
1106         while (1) {
1107             if (deflate(&zs, Z_NO_FLUSH) != Z_OK) {
1108                 fprintf(stderr, "rfxswf: zlib compression failed");
1109                 return -4;
1110             }
1111             if (zs.next_out != data) {
1112                 swf_SetBlock(tag, data, zs.next_out - data);
1113                 zs.next_out = data;
1114                 zs.avail_out = OUTBUFFER_SIZE;
1115             }
1116             if (!zs.avail_in) {
1117                 break;
1118             }
1119         }
1120     }
1121
1122     rfx_free(scanline);
1123
1124     while (1) {
1125         int ret = deflate(&zs, Z_FINISH);
1126         if (ret != Z_OK && ret != Z_STREAM_END) {
1127             fprintf(stderr, "rfxswf: zlib compression failed");
1128             return -5;
1129         }
1130         if (zs.next_out != data) {
1131             swf_SetBlock(tag, data, zs.next_out - data);
1132             zs.next_out = data;
1133             zs.avail_out = OUTBUFFER_SIZE;
1134         }
1135         if (ret == Z_STREAM_END) {
1136             break;
1137         }
1138     }
1139
1140     deflateEnd(&zs);
1141     rfx_free(data);
1142     return 0;
1143 }
1144
1145 #else
1146 int swf_SetJPEGBits3(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
1147 {
1148     fprintf(stderr, "Error: swftools compiled without jpeglib\n");
1149     return -1;
1150 }
1151 #endif
1152
1153
1154 /* expects mem to be non-premultiplied */
1155 TAG* swf_AddImage(TAG*tag, int bitid, RGBA*mem, int width, int height, int quality)
1156 {
1157     TAG *tag1 = 0, *tag2 = 0;
1158     int has_alpha = swf_ImageHasAlpha(mem,width,height);
1159
1160     /* try lossless image */
1161
1162 #ifdef NO_LOSSLESS
1163     tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0);
1164     tag1->len = 0x7fffffff;
1165 #else
1166     tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0);
1167     swf_SetU16(tag1, bitid);
1168     swf_SetLosslessImage(tag1, mem, width, height);
1169 #endif
1170
1171 #if defined(HAVE_JPEGLIB)
1172     /* try jpeg image */
1173     if(has_alpha) {
1174         tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG3);
1175         swf_SetU16(tag2, bitid);
1176         swf_SetJPEGBits3(tag2, width, height, mem, quality);
1177     } else {
1178         tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG2);
1179         swf_SetU16(tag2, bitid);
1180         swf_SetJPEGBits2(tag2, width, height, mem, quality);
1181     }
1182 #endif
1183
1184     if(quality>100 || !tag2 || (tag1 && tag1->len < tag2->len)) {
1185         /* use the zlib version- it's smaller */
1186         tag1->prev = tag;
1187         if(tag) tag->next = tag1;
1188         tag = tag1;
1189         swf_DeleteTag(0, tag2);
1190     } else {
1191         /* use the jpeg version- it's smaller */
1192         tag2->prev = tag;
1193         if(tag) tag->next = tag2;
1194         tag = tag2;
1195         swf_DeleteTag(0, tag1);
1196     }
1197     return tag;
1198 }
1199
1200 RGBA *swf_ExtractImage(TAG * tag, int *dwidth, int *dheight)
1201 {
1202     RGBA *img;
1203     
1204     swf_SetTagPos(tag, 2); // id is 2 bytes
1205
1206     if (tag->id == ST_DEFINEBITSJPEG ||
1207         tag->id == ST_DEFINEBITSJPEG2 || tag->id == ST_DEFINEBITSJPEG3) {
1208 #ifdef HAVE_JPEGLIB
1209         return swf_JPEG2TagToImage(tag, dwidth, dheight);
1210 #else
1211         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
1212         return 0;
1213 #endif
1214     }
1215     if (tag->id == ST_DEFINEBITSLOSSLESS ||
1216         tag->id == ST_DEFINEBITSLOSSLESS2) {
1217 #ifdef HAVE_ZLIB
1218         return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight);
1219 #else
1220         fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
1221         return 0;
1222 #endif
1223     }
1224     fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id,
1225             swf_TagGetName(tag));
1226     return 0;
1227 }
1228
1229 #undef OUTBUFFER_SIZE
1230
1231
1232 void swf_RemoveJPEGTables(SWF * swf)
1233 {
1234     TAG *tag = swf->firstTag;
1235     TAG *tables_tag = 0;
1236     while (tag) {
1237         if (tag->id == ST_JPEGTABLES) {
1238             tables_tag = tag;
1239         }
1240         tag = tag->next;
1241     }
1242
1243     if (!tables_tag)
1244         return;
1245
1246     tag = swf->firstTag;
1247     while (tag) {
1248         if (tag->id == ST_DEFINEBITSJPEG) {
1249             int len = tag->len;
1250             void *data = rfx_alloc(len);
1251             swf_GetBlock(tag, (U8*)data, tag->len);
1252             swf_ResetTag(tag, ST_DEFINEBITSJPEG2);
1253             swf_SetBlock(tag, &((U8*)data)[0], 2); //id
1254             swf_SetBlock(tag, tables_tag->data, tables_tag->len);
1255             swf_SetBlock(tag, &((U8*)data)[2], len-2);
1256             free(data);
1257         }
1258         tag = tag->next;
1259     }
1260     if (swf->firstTag == tables_tag)
1261         swf->firstTag = tables_tag->next;
1262     swf_DeleteTag(swf, tables_tag);
1263 }
1264