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