unsigned int invert_cmyk_jpeg:1;
unsigned int decoded:1;
unsigned int scalable:1;
+ uint8_t orientation;
fz_image *mask;
int xres; /* As given in the image, not necessarily as rendered */
int yres; /* As given in the image, not necessarily as rendered */
*/
void fz_image_resolution(fz_image *image, int *xres, int *yres);
+/**
+ Request the natural orientation of an image.
+
+ This is for images (such as JPEG) that can contain internal
+ specifications of rotation/flips. This is ignored by all the
+ internal decode/rendering routines, but can be used by callers
+ (such as the image document handler) to respect such
+ specifications.
+
+ The values used by MuPDF are as follows, with the equivalent
+ Exif specifications given for information:
+
+ 0: Undefined
+ 1: 0 degree ccw rotation. (Exif = 1)
+ 2: 90 degree ccw rotation. (Exif = 8)
+ 3: 180 degree ccw rotation. (Exif = 3)
+ 4: 270 degree ccw rotation. (Exif = 6)
+ 5: flip on X. (Exif = 2)
+ 6: flip on X, then rotate ccw by 90 degrees. (Exif = 5)
+ 7: flip on X, then rotate ccw by 180 degrees. (Exif = 4)
+ 8: flip on X, then rotate ccw by 270 degrees. (Exif = 7)
+*/
+uint8_t fz_image_orientation(fz_context *ctx, fz_image *image);
+
+fz_matrix
+fz_image_orientation_matrix(fz_context *ctx, fz_image *image);
+
/**
Retrieve the underlying compressed data for an image.
fz_image *image = page->image;
int xres, yres;
fz_rect bbox;
+ uint8_t orientation = fz_image_orientation(ctx, page->image);
fz_image_resolution(image, &xres, &yres);
bbox.x0 = bbox.y0 = 0;
- bbox.x1 = image->w * DPI / xres;
- bbox.y1 = image->h * DPI / yres;
+ if (orientation == 0 || (orientation & 1) == 1)
+ {
+ bbox.x1 = image->w * DPI / xres;
+ bbox.y1 = image->h * DPI / yres;
+ }
+ else
+ {
+ bbox.y1 = image->w * DPI / xres;
+ bbox.x1 = image->h * DPI / yres;
+ }
return bbox;
}
fz_image *image = page->image;
int xres, yres;
float w, h;
+ uint8_t orientation = fz_image_orientation(ctx, page->image);
+ fz_matrix immat = fz_image_orientation_matrix(ctx, page->image);
fz_image_resolution(image, &xres, &yres);
- w = image->w * DPI / xres;
- h = image->h * DPI / yres;
- ctm = fz_pre_scale(ctm, w, h);
+ if (orientation == 0 || (orientation & 1) == 1)
+ {
+ w = image->w * DPI / xres;
+ h = image->h * DPI / yres;
+ }
+ else
+ {
+ h = image->w * DPI / xres;
+ w = image->h * DPI / yres;
+ }
+ immat = fz_post_scale(immat, w, h);
+ ctm = fz_concat(immat, ctm);
fz_fill_image(ctx, dev, image, ctm, 1, fz_default_color_params);
}
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);
-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);
+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);
void fz_load_png_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_tiff_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
fz_image *image = NULL;
int type;
int bpc;
+ uint8_t orientation = 0;
if (len < 8)
fz_throw(ctx, FZ_ERROR_GENERIC, "unknown image file format");
fz_load_jpx_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace);
break;
case FZ_IMAGE_JPEG:
- fz_load_jpeg_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace);
+ fz_load_jpeg_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace, &orientation);
break;
case FZ_IMAGE_PNG:
fz_load_png_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace);
if (type == FZ_IMAGE_JPEG)
bc->params.u.jpeg.color_transform = -1;
image = fz_new_image_from_compressed_buffer(ctx, w, h, bpc, cspace, xres, yres, 0, 0, NULL, NULL, bc, NULL);
+ image->orientation = orientation;
}
fz_always(ctx)
fz_drop_colorspace(ctx, cspace);
}
}
+uint8_t fz_image_orientation(fz_context *ctx, fz_image *image)
+{
+ return image ? image->orientation : 0;
+}
+
+fz_matrix fz_image_orientation_matrix(fz_context *ctx, fz_image *image)
+{
+ fz_matrix m;
+
+ switch (image ? image->orientation : 0)
+ {
+ case 0:
+ case 1: /* 0 degree rotation */
+ m.a = 1; m.b = 0;
+ m.c = 0; m.d = 1;
+ m.e = 0; m.f = 0;
+ break;
+ case 2: /* 90 degree ccw */
+ m.a = 0; m.b = -1;
+ m.c = 1; m.d = 0;
+ m.e = 0; m.f = 1;
+ break;
+ case 3: /* 180 degree ccw */
+ m.a = -1; m.b = 0;
+ m.c = 0; m.d = -1;
+ m.e = 1; m.f = 1;
+ break;
+ case 4: /* 270 degree ccw */
+ m.a = 0; m.b = 1;
+ m.c = -1; m.d = 0;
+ m.e = 1; m.f = 0;
+ break;
+ case 5: /* flip on X */
+ m.a = -1; m.b = 0;
+ m.c = 0; m.d = 1;
+ m.e = 1; m.f = 0;
+ break;
+ case 6: /* flip on X, then rotate ccw by 90 degrees */
+ m.a = 0; m.b = 1;
+ m.c = 1; m.d = 0;
+ m.e = 0; m.f = 0;
+ break;
+ case 7: /* flip on X, then rotate ccw by 180 degrees */
+ m.a = 1; m.b = 0;
+ m.c = 0; m.d = -1;
+ m.e = 0; m.f = 1;
+ break;
+ case 8: /* flip on X, then rotate ccw by 270 degrees */
+ m.a = 0; m.b = -1;
+ m.c = -1; m.d = 0;
+ m.e = 1; m.f = 1;
+ break;
+ }
+
+ return m;
+}
+
typedef struct fz_display_list_image_s
{
fz_image super;
#endif
}
-static int extract_exif_resolution(jpeg_saved_marker_ptr marker, int *xres, int *yres)
+/* 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)
{
- int is_big_endian;
+ 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 int extract_exif_resolution(jpeg_saved_marker_ptr marker,
+ 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;
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 <= marker->data_length - 8)
x_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
image = fz_new_pixmap(ctx, colorspace, cinfo.output_width, cinfo.output_height, NULL, 0);
- if (extract_exif_resolution(cinfo.marker_list, &image->xres, &image->yres))
+ if (extract_exif_resolution(cinfo.marker_list, &image->xres, &image->yres, NULL))
/* XPS prefers EXIF resolution to JFIF density */;
else if (extract_app13_resolution(cinfo.marker_list, &image->xres, &image->yres))
/* XPS prefers APP13 resolution to JFIF density */;
}
void
-fz_load_jpeg_info(fz_context *ctx, const unsigned char *rbuf, size_t rlen, int *xp, int *yp, int *xresp, int *yresp, fz_colorspace **cspacep)
+fz_load_jpeg_info(fz_context *ctx, const unsigned char *rbuf, size_t rlen, int *xp, int *yp, int *xresp, int *yresp, fz_colorspace **cspacep, uint8_t *orientation)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr err;
fz_colorspace *icc = NULL;
*cspacep = NULL;
+ if (orientation)
+ *orientation = NULL;
cinfo.mem = NULL;
cinfo.global_state = 0;
if (!*cspacep)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot determine colorspace");
- if (extract_exif_resolution(cinfo.marker_list, xresp, yresp))
+ if (extract_exif_resolution(cinfo.marker_list, xresp, yresp, orientation))
/* XPS prefers EXIF resolution to JFIF density */;
else if (extract_app13_resolution(cinfo.marker_list, xresp, yresp))
/* XPS prefers APP13 resolution to JFIF density */;