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