diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am index f384cdd..564bc3a 100644 --- a/swfdec/Makefile.am +++ b/swfdec/Makefile.am @@ -1,5 +1,3 @@ -SUBDIRS = jpeg - CODEC_FILES = if HAVE_GST @@ -192,7 +190,7 @@ libswfdec_@SWFDEC_MAJORMINOR@_la_SOURCES = \ libswfdec_@SWFDEC_MAJORMINOR@_la_CFLAGS = \ $(GLOBAL_CFLAGS) $(CAIRO_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) \ - -I$(top_srcdir) -I$(srcdir)/jpeg/ $(LIBOIL_CFLAGS) $(GST_CFLAGS) \ + -I$(top_srcdir) $(LIBOIL_CFLAGS) $(GST_CFLAGS) \ -DG_LOG_DOMAIN=\"Swfdec\" libswfdec_@SWFDEC_MAJORMINOR@_la_LDFLAGS = \ $(SYMBOLIC_LDFLAGS) \ @@ -229,8 +227,6 @@ public_headers = \ swfdec_system.h \ swfdec_url.h -libswfdec_@SWFDEC_MAJORMINOR@_la_LIBADD = jpeg/libjpeg.la - libswfdec_@SWFDEC_MAJORMINOR@includedir = $(includedir)/swfdec-@SWFDEC_MAJORMINOR@/swfdec libswfdec_@SWFDEC_MAJORMINOR@include_HEADERS = $(public_headers) swfdec_enums.h nodist_libswfdec_@SWFDEC_MAJORMINOR@include_HEADERS = swfdec_version.h diff --git a/swfdec/swfdec_image.c b/swfdec/swfdec_image.c index 375de6a..7a090e2 100644 --- a/swfdec/swfdec_image.c +++ b/swfdec/swfdec_image.c @@ -26,8 +26,12 @@ #include #include #include +#include +#include +#include +#include +#include -#include "jpeg.h" #include "swfdec_image.h" #include "swfdec_cache.h" #include "swfdec_cached_image.h" @@ -37,6 +41,25 @@ G_DEFINE_TYPE (SwfdecImage, swfdec_image, SWFDEC_TYPE_CHARACTER) +#define JPEG_BUF_SIZE 4096 + +typedef struct { + struct jpeg_source_mgr pub; + unsigned char * infile; + int length; + JOCTET * buffer; + gboolean start_of_file; +} swfdecJpegSourceMgr; + +typedef swfdecJpegSourceMgr * swfdec_jpeg_src_ptr; + +struct swfdecJpegErrorMgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +typedef struct swfdecJpegErrorMgr * swfdec_jpeg_error_ptr; + static void swfdec_image_dispose (GObject *object) { @@ -132,6 +155,208 @@ swfdec_image_validate_size (SwfdecRenderer *renderer, guint width, guint height) return TRUE; } +static void +G_GNUC_NORETURN +swfdec_jpeg_error_exit (j_common_ptr cinfo) +{ + swfdec_jpeg_error_ptr err = (swfdec_jpeg_error_ptr) cinfo->err; + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + SWFDEC_ERROR ("Error decoding JPEG image: %s", buffer); + longjmp (err->setjmp_buffer, 1); +} + +static void +swfdec_jpeg_error_set (j_common_ptr cinfo, int msg_code) +{ + cinfo->err->msg_code = msg_code; +} + +static void +swfdec_jpeg_init_source (j_decompress_ptr cinfo) +{ + swfdec_jpeg_src_ptr src = (swfdec_jpeg_src_ptr) cinfo->src; + src->start_of_file = TRUE; +} + +static gboolean +swfdec_jpeg_fill_input_buffer (j_decompress_ptr cinfo) +{ + swfdec_jpeg_src_ptr src = (swfdec_jpeg_src_ptr) cinfo->src; + size_t nbytes; + + nbytes = MIN (src->length, JPEG_BUF_SIZE); + if (nbytes > 0) { + memcpy (src->buffer, src->infile, nbytes); + } else { + /* Insert a fake EOI marker */ + src->buffer[0] = 0xff; + src->buffer[1] = JPEG_EOI; + nbytes = 2; + } + + /* Workaround for bogus jpeg header */ + if (src->start_of_file && nbytes >= 4) { + if (src->buffer[0] == 0xff + && src->buffer[1] == JPEG_EOI + && src->buffer[2] == 0xff + && src->buffer[3] == 0xd8) { + src->buffer[1] = 0xd8; + src->buffer[3] = JPEG_EOI; + } + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->infile += nbytes; + src->length -= nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + +static void +swfdec_jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + swfdec_jpeg_src_ptr src = (swfdec_jpeg_src_ptr) cinfo->src; + + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) swfdec_jpeg_fill_input_buffer (cinfo); + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +swfdec_jpeg_term_source (j_decompress_ptr cinfo) +{ +} + +static void +swfdec_jpeg_rgb_to_argb (struct jpeg_decompress_struct *cinfo, + JSAMPROW *rgb, uint32_t *argb) +{ + guint size = cinfo->output_width * cinfo->output_components; + + for (guint i = 0, j = 0; i < size && j < cinfo->output_width; i += 3, j++) + argb[j] = SWFDEC_COLOR_COMBINE(rgb[0][i], rgb[0][i+1], rgb[0][i+2], 0xff); +} + +static void +swfdec_jpeg_grayscale_to_argb (struct jpeg_decompress_struct *cinfo, + JSAMPROW *gray, uint32_t *argb) +{ + for (guint i = 0; i < cinfo->output_width; i++) + argb[i] = SWFDEC_COLOR_COMBINE(gray[0][i], gray[0][i], gray[0][i], 0xff); +} + +static void +swfdec_jpeg_cmyk_to_argb (struct jpeg_decompress_struct *cinfo, + JSAMPROW *cmyk, uint32_t *argb) +{ +#if 0 + guint size = cinfo->output_width * cinfo->output_components; + guint colors; + + for (guint i = 0, j = 0; i < size && j < cinfo->output_width; i += 4, j++) { + colors = 255 - cmyk[0][i+3]; + argb[j] = SWFDEC_COLOR_COMBINE(colors * (255 - cmyk[0][i]) / 255, + colors * (255 - cmyk[0][i+1]) / 255, + colors * (255 - cmyk[0][i+2]) / 255, + 0xff); + } +#endif +} + +static gboolean +swfdec_jpeg_decode (unsigned char *data, int length, + uint32_t **outdata, guint *width, guint *height) +{ + struct jpeg_decompress_struct cinfo; + struct swfdecJpegErrorMgr jerr; + swfdec_jpeg_src_ptr src; + JSAMPROW buf[1]; + uint32_t *image; + + cinfo.err = jpeg_std_error (&jerr.pub); + jerr.pub.error_exit = swfdec_jpeg_error_exit; + + if (setjmp(jerr.setjmp_buffer)) { + jpeg_destroy_decompress (&cinfo); + return FALSE; + } + + jpeg_create_decompress (&cinfo); + + cinfo.src = (struct jpeg_source_mgr *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + sizeof (swfdecJpegSourceMgr)); + src = (swfdec_jpeg_src_ptr) cinfo.src; + src->buffer = (JOCTET *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + JPEG_BUF_SIZE * sizeof (JOCTET)); + src->pub.init_source = swfdec_jpeg_init_source; + src->pub.fill_input_buffer = swfdec_jpeg_fill_input_buffer; + src->pub.skip_input_data = swfdec_jpeg_skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; + src->pub.term_source = swfdec_jpeg_term_source; + src->infile = data; + src->length = length; + src->pub.bytes_in_buffer = 0; + src->pub.next_input_byte = NULL; + + while (cinfo.global_state != DSTATE_READY) { + jpeg_read_header (&cinfo, FALSE); + } + jpeg_start_decompress (&cinfo); + + *height = cinfo.output_height; + *width = cinfo.output_width; + + if ((1 << 30) / cinfo.output_width / cinfo.output_height < sizeof (uint32_t)) { + swfdec_jpeg_error_set ((j_common_ptr) &cinfo, JERR_OUT_OF_MEMORY); + swfdec_jpeg_error_exit ((j_common_ptr) &cinfo); + } + + buf[0] = g_malloc (cinfo.output_width * cinfo.output_components); + image = g_malloc (cinfo.output_width * cinfo.output_height * sizeof (uint32_t)); + *outdata = image; + + while (cinfo.output_scanline < cinfo.image_height) { + jpeg_read_scanlines(&cinfo, buf, 1); + switch (cinfo.out_color_space) { + case JCS_GRAYSCALE: + swfdec_jpeg_grayscale_to_argb (&cinfo, buf, image); + break; + case JCS_RGB: + swfdec_jpeg_rgb_to_argb (&cinfo, buf, image); + break; + case JCS_CMYK: + swfdec_jpeg_cmyk_to_argb (&cinfo, buf, image); + break; + case JCS_YCbCr: + case JCS_YCCK: + case JCS_UNKNOWN: + default: + g_free (buf[0]); + swfdec_jpeg_error_set ((j_common_ptr) &cinfo, JERR_BAD_J_COLORSPACE); + swfdec_jpeg_error_exit ((j_common_ptr) &cinfo); + break; + } + image += cinfo.output_width; + } + + g_free (buf[0]); + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + + return TRUE; +} + /** * swfdec_jpeg_decode_argb: * @@ -146,24 +371,35 @@ swfdec_jpeg_decode_argb (SwfdecRenderer *renderer, unsigned char *data2, int length2, void *outdata, guint *width, guint *height) { - gboolean ret; - - if (data2) { - unsigned char *tmpdata; - int tmplength; + unsigned char *data; + int length; + gboolean ret = FALSE; + + if (data1 && !data2) { + ret = swfdec_jpeg_decode (data1, length1, outdata, width, height); + } else if (!data1 && data2) { + ret = swfdec_jpeg_decode (data2, length2, outdata, width, height); + } else if (data1 && data2) { +#if 0 + while (length1 >= 2 && data1[length1-2] == 0xff && + (data1[length1-1] == 0xd9 || data1[length1-1] == 0xd8)) { + length1 -= 2; + } - tmplength = length1 + length2; - tmpdata = g_malloc (tmplength); + while (length2 >= 2 && data2[0] == 0xff && + (data2[1] == 0xd8 || data2[1] == 0xd9)) { + data2 += 2; + length2 -= 2; + } +#endif - memcpy (tmpdata, data1, length1); - memcpy (tmpdata + length1, data2, length2); - ret = jpeg_decode_argb (tmpdata, tmplength, outdata, width, height); + length = length1 + length2; + data = g_malloc (length); + memcpy (data, data1, length1); + memcpy (data + length1, data2, length2); - g_free (tmpdata); - } else if (data1) { - ret = jpeg_decode_argb (data1, length1, outdata, width, height); - } else { - ret = FALSE; + ret = swfdec_jpeg_decode (data, length, outdata, width, height); + g_free (data); } if (ret)