3 Bitmap functions (needs libjpeg)
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
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.
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.
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 */
27 #include "../../config.h"
44 #endif // HAVE_JPEGLIB
46 #include "../rfxswf.h"
48 #define OUTBUFFER_SIZE 0x8000
50 int swf_ImageHasAlpha(RGBA*img, int width, int height)
52 int len = width*height;
56 if(img[t].a >= 4 && img[t].a < 0xfc)
64 int swf_ImageGetNumberOfPaletteEntries2(RGBA*_img, int width, int height)
66 int len = width*height;
68 U32* img = (U32*)_img;
72 if(img[t] != color1) {
81 if(img[t] != color1 && img[t] != color2) {
88 /*int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette)
90 int len = width*height;
95 int palette_overflow = 0;
98 if(sizeof(RGBA)!=sizeof(U32))
99 fprintf(stderr, "rfxswf: sizeof(RGBA)!=sizeof(U32))");
101 lastcol32 = pal32[palsize++] = *(U32*)&img[0];
105 U32 col32 = *(U32*)&img[t];
109 for(i=0;i<palsize;i++) {
110 if(col32 == pal32[i])
115 palette_overflow = 1;
118 pal32[palsize++] = col32;
125 memcpy(palette, pal, palsize*sizeof(RGBA));
129 int swf_ImageGetNumberOfPaletteEntries(RGBA*img, int width, int height, RGBA*palette)
131 int len = width*height;
136 int palette_overflow = 0;
139 pal = (U32*)malloc(65536*sizeof(U32));
141 memset(size, 0, sizeof(size));
143 if(sizeof(RGBA)!=sizeof(U32))
144 fprintf(stderr, "rfxswf: sizeof(RGBA)!=sizeof(U32))");
146 lastcol32 = (*(U32*)&img[0])^0xffffffff; // don't match
150 U32 col32 = *(U32*)&img[t];
155 if(col32 == lastcol32)
157 hash = (col32 >> 17) ^ col32;
158 hash ^= ((hash>>8) + 1) ^ hash;
162 cpal = &pal[hash*256];
163 for(i=0;i<csize;i++) {
169 palette_overflow = 1;
172 cpal[size[hash]++] = col32;
177 if(palette_overflow) {
186 U32* cpal = &pal[t*256];
188 palette[i++] = *(RGBA*)(&cpal[s]);
199 typedef struct _JPEGDESTMGR {
200 struct jpeg_destination_mgr mgr;
203 struct jpeg_compress_struct cinfo;
204 struct jpeg_error_mgr jerr;
205 } JPEGDESTMGR, *LPJPEGDESTMGR;
207 // Destination manager callbacks
209 static void RFXSWF_init_destination(j_compress_ptr cinfo)
211 JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest;
212 dmgr->buffer = (JOCTET *) rfx_alloc(OUTBUFFER_SIZE);
213 dmgr->mgr.next_output_byte = dmgr->buffer;
214 dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
217 static boolean RFXSWF_empty_output_buffer(j_compress_ptr cinfo)
219 JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest;
220 swf_SetBlock(dmgr->t, (U8 *) dmgr->buffer, OUTBUFFER_SIZE);
221 dmgr->mgr.next_output_byte = dmgr->buffer;
222 dmgr->mgr.free_in_buffer = OUTBUFFER_SIZE;
226 static void RFXSWF_term_destination(j_compress_ptr cinfo)
228 JPEGDESTMGR *dmgr = (JPEGDESTMGR *) cinfo->dest;
229 swf_SetBlock(dmgr->t, (U8 *) dmgr->buffer,
230 OUTBUFFER_SIZE - dmgr->mgr.free_in_buffer);
231 rfx_free(dmgr->buffer);
232 dmgr->mgr.free_in_buffer = 0;
235 JPEGBITS *swf_SetJPEGBitsStart(TAG * t, int width, int height, int quality)
239 // redirect compression lib output to local SWF Tag structure
241 jpeg = (JPEGDESTMGR *) rfx_calloc(sizeof(JPEGDESTMGR));
243 jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
245 jpeg_create_compress(&jpeg->cinfo);
247 jpeg->mgr.init_destination = RFXSWF_init_destination;
248 jpeg->mgr.empty_output_buffer = RFXSWF_empty_output_buffer;
249 jpeg->mgr.term_destination = RFXSWF_term_destination;
253 jpeg->cinfo.dest = (struct jpeg_destination_mgr *) jpeg;
257 jpeg->cinfo.image_width = width;
258 jpeg->cinfo.image_height = height;
259 jpeg->cinfo.input_components = 3;
260 jpeg->cinfo.in_color_space = JCS_RGB;
262 jpeg_set_defaults(&jpeg->cinfo);
263 jpeg_set_quality(&jpeg->cinfo, quality, TRUE);
265 // write tables to SWF
267 jpeg_write_tables(&jpeg->cinfo);
269 // compess image to SWF
271 jpeg_suppress_tables(&jpeg->cinfo, TRUE);
272 jpeg_start_compress(&jpeg->cinfo, FALSE);
274 return (JPEGBITS *) jpeg;
277 int swf_SetJPEGBitsLines(JPEGBITS * jpegbits, U8 ** data, int n)
279 JPEGDESTMGR *jpeg = (JPEGDESTMGR *) jpegbits;
282 jpeg_write_scanlines(&jpeg->cinfo, data, n);
286 int swf_SetJPEGBitsLine(JPEGBITS * jpegbits, U8 * data)
288 return swf_SetJPEGBitsLines(jpegbits, &data, 1);
291 int swf_SetJPEGBitsFinish(JPEGBITS * jpegbits)
293 JPEGDESTMGR *jpeg = (JPEGDESTMGR *) jpegbits;
296 jpeg_finish_compress(&jpeg->cinfo);
297 jpeg_destroy_compress(&jpeg->cinfo);
302 #if defined(HAVE_JPEGLIB)
303 void swf_SetJPEGBits2(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
307 jpeg = swf_SetJPEGBitsStart(tag, width, height, quality);
308 U8 *scanline = (U8*)rfx_alloc(3 * width);
309 for (y = 0; y < height; y++) {
311 for (x = 0; x < width; x++) {
312 scanline[p++] = bitmap[width * y + x].r;
313 scanline[p++] = bitmap[width * y + x].g;
314 scanline[p++] = bitmap[width * y + x].b;
316 swf_SetJPEGBitsLine(jpeg, scanline);
319 swf_SetJPEGBitsFinish(jpeg);
322 void swf_SetJPEGBits2(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
324 fprintf(stderr, "Error: swftools compiled without jpeglib\n");
329 void swf_GetJPEGSize(char *fname, int *width, int *height)
331 struct jpeg_decompress_struct cinfo;
332 struct jpeg_error_mgr jerr;
336 cinfo.err = jpeg_std_error(&jerr);
337 jpeg_create_decompress(&cinfo);
338 if ((fi = fopen(fname, "rb")) == NULL) {
339 fprintf(stderr, "rfxswf: file open error\n");
342 jpeg_stdio_src(&cinfo, fi);
343 jpeg_read_header(&cinfo, TRUE);
344 *width = cinfo.image_width;
345 *height = cinfo.image_height;
346 jpeg_destroy_decompress(&cinfo);
350 int swf_SetJPEGBits(TAG * t, char *fname, int quality)
352 struct jpeg_decompress_struct cinfo;
353 struct jpeg_error_mgr jerr;
358 cinfo.err = jpeg_std_error(&jerr);
359 jpeg_create_decompress(&cinfo);
361 if ((f = fopen(fname, "rb")) == NULL) {
362 fprintf(stderr, "rfxswf: file open error\n");
366 jpeg_stdio_src(&cinfo, f);
367 jpeg_read_header(&cinfo, TRUE);
368 jpeg_start_decompress(&cinfo);
371 swf_SetJPEGBitsStart(t, cinfo.output_width, cinfo.output_height,
373 scanline = (U8 *) rfx_alloc(4 * cinfo.output_width);
378 if (cinfo.out_color_space == JCS_GRAYSCALE) {
379 for (y = 0; y < cinfo.output_height; y++) {
381 jpeg_read_scanlines(&cinfo, &js, 1);
382 for (x = cinfo.output_width - 1; x >= 0; x--) {
383 js[x * 3] = js[x * 3 + 1] = js[x * 3 + 2] = js[x];
385 swf_SetJPEGBitsLines(out, (U8 **) & js, 1);
387 } else if (cinfo.out_color_space == JCS_RGB) {
388 for (y = 0; y < cinfo.output_height; y++) {
389 jpeg_read_scanlines(&cinfo, &js, 1);
390 swf_SetJPEGBitsLines(out, (U8 **) & js, 1);
392 } else if (cinfo.out_color_space == JCS_YCCK) {
394 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
396 } else if (cinfo.out_color_space == JCS_YCbCr) {
397 for (y = 0; y < cinfo.output_height; y++) {
399 for (x = 0; x < cinfo.output_width; x++) {
400 int y = js[x * 3 + 0];
401 int u = js[x * 3 + 1];
402 int v = js[x * 3 + 1];
403 js[x * 3 + 0] = y + ((360 * (v - 128)) >> 8);
405 y - ((88 * (u - 128) + 183 * (v - 128)) >> 8);
406 js[x * 3 + 2] = y + ((455 * (u - 128)) >> 8);
409 } else if (cinfo.out_color_space == JCS_CMYK) {
410 for (y = 0; y < cinfo.output_height; y++) {
412 jpeg_read_scanlines(&cinfo, &js, 1);
413 /* This routine seems to work for now-
414 It's a mixture of 3 different
415 CMYK->RGB conversion routines I found in the
416 web. (which all produced garbage)
417 I'm happily accepting suggestions. (mk) */
418 for (x = 0; x < cinfo.output_width; x++) {
419 int white = 255 - js[x * 4 + 3];
420 js[x * 3 + 0] = white - ((js[x * 4] * white) >> 8);
421 js[x * 3 + 1] = white - ((js[x * 4 + 1] * white) >> 8);
422 js[x * 3 + 2] = white - ((js[x * 4 + 2] * white) >> 8);
424 swf_SetJPEGBitsLines(out, (U8 **) & js, 1);
430 swf_SetJPEGBitsFinish(out);
431 jpeg_finish_decompress(&cinfo);
437 typedef struct _JPEGFILEMGR {
438 struct jpeg_destination_mgr mgr;
440 struct jpeg_compress_struct* cinfo;
441 struct jpeg_error_mgr* jerr;
445 static void file_init_destination(j_compress_ptr cinfo)
447 JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest);
448 struct jpeg_destination_mgr*dmgr = &fmgr->mgr;
450 fmgr->buffer = (JOCTET*)rfx_alloc(OUTBUFFER_SIZE);
453 fprintf(stderr, "Out of memory!\n");
457 dmgr->next_output_byte = fmgr->buffer;
458 dmgr->free_in_buffer = OUTBUFFER_SIZE;
461 static boolean file_empty_output_buffer(j_compress_ptr cinfo)
463 JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest);
464 struct jpeg_destination_mgr*dmgr = &fmgr->mgr;
467 fwrite(fmgr->buffer, OUTBUFFER_SIZE, 1, fmgr->fi);
469 dmgr->next_output_byte = fmgr->buffer;
470 dmgr->free_in_buffer = OUTBUFFER_SIZE;
474 static void file_term_destination(j_compress_ptr cinfo)
476 JPEGFILEMGR*fmgr = (JPEGFILEMGR*)(cinfo->dest);
477 struct jpeg_destination_mgr*dmgr = &fmgr->mgr;
480 fwrite(fmgr->buffer, OUTBUFFER_SIZE-dmgr->free_in_buffer, 1, fmgr->fi);
482 rfx_free(fmgr->buffer);
484 dmgr->free_in_buffer = 0;
485 dmgr->next_output_byte = 0;
488 void swf_SaveJPEG(char*filename, RGBA*pixels, int width, int height, int quality)
491 struct jpeg_compress_struct cinfo;
492 struct jpeg_error_mgr jerr;
493 unsigned char*data2 = 0;
496 FILE*fi = fopen(filename, "wb");
499 sprintf(buf, "rfxswf: Couldn't create %s", filename);
503 data2 = (unsigned char *)rfx_calloc(width*3);
505 memset(&cinfo, 0, sizeof(cinfo));
506 memset(&jerr, 0, sizeof(jerr));
507 memset(&fmgr, 0, sizeof(fmgr));
508 cinfo.err = jpeg_std_error(&jerr);
509 jpeg_create_compress(&cinfo);
511 fmgr.mgr.init_destination = file_init_destination;
512 fmgr.mgr.empty_output_buffer = file_empty_output_buffer;
513 fmgr.mgr.term_destination = file_term_destination;
517 cinfo.dest = (struct jpeg_destination_mgr*)&fmgr;
521 cinfo.image_width = width;
522 cinfo.image_height = height;
523 cinfo.input_components = 3;
524 cinfo.in_color_space = JCS_RGB;
525 jpeg_set_defaults(&cinfo);
526 cinfo.dct_method = JDCT_IFAST;
527 jpeg_set_quality(&cinfo,quality,TRUE);
529 //jpeg_write_tables(&cinfo);
530 //jpeg_suppress_tables(&cinfo, TRUE);
531 jpeg_start_compress(&cinfo, FALSE);
533 for(y=0;y<height;y++) {
535 RGBA*src = &pixels[y*width];
536 for(x=0;x<width;x++) {
537 data2[x*3+0] = src[x].r;
538 data2[x*3+1] = src[x].g;
539 data2[x*3+2] = src[x].b;
541 jpeg_write_scanlines(&cinfo, &data2, 1);
544 jpeg_finish_compress(&cinfo);
545 jpeg_destroy_compress(&cinfo);
550 /* jpeg_source_mgr functions */
551 static void tag_init_source(struct jpeg_decompress_struct *cinfo)
553 TAG *tag = (TAG *) cinfo->client_data;
554 if (tag->id == ST_DEFINEBITSJPEG3) {
555 swf_SetTagPos(tag, 6);
557 swf_SetTagPos(tag, 2);
559 cinfo->src->bytes_in_buffer = 0;
561 static boolean tag_fill_input_buffer(struct jpeg_decompress_struct *cinfo)
563 TAG *tag = (TAG *) cinfo->client_data;
564 if (tag->data[tag->pos + 0] == 0xff &&
565 tag->data[tag->pos + 1] == 0xd9 &&
566 tag->data[tag->pos + 2] == 0xff &&
567 tag->data[tag->pos + 3] == 0xd8) {
570 if (tag->pos >= tag->len) {
571 cinfo->src->next_input_byte = 0;
572 cinfo->src->bytes_in_buffer = 0;
575 cinfo->src->next_input_byte = &tag->data[tag->pos];
576 cinfo->src->bytes_in_buffer = 1; //tag->len - tag->pos;
580 static void tag_skip_input_data(struct jpeg_decompress_struct *cinfo, long count)
582 TAG *tag = (TAG *) cinfo->client_data;
583 cinfo->src->next_input_byte = 0;
584 cinfo->src->bytes_in_buffer = 0;
587 static boolean tag_resync_to_restart(struct jpeg_decompress_struct *cinfo, int desired)
589 return jpeg_resync_to_restart(cinfo, desired);
591 static void tag_term_source(struct jpeg_decompress_struct *cinfo)
593 TAG *tag = (TAG *) cinfo->client_data;
595 RGBA *swf_JPEG2TagToImage(TAG * tag, int *width, int *height)
597 struct jpeg_decompress_struct cinfo;
598 struct jpeg_error_mgr jerr;
599 struct jpeg_source_mgr mgr;
607 if (tag->id == ST_DEFINEBITSJPEG) {
608 fprintf(stderr, "rfxswf: extracting from definebitsjpeg not yet supported\n");
611 if (tag->id == ST_DEFINEBITSJPEG3) {
613 offset = swf_GetU32(tag);
614 oldtaglen = tag->len;
617 fprintf(stderr, "rfxswf: extracting from definebitsjpeg3 not possible: no zlib\n");
622 cinfo.err = jpeg_std_error(&jerr);
623 jpeg_create_decompress(&cinfo);
625 cinfo.client_data = (void *) tag;
627 cinfo.src->init_source = tag_init_source;
628 cinfo.src->fill_input_buffer = tag_fill_input_buffer;
629 cinfo.src->skip_input_data = tag_skip_input_data;
630 cinfo.src->resync_to_restart = jpeg_resync_to_restart;
631 cinfo.src->term_source = tag_term_source;
632 cinfo.out_color_space = JCS_RGB;
634 jpeg_read_header(&cinfo, TRUE);
635 *width = cinfo.image_width;
636 *height = cinfo.image_height;
638 rfx_alloc(sizeof(RGBA) * cinfo.image_width * cinfo.image_height);
640 jpeg_start_decompress(&cinfo);
641 for (y = 0; y < cinfo.output_height; y++) {
642 RGBA *line = &dest[y * cinfo.image_width];
643 U8 *to = (U8 *) line;
645 jpeg_read_scanlines(&cinfo, &to, 1);
646 for (x = cinfo.output_width - 1; x >= 0; --x) {
647 int r = to[x * 3 + 0];
648 int g = to[x * 3 + 1];
649 int b = to[x * 3 + 2];
657 jpeg_finish_decompress(&cinfo);
659 jpeg_destroy_decompress(&cinfo);
663 uLongf datalen = cinfo.output_width*cinfo.output_height;
664 U8* alphadata = (U8*)rfx_alloc(datalen);
666 tag->len = oldtaglen;
667 swf_SetTagPos(tag, 6+offset);
668 error = uncompress(alphadata, &datalen, &tag->data[tag->pos], tag->len - tag->pos);
670 fprintf(stderr, "rfxswf: Zlib error %d while extracting definejpeg3\n", error);
673 for(y=0;y<cinfo.output_height;y++) {
674 RGBA*line = &dest[y*cinfo.output_width];
675 U8*aline = &alphadata[y*cinfo.output_width];
677 for(x=0;x<cinfo.output_width;x++) {
678 line[x].a = aline[x];
687 #endif // HAVE_JPEGLIB
689 // Lossless compression texture based on zlib
693 int RFXSWF_deflate_wraper(TAG * t, z_stream * zs, boolean finish)
695 U8 *data = (U8*)rfx_alloc(OUTBUFFER_SIZE);
697 zs->avail_out = OUTBUFFER_SIZE;
699 int status = deflate(zs, Z_NO_FLUSH);
701 if (status != Z_OK) {
702 fprintf(stderr, "rfxswf: zlib compression error (%i)\n", status);
707 if (zs->next_out != data) {
708 swf_SetBlock(t, data, zs->next_out - data);
710 zs->avail_out = OUTBUFFER_SIZE;
713 if (zs->avail_in == 0)
723 int status = deflate(zs, Z_FINISH);
724 if (status != Z_OK && status != Z_STREAM_END) {
725 fprintf(stderr, "rfxswf: zlib compression error (%i)\n", status);
730 if (zs->next_out != data) {
731 swf_SetBlock(t, data, zs->next_out - data);
733 zs->avail_out = OUTBUFFER_SIZE;
736 if (status == Z_STREAM_END)
744 int swf_SetLosslessBits(TAG * t, U16 width, U16 height, void *bitmap, U8 bitmap_flags)
749 switch (bitmap_flags) {
751 return swf_SetLosslessBitsIndexed(t, width, height, (U8*)bitmap, NULL, 256);
753 bps = BYTES_PER_SCANLINE(sizeof(U16) * width);
759 fprintf(stderr, "rfxswf: unknown bitmap type %d\n", bitmap_flags);
763 swf_SetU8(t, bitmap_flags);
764 swf_SetU16(t, width);
765 swf_SetU16(t, height);
770 memset(&zs, 0x00, sizeof(z_stream));
774 if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) == Z_OK) {
775 zs.avail_in = bps * height;
776 zs.next_in = (Bytef *)bitmap;
778 if (RFXSWF_deflate_wraper(t, &zs, TRUE) < 0)
783 res = -3; // zlib error
788 int swf_SetLosslessBitsIndexed(TAG * t, U16 width, U16 height, U8 * bitmap, RGBA * palette, U16 ncolors)
791 int bps = BYTES_PER_SCANLINE(width);
794 if (!pal) // create default palette for grayscale images
797 pal = (RGBA*)rfx_alloc(256 * sizeof(RGBA));
798 for (i = 0; i < 256; i++) {
799 pal[i].r = pal[i].g = pal[i].b = i;
805 if ((ncolors < 2) || (ncolors > 256) || (!t)) {
806 fprintf(stderr, "rfxswf: unsupported number of colors: %d\n",
808 return -1; // parameter error
811 swf_SetU8(t, BMF_8BIT);
812 swf_SetU16(t, width);
813 swf_SetU16(t, height);
814 swf_SetU8(t, ncolors - 1); // number of pal entries
819 memset(&zs, 0x00, sizeof(z_stream));
823 if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) == Z_OK) {
824 U8 *zpal; // compress palette
825 if ((zpal = (U8*)rfx_alloc(ncolors * 4))) {
829 /* be careful with ST_DEFINEBITSLOSSLESS2, because
830 the Flash player produces great bugs if you use too many
831 alpha colors in your palette. The only sensible result that
832 can be archeived is setting one color to r=0,b=0,g=0,a=0 to
833 make transparent parts in sprites. That's the cause why alpha
834 handling is implemented in lossless routines of rfxswf.
836 Indeed: I haven't understood yet how flash player handles
837 alpha values different from 0 and 0xff in lossless bitmaps...
840 if (swf_GetTagID(t) == ST_DEFINEBITSLOSSLESS2) // have alpha channel?
842 for (i = 0; i < ncolors; i++) {
849 zs.avail_in = 4 * ncolors;
851 for (i = 0; i < ncolors; i++) // pack RGBA structures to RGB
858 zs.avail_in = 3 * ncolors;
863 if (RFXSWF_deflate_wraper(t, &zs, FALSE) < 0)
868 zs.avail_in = (bps * height * sizeof(U8));
870 if (RFXSWF_deflate_wraper(t, &zs, TRUE) < 0)
877 res = -2; // memory error
879 res = -3; // zlib error
888 int swf_SetLosslessBitsGrayscale(TAG * t, U16 width, U16 height, U8 * bitmap)
890 return swf_SetLosslessBitsIndexed(t, width, height, bitmap, NULL, 256);
893 void swf_PreMultiplyAlpha(RGBA*data, int width, int height)
895 int num = width*height;
898 data[t].r = ((int)data[t].r*data[t].a)/255;
899 data[t].g = ((int)data[t].g*data[t].a)/255;
900 data[t].b = ((int)data[t].b*data[t].a)/255;
904 /* expects mem to be non-premultiplied */
905 void swf_SetLosslessImage(TAG*tag, RGBA*data, int width, int height)
907 int hasalpha = swf_ImageHasAlpha(data, width, height);
910 tag->id = ST_DEFINEBITSLOSSLESS;
912 tag->id = ST_DEFINEBITSLOSSLESS2;
913 swf_PreMultiplyAlpha(data, width, height);
915 num = swf_ImageGetNumberOfPaletteEntries(data, width, height, 0);
916 if(num>1 && num<=256) {
917 RGBA*palette = (RGBA*)malloc(sizeof(RGBA)*num);
918 int width2 = BYTES_PER_SCANLINE(width);
919 U8*data2 = (U8*)malloc(width2*height);
920 int len = width*height;
923 swf_ImageGetNumberOfPaletteEntries(data, width, height, palette);
924 for(y=0;y<height;y++) {
925 RGBA*src = &data[width*y];
926 U8*dest = &data2[width2*y];
927 for(x=0;x<width;x++) {
930 if(*(U32*)&col == *(U32*)&palette[r]) {
936 fprintf(stderr, "Internal error: Couldn't find color %02x%02x%02x%02x in palette (%d entries)\n",
937 col.r, col.g, col.b, col.a, num);
942 swf_SetLosslessBitsIndexed(tag, width, height, data2, palette, num);
946 swf_SetLosslessBits(tag, width, height, data, BMF_32BIT);
950 RGBA *swf_DefineLosslessBitsTagToImage(TAG * tag, int *dwidth, int *dheight)
952 int id, format, height, width, pos;
953 uLongf datalen, datalen2;
958 char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
963 if (tag->id != ST_DEFINEBITSLOSSLESS &&
964 tag->id != ST_DEFINEBITSLOSSLESS2) {
965 fprintf(stderr, "rfxswf: Object %d is not a PNG picture!\n",
969 swf_SetTagPos(tag, 0);
970 id = swf_GetU16(tag);
971 format = swf_GetU8(tag);
978 if (format != 3 && format != 5) {
981 "rfxswf: Can't handle 16-bit palette images yet (image %d)\n",
984 fprintf(stderr, "rfxswf: Unknown image type %d in image %d\n",
988 *dwidth = width = swf_GetU16(tag);
989 *dheight = height = swf_GetU16(tag);
991 dest = (RGBA*)rfx_alloc(sizeof(RGBA) * width * height);
994 cols = swf_GetU8(tag) + 1;
999 datalen = (width * height * bpp / 8 + cols * 8);
1004 data = (U8*)rfx_alloc(datalen);
1006 uncompress(data, &datalen, &tag->data[tag->pos],
1007 tag->len - tag->pos);
1008 } while (error == Z_BUF_ERROR);
1009 if (error != Z_OK) {
1010 fprintf(stderr, "rfxswf: Zlib error %d (image %d)\n", error, id);
1016 palette = (RGBA *) rfx_alloc(cols * sizeof(RGBA));
1017 for (t = 0; t < cols; t++) {
1018 palette[t].r = data[pos++];
1019 palette[t].g = data[pos++];
1020 palette[t].b = data[pos++];
1022 palette[t].a = data[pos++];
1029 for (y = 0; y < height; y++) {
1030 int srcwidth = width * (bpp / 8);
1033 // 32 bit to 24 bit "conversion"
1034 for (x = 0; x < width; x++) {
1035 dest[pos2].r = data[pos + 1];
1036 dest[pos2].g = data[pos + 2];
1037 dest[pos2].b = data[pos + 3];
1040 pos += 4; //ignore padding byte
1043 for (x = 0; x < width; x++) {
1044 /* remove premultiplication */
1045 int alpha = data[pos+0];
1047 alpha = 0xff0000/alpha;
1048 dest[pos2].r = (data[pos + 1]*alpha)>>16;
1049 dest[pos2].g = (data[pos + 2]*alpha)>>16;
1050 dest[pos2].b = (data[pos + 3]*alpha)>>16;
1051 dest[pos2].a = data[pos + 0]; //alpha
1057 for (x = 0; x < srcwidth; x++) {
1058 dest[pos2] = palette[data[pos++]];
1062 pos += ((srcwidth + 3) & ~3) - srcwidth; //align
1072 #if defined(HAVE_ZLIB) && defined(HAVE_JPEGLIB)
1074 /* expects bitmap to be non-premultiplied */
1075 int swf_SetJPEGBits3(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
1085 swf_SetU32(tag, 0); //placeholder
1086 jpeg = swf_SetJPEGBitsStart(tag, width, height, quality);
1087 U8 *scanline = (U8*)rfx_alloc(3 * width);
1088 for (y = 0; y < height; y++) {
1090 for (x = 0; x < width; x++) {
1091 //int ia = bitmap[width*y+x].a;
1093 // /* remove premultiplication */
1094 // ia = 0xff0000/ia;
1096 //scanline[p++] = (bitmap[width * y + x].r*ia)>>16;
1097 //scanline[p++] = (bitmap[width * y + x].g*ia)>>16;
1098 //scanline[p++] = (bitmap[width * y + x].b*ia)>>16;
1099 scanline[p++] = bitmap[width * y + x].r;
1100 scanline[p++] = bitmap[width * y + x].g;
1101 scanline[p++] = bitmap[width * y + x].b;
1103 swf_SetJPEGBitsLine(jpeg, scanline);
1106 swf_SetJPEGBitsFinish(jpeg);
1107 PUT32(&tag->data[pos], tag->len - pos - 4);
1109 data = (U8*)rfx_alloc(OUTBUFFER_SIZE);
1110 memset(&zs, 0x00, sizeof(z_stream));
1112 if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
1113 fprintf(stderr, "rfxswf: zlib compression failed");
1118 zs.avail_out = OUTBUFFER_SIZE;
1120 scanline = (U8*)rfx_alloc(width);
1121 for (y = 0; y < height; y++) {
1123 for (x = 0; x < width; x++) {
1124 scanline[p++] = bitmap[width * y + x].a;
1126 zs.avail_in = width;
1127 zs.next_in = scanline;
1130 if (deflate(&zs, Z_NO_FLUSH) != Z_OK) {
1131 fprintf(stderr, "rfxswf: zlib compression failed");
1134 if (zs.next_out != data) {
1135 swf_SetBlock(tag, data, zs.next_out - data);
1137 zs.avail_out = OUTBUFFER_SIZE;
1148 int ret = deflate(&zs, Z_FINISH);
1149 if (ret != Z_OK && ret != Z_STREAM_END) {
1150 fprintf(stderr, "rfxswf: zlib compression failed");
1153 if (zs.next_out != data) {
1154 swf_SetBlock(tag, data, zs.next_out - data);
1156 zs.avail_out = OUTBUFFER_SIZE;
1158 if (ret == Z_STREAM_END) {
1169 int swf_SetJPEGBits3(TAG * tag, U16 width, U16 height, RGBA * bitmap, int quality)
1171 fprintf(stderr, "Error: swftools compiled without jpeglib\n");
1177 /* expects mem to be non-premultiplied */
1178 TAG* swf_AddImage(TAG*tag, int bitid, RGBA*mem, int width, int height, int quality)
1180 TAG *tag1 = 0, *tag2 = 0;
1181 int has_alpha = swf_ImageHasAlpha(mem,width,height);
1183 /* try lossless image */
1184 tag1 = swf_InsertTag(0, /*ST_DEFINEBITSLOSSLESS1/2*/0);
1185 swf_SetU16(tag1, bitid);
1186 swf_SetLosslessImage(tag1, mem, width, height);
1188 /* try jpeg image */
1190 tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG3);
1191 swf_SetU16(tag2, bitid);
1192 swf_SetJPEGBits3(tag2, width, height, mem, quality);
1194 tag2 = swf_InsertTag(0, ST_DEFINEBITSJPEG2);
1195 swf_SetU16(tag2, bitid);
1196 swf_SetJPEGBits2(tag2, width, height, mem, quality);
1199 if(quality>100 || (tag1 && tag1->len < tag2->len)) {
1200 /* use the zlib version- it's smaller */
1202 if(tag) tag->next = tag1;
1204 swf_DeleteTag(0, tag2);
1206 /* use the jpeg version- it's smaller */
1208 if(tag) tag->next = tag2;
1210 swf_DeleteTag(0, tag1);
1215 RGBA *swf_ExtractImage(TAG * tag, int *dwidth, int *dheight)
1219 swf_SetTagPos(tag, 2); // id is 2 bytes
1221 if (tag->id == ST_DEFINEBITSJPEG ||
1222 tag->id == ST_DEFINEBITSJPEG2 || tag->id == ST_DEFINEBITSJPEG3) {
1224 return swf_JPEG2TagToImage(tag, dwidth, dheight);
1226 fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
1230 if (tag->id == ST_DEFINEBITSLOSSLESS ||
1231 tag->id == ST_DEFINEBITSLOSSLESS2) {
1233 return swf_DefineLosslessBitsTagToImage(tag, dwidth, dheight);
1235 fprintf(stderr, "rfxswf: Error: No JPEG library compiled in");
1239 fprintf(stderr, "rfxswf: Error: Invalid tag (%d, %s)", tag->id,
1240 swf_TagGetName(tag));
1244 #undef OUTBUFFER_SIZE
1247 void swf_RemoveJPEGTables(SWF * swf)
1249 TAG *tag = swf->firstTag;
1250 TAG *tables_tag = 0;
1252 if (tag->id == ST_JPEGTABLES) {
1261 tag = swf->firstTag;
1263 if (tag->id == ST_DEFINEBITSJPEG) {
1265 void *data = rfx_alloc(len);
1266 swf_GetBlock(tag, (U8*)data, tag->len);
1267 swf_ResetTag(tag, ST_DEFINEBITSJPEG2);
1268 swf_SetBlock(tag, &((U8*)data)[0], 2); //id
1269 swf_SetBlock(tag, tables_tag->data, tables_tag->len);
1270 swf_SetBlock(tag, &((U8*)data)[2], len-2);
1275 if (swf->firstTag == tables_tag)
1276 swf->firstTag = tables_tag->next;
1277 swf_DeleteTag(swf, tables_tag);
1280 typedef struct scale_lookup {
1282 unsigned int weight;
1285 typedef struct rgba_int {
1286 unsigned int r,g,b,a;
1289 static int bicubic = 0;
1291 static scale_lookup_t**make_scale_lookup(int width, int newwidth)
1293 scale_lookup_t*lookupx = (scale_lookup_t*)malloc((width>newwidth?width:newwidth)*2*sizeof(scale_lookup_t));
1294 scale_lookup_t**lblockx = (scale_lookup_t**)malloc((newwidth+1)*sizeof(scale_lookup_t**));
1295 double fx = ((double)width)/((double)newwidth);
1298 scale_lookup_t*p_x = lookupx;
1300 if(newwidth<=width) {
1301 for(x=0;x<newwidth;x++) {
1302 double ex = px + fx;
1303 int fromx = (int)px;
1305 double rem = fromx+1-px;
1306 int i = (int)(256/fx);
1307 int xweight = (int)(rem*256/fx);
1311 if(tox>=width) tox = width-1;
1312 for(xx=fromx;xx<=tox;xx++) {
1313 if(xx==fromx && xx==tox) p_x->weight = 256;
1314 else if(xx==fromx) p_x->weight = xweight;
1315 else if(xx==tox) p_x->weight = 256-w;
1316 else p_x->weight = i;
1324 for(x=0;x<newwidth;x++) {
1326 int ix2 = ((int)px)+1;
1328 if(ix2>=width) ix2=width-1;
1332 p_x[0].weight = (int)(256*(1-r));
1334 p_x[1].weight = 256-p_x[0].weight;
1340 lblockx[newwidth] = p_x;
1344 static void encodeMonochromeImage(RGBA*data, int width, int height, RGBA*colors)
1347 int len = width*height;
1349 U32* img = (U32*)data;
1350 U32 color1 = img[0];
1352 for(t=1;t<len;t++) {
1353 if(img[t] != color1) {
1358 *(U32*)&colors[0] = color1;
1359 *(U32*)&colors[1] = color2;
1360 for(t=0;t<len;t++) {
1361 if(img[t] == color1) {
1364 img[t] = 0xffffffff;
1369 static void decodeMonochromeImage(RGBA*data, int width, int height, RGBA*colors)
1372 int len = width*height;
1374 for(t=0;t<len;t++) {
1376 data[t].r = (colors[0].r * (255-m) + colors[1].r * m) >> 8;
1377 data[t].g = (colors[0].g * (255-m) + colors[1].g * m) >> 8;
1378 data[t].b = (colors[0].b * (255-m) + colors[1].b * m) >> 8;
1379 data[t].a = (colors[0].a * (255-m) + colors[1].a * m) >> 8;
1383 RGBA* swf_ImageScale(RGBA*data, int width, int height, int newwidth, int newheight)
1387 scale_lookup_t *p, **lblockx,**lblocky;
1390 RGBA monochrome_colors[2];
1392 if(newwidth<1 || newheight<1)
1395 if(swf_ImageGetNumberOfPaletteEntries2(data, width, height) == 2) {
1397 encodeMonochromeImage(data, width, height, monochrome_colors);
1400 tmpline = (rgba_int_t*)malloc(width*sizeof(rgba_int_t));
1401 newdata = (RGBA*)malloc(newwidth*newheight*sizeof(RGBA));
1403 lblockx = make_scale_lookup(width, newwidth);
1404 lblocky = make_scale_lookup(height, newheight);
1406 for(p=lblocky[0];p<lblocky[newheight];p++)
1409 for(y=0;y<newheight;y++) {
1410 RGBA*destline = &newdata[y*newwidth];
1412 /* create lookup table for y */
1413 rgba_int_t*l = tmpline;
1414 scale_lookup_t*p_y,*p_x;
1415 memset(tmpline, 0, width*sizeof(rgba_int_t));
1416 for(p_y=lblocky[y];p_y<lblocky[y+1];p_y++) {
1417 RGBA*line = &data[p_y->pos];
1419 int weight = p_y->weight;
1420 for(x=0;x<width;x++) {
1421 tmpline[x].r += line[x].r*weight;
1422 tmpline[x].g += line[x].g*weight;
1423 tmpline[x].b += line[x].b*weight;
1424 tmpline[x].a += line[x].a*weight;
1428 /* process x direction */
1430 for(x=0;x<newwidth;x++) {
1431 unsigned int r=0,g=0,b=0,a=0;
1432 scale_lookup_t*p_x_to = lblockx[x+1];
1434 rgba_int_t* col = &tmpline[p_x->pos];
1435 unsigned int weight = p_x->weight;
1441 } while (p_x<p_x_to);
1443 destline->r = r >> 16;
1444 destline->g = g >> 16;
1445 destline->b = b >> 16;
1446 destline->a = a >> 16;
1453 decodeMonochromeImage(newdata, newwidth, newheight, monochrome_colors);