File webp-upstream-697749.patch of Package mupdf
diff --git a/include/mupdf/fitz/compressed-buffer.h b/include/mupdf/fitz/compressed-buffer.h
index 80458f11..64202fd6 100644
--- a/include/mupdf/fitz/compressed-buffer.h
+++ b/include/mupdf/fitz/compressed-buffer.h
@@ -125,10 +125,10 @@ fz_stream *fz_open_image_decomp_stream_from_buffer(fz_context *ctx, fz_compresse
fz_stream *fz_open_image_decomp_stream(fz_context *ctx, fz_stream *, fz_compression_params *, int *l2factor);
/**
- Recognise image format strings in the first 8 bytes from image
+ Recognise image format strings in the first 12 bytes from image
data.
*/
-int fz_recognize_image_format(fz_context *ctx, unsigned char p[8]);
+int fz_recognize_image_format(fz_context *ctx, unsigned char p[12]);
/**
Map from FZ_IMAGE_* value to string.
@@ -167,6 +167,7 @@ enum
FZ_IMAGE_PNM,
FZ_IMAGE_TIFF,
FZ_IMAGE_PSD,
+ FZ_IMAGE_WEBP,
};
/**
diff --git a/scripts/wrap/make_cppyy.py b/scripts/wrap/make_cppyy.py
index 89be2fec..91396c33 100644
--- a/scripts/wrap/make_cppyy.py
+++ b/scripts/wrap/make_cppyy.py
@@ -763,7 +763,7 @@ def make_cppyy(
''')
# Provide an overload for mfz_recognize_image_format(), because
- # the default unsigned char p[8] causes problems.
+ # the default unsigned char p[12] causes problems.
#
cppyy.cppdef(f'''
namespace mupdf
diff --git a/source/cbz/mucbz.c b/source/cbz/mucbz.c
index e2f99530..e877490b 100644
--- a/source/cbz/mucbz.c
+++ b/source/cbz/mucbz.c
@@ -49,6 +49,7 @@ static const char *cbz_ext_list[] = {
".tif",
".tiff",
".wdp",
+ ".webp",
NULL
};
diff --git a/source/cbz/muimg.c b/source/cbz/muimg.c
index 69d1f990..78c07017 100644
--- a/source/cbz/muimg.c
+++ b/source/cbz/muimg.c
@@ -191,7 +191,7 @@ img_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream
len = fz_buffer_storage(ctx, doc->buffer, &data);
fmt = FZ_IMAGE_UNKNOWN;
- if (len >= 8)
+ if (len >= 12)
fmt = fz_recognize_image_format(ctx, data);
if (fmt == FZ_IMAGE_TIFF)
{
@@ -236,7 +236,7 @@ img_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream
static int
img_recognize_content(fz_context *ctx, const fz_document_handler *handler, fz_stream *stream, fz_archive *dir, void **state, fz_document_recognize_state_free_fn **free_state)
{
- unsigned char data[8];
+ unsigned char data[12];
size_t n;
int fmt;
@@ -248,9 +248,9 @@ img_recognize_content(fz_context *ctx, const fz_document_handler *handler, fz_st
if (free_state)
*free_state = NULL;
- n = fz_read(ctx, stream, data, 8);
+ n = fz_read(ctx, stream, data, 12);
- if (n != 8)
+ if (n != 12)
return 0;
fmt = fz_recognize_image_format(ctx, data);
diff --git a/source/fitz/image-imp.h b/source/fitz/image-imp.h
index 06db2afc..ee6db1c5 100644
--- a/source/fitz/image-imp.h
+++ b/source/fitz/image-imp.h
@@ -32,6 +32,7 @@ fz_pixmap *fz_load_gif(fz_context *ctx, const unsigned char *data, size_t size);
fz_pixmap *fz_load_bmp(fz_context *ctx, const unsigned char *data, size_t size);
fz_pixmap *fz_load_pnm(fz_context *ctx, const unsigned char *data, size_t size);
fz_pixmap *fz_load_jbig2(fz_context *ctx, const unsigned char *data, size_t size);
+fz_pixmap *fz_load_webp(fz_context *ctx, const unsigned char *data, size_t size);
void fz_load_jpeg_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace, uint8_t *orientation);
void fz_load_jpx_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
@@ -43,5 +44,6 @@ void fz_load_gif_info(fz_context *ctx, const unsigned char *data, size_t size, i
void fz_load_bmp_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
void fz_load_pnm_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
void fz_load_jbig2_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
+void fz_load_webp_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
#endif
diff --git a/source/fitz/image.c b/source/fitz/image.c
index e898aa42..915ddf28 100644
--- a/source/fitz/image.c
+++ b/source/fitz/image.c
@@ -794,6 +794,9 @@ compressed_image_get_pixmap(fz_context *ctx, fz_image *image_, fz_irect *subarea
case FZ_IMAGE_BMP:
tile = fz_load_bmp(ctx, image->buffer->buffer->data, image->buffer->buffer->len);
break;
+ case FZ_IMAGE_WEBP:
+ tile = fz_load_webp(ctx, image->buffer->buffer->data, image->buffer->buffer->len);
+ break;
case FZ_IMAGE_TIFF:
tile = fz_load_tiff(ctx, image->buffer->buffer->data, image->buffer->buffer->len);
break;
@@ -1295,7 +1298,7 @@ fz_lookup_image_type(const char *type)
}
int
-fz_recognize_image_format(fz_context *ctx, unsigned char p[8])
+fz_recognize_image_format(fz_context *ctx, unsigned char p[12])
{
if (p[0] == 'P' && p[1] >= '1' && p[1] <= '7')
return FZ_IMAGE_PNM;
@@ -1328,6 +1331,9 @@ fz_recognize_image_format(fz_context *ctx, unsigned char p[8])
return FZ_IMAGE_JBIG2;
if (p[0] == '8' && p[1] == 'B' && p[2] == 'P' && p[3] == 'S')
return FZ_IMAGE_PSD;
+ if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F' &&
+ p[8] == 'W' && p[9] == 'E' && p[10] == 'B' && p[11] == 'P')
+ return FZ_IMAGE_WEBP;
return FZ_IMAGE_UNKNOWN;
}
@@ -1344,7 +1350,7 @@ fz_new_image_from_buffer(fz_context *ctx, fz_buffer *buffer)
int bpc;
uint8_t orientation = 0;
- if (len < 8)
+ if (len < 12)
fz_throw(ctx, FZ_ERROR_FORMAT, "unknown image file format");
type = fz_recognize_image_format(ctx, buf);
@@ -1382,6 +1388,9 @@ fz_new_image_from_buffer(fz_context *ctx, fz_buffer *buffer)
fz_load_jbig2_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace);
bpc = 1;
break;
+ case FZ_IMAGE_WEBP:
+ fz_load_webp_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace);
+ break;
default:
fz_throw(ctx, FZ_ERROR_FORMAT, "unknown image file format");
}
diff --git a/source/fitz/load-webp.c b/source/fitz/load-webp.c
new file mode 100644
index 00000000..1ab554da
--- /dev/null
+++ b/source/fitz/load-webp.c
@@ -0,0 +1,280 @@
+#include "mupdf/fitz.h"
+
+#ifdef HAVE_WEBP
+
+#include "pixmap-imp.h"
+
+#include <math.h>
+#include <string.h>
+#include <limits.h>
+
+#include <webp/decode.h>
+#include <webp/demux.h>
+#include <webp/types.h>
+
+struct info
+{
+ int width, height;
+ int xres, yres;
+ uint8_t orientation;
+ int pages;
+ fz_colorspace *cs;
+};
+
+/* Returns true if <x> can be represented as an integer without overflow.
+ *
+ * We can't use comparisons such as 'return x < INT_MAX' because INT_MAX is
+ * not safely convertible to float - it ends up as INT_MAX+1 so the comparison
+ * doesn't do what we want.
+ *
+ * Instead we do a round-trip conversion and return true if this differs by
+ * less than 1. This relies on high adjacent float values that differ by more
+ * than 1, actually being exact integers, so the round-trip doesn't change the
+ * value.
+ */
+static int float_can_be_int(float x)
+{
+ return fabsf(x - (float)(int) x) < 1;
+}
+
+static uint8_t exif_orientation_to_mupdf[9] = { 0, 1, 5, 3, 7, 6, 4, 8, 2 };
+
+static inline int read_value(const unsigned char *data, int bytes, int is_big_endian)
+{
+ int value = 0;
+ if (!is_big_endian)
+ data += bytes;
+ for (; bytes > 0; bytes--)
+ value = (value << 8) | (is_big_endian ? *data++ : *--data);
+ return value;
+}
+
+static int extract_exif_resolution(WebPData* chunk,
+ int *xres, int *yres, uint8_t *orientation)
+{
+ int is_big_endian, orient;
+ const unsigned char *data;
+ unsigned int offset, ifd_len, res_type = 0;
+ float x_res = 0, y_res = 0;
+
+ if (!chunk || chunk->size < 14)
+ return 0;
+ data = (const unsigned char *)chunk->bytes;
+ if (read_value(data, 4, 1) != 0x45786966 /* Exif */ || read_value(data + 4, 2, 1) != 0x0000)
+ return 0;
+ if (read_value(data + 6, 4, 1) == 0x49492A00)
+ is_big_endian = 0;
+ else if (read_value(data + 6, 4, 1) == 0x4D4D002A)
+ is_big_endian = 1;
+ else
+ return 0;
+
+ offset = read_value(data + 10, 4, is_big_endian) + 6;
+ if (offset < 14 || offset > chunk->size - 2)
+ return 0;
+ ifd_len = read_value(data + offset, 2, is_big_endian);
+ for (offset += 2; ifd_len > 0 && offset + 12 < chunk->size; ifd_len--, offset += 12)
+ {
+ int tag = read_value(data + offset, 2, is_big_endian);
+ int type = read_value(data + offset + 2, 2, is_big_endian);
+ int count = read_value(data + offset + 4, 4, is_big_endian);
+ unsigned int value_off = read_value(data + offset + 8, 4, is_big_endian) + 6;
+ switch (tag)
+ {
+ case 0x112:
+ if (type == 3 && count == 1) {
+ orient = read_value(data + offset + 8, 2, is_big_endian);
+ if (orient >= 1 && orient <= 8 && orientation)
+ *orientation = exif_orientation_to_mupdf[orient];
+ }
+ break;
+ case 0x11A:
+ if (type == 5 && value_off > offset && value_off <= chunk->size - 8)
+ x_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
+ break;
+ case 0x11B:
+ if (type == 5 && value_off > offset && value_off <= chunk->size - 8)
+ y_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
+ break;
+ case 0x128:
+ if (type == 3 && count == 1)
+ res_type = read_value(data + offset + 8, 2, is_big_endian);
+ break;
+ }
+ }
+
+ if (x_res <= 0 || !float_can_be_int(x_res) || y_res <= 0 || !float_can_be_int(y_res))
+ return 0;
+ if (res_type == 2)
+ {
+ *xres = (int)x_res;
+ *yres = (int)y_res;
+ }
+ else if (res_type == 3)
+ {
+ *xres = (int)(x_res * 254 / 100);
+ *yres = (int)(y_res * 254 / 100);
+ }
+ else
+ {
+ *xres = 0;
+ *yres = 0;
+ }
+ return 1;
+}
+
+static fz_colorspace *extract_icc_profile(fz_context *ctx, WebPData* chunk, fz_colorspace *colorspace)
+{
+#if FZ_ENABLE_ICC
+ fz_buffer *buf = NULL;
+
+ fz_var(buf);
+
+ if (!chunk || !chunk->bytes || chunk->size == 0)
+ return colorspace;
+
+ fz_try(ctx)
+ {
+ buf = fz_new_buffer_from_copied_data(ctx, chunk->bytes, chunk->size);
+ if (buf)
+ {
+ fz_colorspace *icc = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_NONE, 0, NULL, buf);
+ fz_drop_colorspace(ctx, colorspace);
+ colorspace = icc;
+ }
+ }
+ fz_always(ctx)
+ fz_drop_buffer(ctx, buf);
+ fz_catch(ctx)
+ fz_warn(ctx, "ignoring embedded ICC profile in JPEG");
+
+ return colorspace;
+#else
+ return colorspace;
+#endif
+}
+
+static fz_pixmap *
+webp_read_image(fz_context *ctx, struct info *info, const unsigned char *p, size_t total, int only_metadata)
+{
+ fz_pixmap *image = NULL;
+
+ fz_try(ctx)
+ {
+ struct WebPBitstreamFeatures features;
+ WebPData webp_data;
+ WebPDemuxer* demux = NULL;
+
+ if (WebPGetFeatures(p, total, &features) != VP8_STATUS_OK)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "unable to extract webp features");
+
+ info->width = features.width;
+ info->height = features.height;
+ info->xres = 72;
+ info->yres = 72;
+ info->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
+
+ webp_data.bytes = p;
+ webp_data.size = total;
+ demux = WebPDemux(&webp_data);
+
+ if (demux != NULL)
+ {
+ if (WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS) & EXIF_FLAG)
+ {
+ WebPChunkIterator chunk_iter;
+ if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter))
+ {
+ extract_exif_resolution(&chunk_iter.chunk, &info->xres, &info->yres, &info->orientation);
+ }
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ }
+ if (WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG)
+ {
+ WebPChunkIterator chunk_iter;
+ if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter))
+ {
+ info->cs = extract_icc_profile(ctx, &chunk_iter.chunk, info->cs);
+ }
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ }
+
+ WebPDemuxDelete(demux);
+ }
+
+ if (!only_metadata)
+ {
+ uint8_t* rgba = features.has_alpha ? WebPDecodeRGBA(p, total, NULL, NULL) : WebPDecodeRGB(p, total, NULL, NULL);
+
+ if (rgba == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "failed decoding webp image");
+
+ image = fz_new_pixmap(ctx, info->cs, info->width, info->height, NULL, features.has_alpha);
+ image->xres = info->xres;
+ image->yres = info->yres;
+
+ fz_clear_pixmap(ctx, image);
+ fz_unpack_tile(ctx, image, rgba, image->n, 8, image->stride, 1);
+
+ WebPFree(rgba);
+ }
+ }
+ fz_catch(ctx)
+ fz_rethrow(ctx);
+
+ return image;
+}
+
+fz_pixmap *
+fz_load_webp(fz_context *ctx, const unsigned char *p, size_t total)
+{
+ fz_pixmap *image;
+ struct info info;
+
+ fz_try(ctx)
+ {
+ image = webp_read_image(ctx, &info, p, total, 0);
+ }
+ fz_always(ctx)
+ fz_drop_colorspace(ctx, info.cs);
+ fz_catch(ctx)
+ fz_rethrow(ctx);
+
+ return image;
+}
+
+void
+fz_load_webp_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
+{
+ struct info info;
+
+ fz_try(ctx)
+ webp_read_image(ctx, &info, p, total, 1);
+ fz_catch(ctx)
+ {
+ fz_drop_colorspace(ctx, info.cs);
+ fz_rethrow(ctx);
+ }
+
+ *cspacep = info.cs;
+ *wp = info.width;
+ *hp = info.height;
+ *xresp = info.xres;
+ *yresp = info.xres;
+}
+
+#else /* HAVE_WEBP */
+
+fz_pixmap *
+fz_load_webp(fz_context *ctx, const unsigned char *p, size_t total)
+{
+ fz_throw(ctx, FZ_ERROR_GENERIC, "WebP codec is not available");
+}
+
+void
+fz_load_webp_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
+{
+ fz_throw(ctx, FZ_ERROR_GENERIC, "WebP codec is not available");
+}
+
+#endif /* HAVE_WEBP */
diff --git a/source/html/mobi.c b/source/html/mobi.c
index e0a076d1..e586eddb 100644
--- a/source/html/mobi.c
+++ b/source/html/mobi.c
@@ -318,7 +318,7 @@ fz_extract_html_from_mobi(fz_context *ctx, fz_buffer *mobi)
for (i = extra; i < n; ++i)
{
uint32_t size = offsets[i+1] - offsets[i];
- if (size > 8)
+ if (size > 12)
{
unsigned char *data = mobi->data + offsets[i];
if (fz_recognize_image_format(ctx, data))