summaryrefslogtreecommitdiff
path: root/source/fitz
diff options
context:
space:
mode:
Diffstat (limited to 'source/fitz')
-rw-r--r--source/fitz/draw-device.c88
-rw-r--r--source/fitz/geometry.c12
-rw-r--r--source/fitz/image.c209
-rw-r--r--source/fitz/pixmap.c4
-rw-r--r--source/fitz/stext-output.c2
-rw-r--r--source/fitz/stream-read.c20
-rw-r--r--source/fitz/svg-device.c6
-rw-r--r--source/fitz/test-device.c2
8 files changed, 307 insertions, 36 deletions
diff --git a/source/fitz/draw-device.c b/source/fitz/draw-device.c
index b9db1558..4108cf09 100644
--- a/source/fitz/draw-device.c
+++ b/source/fitz/draw-device.c
@@ -1106,6 +1106,8 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m
fz_colorspace *model = state->dest->colorspace;
fz_irect clip;
fz_matrix local_ctm = *ctm;
+ fz_matrix inverse;
+ fz_irect src_area;
fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest, &clip), &state->scissor);
@@ -1120,10 +1122,42 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m
if (image->w == 0 || image->h == 0)
return;
- dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b);
- dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d);
-
- pixmap = fz_get_pixmap_from_image(ctx, image, dx, dy);
+ /* ctm maps the image (expressed as the unit square) onto the
+ * destination device. Reverse that to get a mapping from
+ * the destination device to the source pixels. */
+ if (fz_try_invert_matrix(&inverse, &local_ctm))
+ {
+ /* Not invertible. Could just bale? Use the whole image
+ * for now. */
+ src_area.x0 = 0;
+ src_area.x1 = image->w;
+ src_area.y0 = 0;
+ src_area.y1 = image->h;
+ }
+ else
+ {
+ float exp;
+ fz_rect rect;
+ fz_irect sane;
+ /* We want to scale from image coords, not from unit square */
+ fz_post_scale(&inverse, image->w, image->h);
+ /* Are we scaling up or down? exp < 1 means scaling down. */
+ exp = fz_matrix_max_expansion(&inverse);
+ fz_rect_from_irect(&rect, &clip);
+ fz_transform_rect(&rect, &inverse);
+ /* Allow for support requirements for scalers. */
+ fz_expand_rect(&rect, fz_max(exp, 1) * 4);
+ fz_irect_from_rect(&src_area, &rect);
+ sane.x0 = 0;
+ sane.y0 = 0;
+ sane.x1 = image->w;
+ sane.y1 = image->h;
+ fz_intersect_irect(&src_area, &sane);
+ if (fz_is_empty_irect(&src_area))
+ return;
+ }
+
+ pixmap = fz_get_pixmap_from_image(ctx, image, &src_area, &local_ctm, &dx, &dy);
orig_pixmap = pixmap;
/* convert images with more components (cmyk->rgb) before scaling */
@@ -1214,6 +1248,8 @@ fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const
fz_colorspace *model = state->dest->colorspace;
fz_irect clip;
fz_matrix local_ctm = *ctm;
+ fz_matrix inverse;
+ fz_irect src_area;
fz_pixmap_bbox(ctx, state->dest, &clip);
fz_intersect_irect(&clip, &state->scissor);
@@ -1221,9 +1257,42 @@ fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const
if (image->w == 0 || image->h == 0)
return;
- dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b);
- dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d);
- pixmap = fz_get_pixmap_from_image(ctx, image, dx, dy);
+ /* ctm maps the image (expressed as the unit square) onto the
+ * destination device. Reverse that to get a mapping from
+ * the destination device to the source pixels. */
+ if (fz_try_invert_matrix(&inverse, &local_ctm))
+ {
+ /* Not invertible. Could just bale? Use the whole image
+ * for now. */
+ src_area.x0 = 0;
+ src_area.x1 = image->w;
+ src_area.y0 = 0;
+ src_area.y1 = image->h;
+ }
+ else
+ {
+ float exp;
+ fz_rect rect;
+ fz_irect sane;
+ /* We want to scale from image coords, not from unit square */
+ fz_post_scale(&inverse, image->w, image->h);
+ /* Are we scaling up or down? exp < 1 means scaling down. */
+ exp = fz_matrix_max_expansion(&inverse);
+ fz_rect_from_irect(&rect, &clip);
+ fz_transform_rect(&rect, &inverse);
+ /* Allow for support requirements for scalers. */
+ fz_expand_rect(&rect, fz_max(exp, 1) * 4);
+ fz_irect_from_rect(&src_area, &rect);
+ sane.x0 = 0;
+ sane.y0 = 0;
+ sane.x1 = image->w;
+ sane.y1 = image->h;
+ fz_intersect_irect(&src_area, &sane);
+ if (fz_is_empty_irect(&src_area))
+ return;
+ }
+
+ pixmap = fz_get_pixmap_from_image(ctx, image, &src_area, &local_ctm, &dx, &dy);
orig_pixmap = pixmap;
fz_try(ctx)
@@ -1321,12 +1390,9 @@ fz_draw_clip_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const
fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, scissor));
}
- dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b);
- dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d);
-
fz_try(ctx)
{
- pixmap = fz_get_pixmap_from_image(ctx, image, dx, dy);
+ pixmap = fz_get_pixmap_from_image(ctx, image, NULL, &local_ctm, &dx, &dy);
orig_pixmap = pixmap;
state[1].mask = mask = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
diff --git a/source/fitz/geometry.c b/source/fitz/geometry.c
index 073536ff..f7a92816 100644
--- a/source/fitz/geometry.c
+++ b/source/fitz/geometry.c
@@ -59,6 +59,18 @@ fz_pre_scale(fz_matrix *mat, float sx, float sy)
}
fz_matrix *
+fz_post_scale(fz_matrix *mat, float sx, float sy)
+{
+ mat->a *= sx;
+ mat->b *= sy;
+ mat->c *= sx;
+ mat->d *= sy;
+ mat->e *= sx;
+ mat->f *= sy;
+ return mat;
+}
+
+fz_matrix *
fz_shear(fz_matrix *mat, float h, float v)
{
mat->a = 1; mat->b = v;
diff --git a/source/fitz/image.c b/source/fitz/image.c
index 9c66176f..36026a19 100644
--- a/source/fitz/image.c
+++ b/source/fitz/image.c
@@ -21,14 +21,16 @@ struct fz_image_key_s {
int refs;
fz_image *image;
int l2factor;
+ fz_irect rect;
};
static int
fz_make_hash_image_key(fz_context *ctx, fz_store_hash *hash, void *key_)
{
fz_image_key *key = (fz_image_key *)key_;
- hash->u.pi.ptr = key->image;
- hash->u.pi.i = key->l2factor;
+ hash->u.pir.ptr = key->image;
+ hash->u.pir.i = key->l2factor;
+ hash->u.pir.r = key->rect;
return 1;
}
@@ -55,7 +57,7 @@ fz_cmp_image_key(fz_context *ctx, void *k0_, void *k1_)
{
fz_image_key *k0 = (fz_image_key *)k0_;
fz_image_key *k1 = (fz_image_key *)k1_;
- return k0->image == k1->image && k0->l2factor == k1->l2factor;
+ return k0->image == k1->image && k0->l2factor == k1->l2factor && k0->rect.x0 == k1->rect.x0 && k0->rect.y0 == k1->rect.y0 && k0->rect.x1 == k1->rect.x1 && k0->rect.y1 == k1->rect.y1;
}
static void
@@ -96,7 +98,7 @@ fz_mask_color_key(fz_pixmap *pix, int n, const int *colorkey)
static void
fz_unblend_masked_tile(fz_context *ctx, fz_pixmap *tile, fz_image *image)
{
- fz_pixmap *mask = fz_get_pixmap_from_image(ctx, image->mask, tile->w, tile->h);
+ fz_pixmap *mask = fz_get_pixmap_from_image(ctx, image->mask, NULL, NULL, NULL, NULL);
unsigned char *s = mask->samples, *end = s + mask->w * mask->h;
unsigned char *d = tile->samples;
int k;
@@ -122,14 +124,39 @@ fz_unblend_masked_tile(fz_context *ctx, fz_pixmap *tile, fz_image *image)
}
fz_pixmap *
-fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, int indexed, int l2factor)
+fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, fz_irect *subarea, int indexed, int l2factor)
{
fz_pixmap *tile = NULL;
int stride, len, i;
unsigned char *samples = NULL;
int f = 1<<l2factor;
- int w = (image->w + f-1) >> l2factor;
- int h = (image->h + f-1) >> l2factor;
+ int w = image->w;
+ int h = image->h;
+
+ if (subarea)
+ {
+ int bpp = image->bpc * image->n;
+ int mask;
+ switch (bpp)
+ {
+ case 1: mask = 8*f; break;
+ case 2: mask = 4*f; break;
+ case 4: mask = 2*f; break;
+ default: mask = f; break;
+ }
+ subarea->x0 &= ~(mask - 1);
+ subarea->y0 &= ~(f - 1);
+ subarea->x1 = (subarea->x1 + mask - 1) & ~(mask - 1);
+ if (subarea->x1 > image->w)
+ subarea->x1 = image->w;
+ subarea->y1 = (subarea->y1 + f - 1) & ~(f - 1);
+ if (subarea->y1 > image->h)
+ subarea->y1 = image->h;
+ w = (subarea->x1 - subarea->x0);
+ h = (subarea->y1 - subarea->y0);
+ }
+ w = (w + f - 1) >> l2factor;
+ h = (h + f - 1) >> l2factor;
fz_var(tile);
fz_var(samples);
@@ -143,7 +170,46 @@ fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, in
samples = fz_malloc_array(ctx, h, stride);
- len = fz_read(ctx, stm, samples, h * stride);
+ if (subarea)
+ {
+ int hh;
+ unsigned char *s = samples;
+ int stream_w = (image->w + f - 1)>>l2factor;
+ int stream_stride = (stream_w * image->n * image->bpc + 7) / 8;
+ int l_margin = subarea->x0 >> l2factor;
+ int t_margin = subarea->y0 >> l2factor;
+ int r_margin = (image->w + f - 1 - subarea->x1) >> l2factor;
+ int b_margin = (image->h + f - 1 - subarea->y1) >> l2factor;
+ int l_skip = (l_margin * image->n * image->bpc)/8;
+ int r_skip = (r_margin * image->n * image->bpc)/8;
+ int t_skip = t_margin * stream_stride + l_skip;
+ int b_skip = b_margin * stream_stride + r_skip;
+ int l = fz_skip(ctx, stm, t_skip);
+ len = 0;
+ if (l == t_skip)
+ {
+ hh = h;
+ do
+ {
+ l = fz_read(ctx, stm, s, stride);
+ s += l;
+ len += l;
+ if (l < stride)
+ break;
+ if (--hh == 0)
+ break;
+ l = fz_skip(ctx, stm, r_skip + l_skip);
+ if (l < r_skip + l_skip)
+ break;
+ }
+ while (1);
+ (void)fz_skip(ctx, stm, r_skip + b_skip);
+ }
+ }
+ else
+ {
+ len = fz_read(ctx, stm, samples, h * stride);
+ }
/* Pad truncated images */
if (len < stride * h)
@@ -219,12 +285,13 @@ fz_drop_image_imp(fz_context *ctx, fz_storable *image_)
}
static fz_pixmap *
-standard_image_get_pixmap(fz_context *ctx, fz_image *image, int w, int h, int *l2factor)
+standard_image_get_pixmap(fz_context *ctx, fz_image *image, fz_irect *subarea, int w, int h, int *l2factor)
{
int native_l2factor;
fz_stream *stm;
int indexed;
fz_pixmap *tile;
+ int can_sub = 0;
/* We need to make a new one. */
/* First check for ones that we can't decode using streams */
@@ -271,7 +338,8 @@ standard_image_get_pixmap(fz_context *ctx, fz_image *image, int w, int h, int *l
native_l2factor -= *l2factor;
indexed = fz_colorspace_is_indexed(ctx, image->colorspace);
- tile = fz_decomp_image_from_stream(ctx, stm, image, indexed, native_l2factor);
+ can_sub = 1;
+ tile = fz_decomp_image_from_stream(ctx, stm, image, subarea, indexed, native_l2factor);
/* CMYK JPEGs in XPS documents have to be inverted */
if (image->invert_cmyk_jpeg &&
@@ -285,20 +353,63 @@ standard_image_get_pixmap(fz_context *ctx, fz_image *image, int w, int h, int *l
break;
}
+ if (can_sub == 0 && subarea != NULL)
+ {
+ subarea->x0 = 0;
+ subarea->y0 = 0;
+ subarea->x1 = image->w;
+ subarea->y1 = image->h;
+ }
+
return tile;
}
+static void
+update_ctm_for_subarea(fz_matrix *ctm, const fz_irect *subarea, int w, int h)
+{
+ fz_matrix m;
+
+ if (subarea->x0 == 0 && subarea->y0 == 0 && subarea->x1 == w && subarea->y1 == h)
+ return;
+
+ m.a = (subarea->x1 - subarea->x0) / (float)w;
+ m.b = 0;
+ m.c = 0;
+ m.d = (subarea->y1 - subarea->y0) / (float)h;
+ m.e = subarea->x0 / (float)w;
+ m.f = subarea->y0 / (float)h;
+ fz_concat(ctm, &m, ctm);
+}
+
fz_pixmap *
-fz_get_pixmap_from_image(fz_context *ctx, fz_image *image, int w, int h)
+fz_get_pixmap_from_image(fz_context *ctx, fz_image *image, const fz_irect *subarea, fz_matrix *ctm, int *dw, int *dh)
{
fz_pixmap *tile;
int l2factor, l2factor_remaining;
fz_image_key key;
fz_image_key *keyp;
+ int w;
+ int h;
if (!image)
return NULL;
+ /* Figure out the extent. */
+ if (ctm)
+ {
+ w = sqrtf(ctm->a * ctm->a + ctm->b * ctm->b);
+ if (w > image->w)
+ w = image->w;
+ h = sqrtf(ctm->c * ctm->c + ctm->d * ctm->d);
+ if (h > image->h)
+ w = image->h;
+ }
+ else
+ {
+ w = image->w;
+ h = image->h;
+ }
+
/* 'Simple' images created direct from pixmaps will have no buffer
* of compressed data. We cannot do any better than just returning
* a pointer to the original 'tile'.
@@ -307,13 +418,13 @@ fz_get_pixmap_from_image(fz_context *ctx, fz_image *image, int w, int h)
* with masks applied, so we need both parts of the following test.
*/
if (image->buffer == NULL && image->tile != NULL)
+ {
+ if (dw)
+ *dw = w;
+ if (dh)
+ *dh = h;
return fz_keep_pixmap(ctx, image->tile); /* That's all we can give you! */
-
- /* Ensure our expectations for tile size are reasonable */
- if (w < 0 || w > image->w)
- w = image->w;
- if (h < 0 || h > image->h)
- h = image->h;
+ }
/* What is our ideal factor? We search for the largest factor where
* we can subdivide and stay larger than the required size. We add
@@ -324,6 +435,61 @@ fz_get_pixmap_from_image(fz_context *ctx, fz_image *image, int w, int h)
else
for (l2factor=0; image->w>>(l2factor+1) >= w+2 && image->h>>(l2factor+1) >= h+2 && l2factor < 6; l2factor++);
+ /* Now figure out if we want to decode just a subarea */
+ if (subarea == NULL || (subarea->x1-subarea->x0)*(subarea->y1-subarea->y0) >= (image->w*image->h/10)*9)
+ {
+ /* Either no subarea specified, or a subarea 90% or more of the
+ * whole area specified. Use the whole image. */
+ key.rect.x0 = 0;
+ key.rect.y0 = 0;
+ key.rect.x1 = image->w;
+ key.rect.y1 = image->h;
+ }
+ else
+ {
+ /* Clip to the edges if they are within 1% */
+ key.rect = *subarea;
+ if (key.rect.x0 <= image->w/100)
+ key.rect.x0 = 0;
+ if (key.rect.y0 <= image->h/100)
+ key.rect.y0 = 0;
+ if (key.rect.x1 >= image->w*99/100)
+ key.rect.x1 = image->w;
+ if (key.rect.y1 >= image->h*99/100)
+ key.rect.y1 = image->h;
+ }
+
+ if (ctm)
+ {
+ float frac_w = (key.rect.x1 - key.rect.x0) / (float)image->w;
+ float frac_h = (key.rect.y1 - key.rect.y0) / (float)image->h;
+ float a = ctm->a * frac_w;
+ float b = ctm->b * frac_h;
+ float c = ctm->c * frac_w;
+ float d = ctm->d * frac_h;
+
+ w = sqrtf(a * a + b * b);
+ h = sqrtf(c * c + d * d);
+ }
+ else
+ {
+ w = image->w;
+ h = image->h;
+ }
+
+ /* Return the true sizes to the caller */
+ if (dw)
+ *dw = w;
+ if (dh)
+ *dh = h;
+ if (w > image->w)
+ w = image->w;
+ if (h > image->h)
+ h = image->h;
+
+ if (w == 0 || h == 0)
+ l2factor = 0;
+
/* Can we find any suitable tiles in the cache? */
key.refs = 1;
key.image = image;
@@ -332,7 +498,10 @@ fz_get_pixmap_from_image(fz_context *ctx, fz_image *image, int w, int h)
{
tile = fz_find_item(ctx, fz_drop_pixmap_imp, &key, &fz_image_store_type);
if (tile)
+ {
+ update_ctm_for_subarea(ctm, &key.rect, image->w, image->h);
return tile;
+ }
key.l2factor--;
}
while (key.l2factor >= 0);
@@ -340,7 +509,10 @@ fz_get_pixmap_from_image(fz_context *ctx, fz_image *image, int w, int h)
/* We'll have to decode the image; request the correct amount of
* downscaling. */
l2factor_remaining = l2factor;
- tile = image->get_pixmap(ctx, image, w, h, &l2factor_remaining);
+ tile = image->get_pixmap(ctx, image, &key.rect, w, h, &l2factor_remaining);
+
+ /* Update the ctm to allow for subareas. */
+ update_ctm_for_subarea(ctm, &key.rect, image->w, image->h);
/* l2factor_remaining is updated to the amount of subscaling left to do */
assert(l2factor_remaining >= 0 && l2factor_remaining <= 6);
@@ -360,6 +532,7 @@ fz_get_pixmap_from_image(fz_context *ctx, fz_image *image, int w, int h)
keyp->refs = 1;
keyp->image = fz_keep_image(ctx, image);
keyp->l2factor = l2factor;
+ keyp->rect = key.rect;
existing_tile = fz_store_item(ctx, keyp, tile, fz_pixmap_size(ctx, tile), &fz_image_store_type);
if (existing_tile)
{
diff --git a/source/fitz/pixmap.c b/source/fitz/pixmap.c
index 88e16d9d..fca6f733 100644
--- a/source/fitz/pixmap.c
+++ b/source/fitz/pixmap.c
@@ -1123,9 +1123,9 @@ png_from_pixmap(fz_context *ctx, fz_pixmap *pix, int drop)
}
fz_buffer *
-fz_new_buffer_from_image_as_png(fz_context *ctx, fz_image *image, int w, int h)
+fz_new_buffer_from_image_as_png(fz_context *ctx, fz_image *image)
{
- return png_from_pixmap(ctx, fz_get_pixmap_from_image(ctx, image, image->w, image->h), 1);
+ return png_from_pixmap(ctx, fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL), 1);
}
fz_buffer *
diff --git a/source/fitz/stext-output.c b/source/fitz/stext-output.c
index 1ba9fe51..70b0bf7e 100644
--- a/source/fitz/stext-output.c
+++ b/source/fitz/stext-output.c
@@ -258,7 +258,7 @@ fz_print_stext_page_html(fz_context *ctx, fz_output *out, fz_stext_page *page)
break;
default:
{
- fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image->image, image->image->w, image->image->h);
+ fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image->image);
fz_printf(ctx, out, "image/png;base64,");
send_data_base64(ctx, out, buf);
fz_drop_buffer(ctx, buf);
diff --git a/source/fitz/stream-read.c b/source/fitz/stream-read.c
index 2ebd5ad6..2ace7b1f 100644
--- a/source/fitz/stream-read.c
+++ b/source/fitz/stream-read.c
@@ -27,6 +27,26 @@ fz_read(fz_context *ctx, fz_stream *stm, unsigned char *buf, int len)
return count;
}
+static unsigned char skip_buf[4096];
+
+int fz_skip(fz_context *ctx, fz_stream *stm, int len)
+{
+ int count, l, total = 0;
+
+ while (len)
+ {
+ l = len;
+ if (l > sizeof(skip_buf))
+ l = sizeof(skip_buf);
+ count = fz_read(ctx, stm, skip_buf, l);
+ total += count;
+ if (count < l)
+ break;
+ len -= count;
+ }
+ return total;
+}
+
fz_buffer *
fz_read_all(fz_context *ctx, fz_stream *stm, int initial)
{
diff --git a/source/fitz/svg-device.c b/source/fitz/svg-device.c
index d36a5872..5f60cf75 100644
--- a/source/fitz/svg-device.c
+++ b/source/fitz/svg-device.c
@@ -743,7 +743,7 @@ svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma
break;
default:
{
- fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image, image->w, image->h);
+ fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
fz_printf(ctx, out, "image/png;base64,");
send_data_base64(ctx, out, buf);
fz_drop_buffer(ctx, buf);
@@ -830,7 +830,7 @@ svg_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const
break;
default:
{
- fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image, image->w, image->h);
+ fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
fz_printf(ctx, out, "image/png;base64,");
send_data_base64(ctx, out, buf);
fz_drop_buffer(ctx, buf);
@@ -874,7 +874,7 @@ svg_dev_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const
break;
default:
{
- fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image, image->w, image->h);
+ fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
fz_printf(ctx, out, "image/png;base64,");
send_data_base64(ctx, out, buf);
fz_drop_buffer(ctx, buf);
diff --git a/source/fitz/test-device.c b/source/fitz/test-device.c
index 64c7804f..6a4e0127 100644
--- a/source/fitz/test-device.c
+++ b/source/fitz/test-device.c
@@ -184,7 +184,7 @@ fz_test_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma
return;
}
- pix = fz_get_pixmap_from_image(ctx, image, 0, 0);
+ pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0);
if (pix == NULL) /* Should never happen really, but... */
return;