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