diff options
Diffstat (limited to 'fiv-io.c')
-rw-r--r-- | fiv-io.c | 122 |
1 files changed, 114 insertions, 8 deletions
@@ -24,6 +24,16 @@ #include <spng.h> #include <turbojpeg.h> + +#ifdef HAVE_JPEG_QS +#include <jpeglib.h> +#include <setjmp.h> +// This library is tricky to build, simply make it work at all. +#define NO_SIMD +#include <jpeg-quantsmooth/quantsmooth.h> +#undef NO_SIMD +#endif // HAVE_JPEG_QS + #ifdef HAVE_LIBRAW #include <libraw.h> #endif // HAVE_LIBRAW @@ -159,7 +169,7 @@ try_append_page(cairo_surface_t *surface, cairo_surface_t **result, return true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// --- Wuffs ------------------------------------------------------------------- // From libwebp, verified to exactly match [x * a / 255]. #define PREMULTIPLY8(a, x) (((uint32_t) (x) * (uint32_t) (a) * 32897U) >> 23) @@ -614,6 +624,8 @@ open_wuffs_using(wuffs_base__image_decoder *(*allocate)(), return surface; } +// --- JPEG -------------------------------------------------------------------- + static void trivial_cmyk_to_host_byte_order_argb(unsigned char *p, int len) { @@ -747,10 +759,10 @@ open_libjpeg_turbo(const gchar *data, gsize len, GError **error) int pixel_format = (colorspace == TJCS_CMYK || colorspace == TJCS_YCCK) ? TJPF_CMYK - : (G_BYTE_ORDER == G_LITTLE_ENDIAN ? TJPF_BGRA : TJPF_ARGB); + : (G_BYTE_ORDER == G_LITTLE_ENDIAN ? TJPF_BGRX : TJPF_XRGB); cairo_surface_t *surface = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); cairo_status_t surface_status = cairo_surface_status(surface); if (surface_status != CAIRO_STATUS_SUCCESS) { set_error(error, cairo_status_to_string(surface_status)); @@ -791,6 +803,97 @@ open_libjpeg_turbo(const gchar *data, gsize len, GError **error) return surface; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#ifdef HAVE_JPEG_QS + +struct libjpeg_error_mgr { + struct jpeg_error_mgr pub; + jmp_buf buf; + GError **error; +}; + +static void +libjpeg_error_exit(j_common_ptr cinfo) +{ + struct libjpeg_error_mgr *err = (struct libjpeg_error_mgr *) cinfo->err; + char buf[JMSG_LENGTH_MAX] = ""; + (*cinfo->err->format_message)(cinfo, buf); + set_error(err->error, buf); + longjmp(err->buf, 1); +} + +static cairo_surface_t * +open_libjpeg_enhanced(const gchar *data, gsize len, GError **error) +{ + cairo_surface_t *volatile surface = NULL; + + struct libjpeg_error_mgr jerr = {.error = error}; + struct jpeg_decompress_struct cinfo = {.err = jpeg_std_error(&jerr.pub)}; + jerr.pub.error_exit = libjpeg_error_exit; + if (setjmp(jerr.buf)) { + g_clear_pointer(&surface, cairo_surface_destroy); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + + jpeg_create_decompress(&cinfo); + jpeg_mem_src(&cinfo, (const unsigned char *) data, len); + (void) jpeg_read_header(&cinfo, true); + if (cinfo.jpeg_color_space == JCS_CMYK || + cinfo.jpeg_color_space == JCS_YCCK) + cinfo.out_color_space = JCS_CMYK; + else if (G_BYTE_ORDER == G_BIG_ENDIAN) + cinfo.out_color_space = JCS_EXT_XRGB; + else + cinfo.out_color_space = JCS_EXT_BGRX; + + jpeg_calc_output_dimensions(&cinfo); + int width = cinfo.output_width; + int height = cinfo.output_height; + + surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); + cairo_status_t surface_status = cairo_surface_status(surface); + if (surface_status != CAIRO_STATUS_SUCCESS) { + set_error(error, cairo_status_to_string(surface_status)); + longjmp(jerr.buf, 1); + } + + unsigned char *surface_data = cairo_image_surface_get_data(surface); + int surface_stride = cairo_image_surface_get_stride(surface); + JSAMPARRAY lines = (*cinfo.mem->alloc_small)( + (j_common_ptr) &cinfo, JPOOL_IMAGE, sizeof *lines * height); + for (int i = 0; i < height; i++) + lines[i] = surface_data + i * surface_stride; + + // Go for the maximum quality setting. + jpegqs_control_t opts = { + .flags = JPEGQS_DIAGONALS | JPEGQS_JOINT_YUV | JPEGQS_UPSAMPLE_UV, + .threads = g_get_num_processors(), + .niter = 3, + }; + + (void) jpegqs_start_decompress(&cinfo, &opts); + while (cinfo.output_scanline < cinfo.output_height) + (void) jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, + cinfo.output_height - cinfo.output_scanline); + if (cinfo.out_color_space == JCS_CMYK) + trivial_cmyk_to_host_byte_order_argb( + surface_data, cinfo.output_width * cinfo.output_height); + cairo_surface_mark_dirty(surface); + (void) jpegqs_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + parse_jpeg_metadata(surface, data, len); + return surface; +} + +#else +#define open_libjpeg_enhanced open_libjpeg_turbo +#endif + +// --- Optional dependencies --------------------------------------------------- + #ifdef HAVE_LIBRAW // --------------------------------------------------------- static cairo_surface_t * @@ -1831,7 +1934,7 @@ cairo_user_data_key_t fiv_io_key_page_next; cairo_user_data_key_t fiv_io_key_page_previous; cairo_surface_t * -fiv_io_open(const gchar *path, GError **error) +fiv_io_open(const gchar *path, gboolean enhance, GError **error) { // TODO(p): Don't always load everything into memory, test type first, // so that we can reject non-pictures early. Wuffs only needs the first @@ -1852,14 +1955,15 @@ fiv_io_open(const gchar *path, GError **error) if (!g_file_get_contents(path, &data, &len, error)) return NULL; - cairo_surface_t *surface = fiv_io_open_from_data(data, len, path, error); + cairo_surface_t *surface = + fiv_io_open_from_data(data, len, path, enhance, error); free(data); return surface; } cairo_surface_t * -fiv_io_open_from_data( - const char *data, size_t len, const gchar *path, GError **error) +fiv_io_open_from_data(const char *data, size_t len, const gchar *path, + gboolean enhance, GError **error) { wuffs_base__slice_u8 prefix = wuffs_base__make_slice_u8((uint8_t *) data, len); @@ -1884,7 +1988,9 @@ fiv_io_open_from_data( error); break; case WUFFS_BASE__FOURCC__JPEG: - surface = open_libjpeg_turbo(data, len, error); + surface = enhance + ? open_libjpeg_enhanced(data, len, error) + : open_libjpeg_turbo(data, len, error); break; default: #ifdef HAVE_LIBRAW // --------------------------------------------------------- |