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