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