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  // ---------------------------------------------------------  | 
