diff options
-rw-r--r-- | apps/mupdfextract.c | 5 | ||||
-rw-r--r-- | cbz/mucbz.c | 73 | ||||
-rw-r--r-- | draw/draw_device.c | 180 | ||||
-rw-r--r-- | draw/draw_glyph.c | 2 | ||||
-rw-r--r-- | fitz/base_hash.c | 97 | ||||
-rw-r--r-- | fitz/dev_bbox.c | 4 | ||||
-rw-r--r-- | fitz/dev_list.c | 16 | ||||
-rw-r--r-- | fitz/dev_null.c | 6 | ||||
-rw-r--r-- | fitz/dev_trace.c | 6 | ||||
-rw-r--r-- | fitz/filt_dctd.c | 11 | ||||
-rw-r--r-- | fitz/fitz.h | 72 | ||||
-rw-r--r-- | fitz/res_colorspace.c | 2 | ||||
-rw-r--r-- | fitz/res_pixmap.c | 20 | ||||
-rw-r--r-- | fitz/res_store.c | 130 | ||||
-rw-r--r-- | fitz/stm_read.c | 3 | ||||
-rw-r--r-- | pdf/mupdf.h | 94 | ||||
-rw-r--r-- | pdf/pdf_cmap_load.c | 4 | ||||
-rw-r--r-- | pdf/pdf_colorspace.c | 4 | ||||
-rw-r--r-- | pdf/pdf_font.c | 4 | ||||
-rw-r--r-- | pdf/pdf_function.c | 4 | ||||
-rw-r--r-- | pdf/pdf_image.c | 474 | ||||
-rw-r--r-- | pdf/pdf_interpret.c | 15 | ||||
-rw-r--r-- | pdf/pdf_pattern.c | 6 | ||||
-rw-r--r-- | pdf/pdf_shade.c | 4 | ||||
-rw-r--r-- | pdf/pdf_store.c | 59 | ||||
-rw-r--r-- | pdf/pdf_stream.c | 130 | ||||
-rw-r--r-- | pdf/pdf_xobject.c | 6 | ||||
-rw-r--r-- | win32/libmupdf.vcproj | 4 | ||||
-rw-r--r-- | xps/xps_image.c | 76 |
29 files changed, 1100 insertions, 411 deletions
diff --git a/apps/mupdfextract.c b/apps/mupdfextract.c index f309f39a..239e9ea7 100644 --- a/apps/mupdfextract.c +++ b/apps/mupdfextract.c @@ -31,6 +31,7 @@ static int isfontdesc(fz_obj *obj) static void saveimage(int num) { + fz_image *image; fz_pixmap *img; fz_obj *ref; char name[1024]; @@ -39,7 +40,9 @@ static void saveimage(int num) /* TODO: detect DCTD and save as jpeg */ - img = pdf_load_image(doc, ref); + image = pdf_load_image(doc, ref); + img = fz_image_to_pixmap(ctx, image, 0, 0); + fz_drop_image(ctx, image); if (dorgb && img->colorspace && img->colorspace != fz_device_rgb) { diff --git a/cbz/mucbz.c b/cbz/mucbz.c index 83c463bd..89c9d4dd 100644 --- a/cbz/mucbz.c +++ b/cbz/mucbz.c @@ -19,9 +19,18 @@ static const char *cbz_ext_list[] = { NULL }; +typedef struct cbz_image_s cbz_image; + +struct cbz_image_s +{ + fz_image base; + int xres, yres; + fz_pixmap *pix; +}; + struct cbz_page_s { - fz_pixmap *image; + cbz_image *image; }; typedef struct cbz_entry_s cbz_entry; @@ -343,12 +352,34 @@ cbz_count_pages(cbz_document *doc) return doc->page_count; } +static void +cbz_free_image(fz_context *ctx, fz_storable *image_) +{ + cbz_image *image = (cbz_image *)image_; + + if (image == NULL) + return; + fz_drop_pixmap(ctx, image->pix); + fz_free(ctx, image); +} + +static fz_pixmap * +cbz_image_to_pixmap(fz_context *ctx, fz_image *image_, int x, int w) +{ + cbz_image *image = (cbz_image *)image_; + + return fz_keep_pixmap(ctx, image->pix); +} + + cbz_page * cbz_load_page(cbz_document *doc, int number) { fz_context *ctx = doc->ctx; unsigned char *data = NULL; cbz_page *page = NULL; + cbz_image *image = NULL; + fz_pixmap *pixmap = NULL; int size; if (number < 0 || number >= doc->page_count) @@ -358,19 +389,31 @@ cbz_load_page(cbz_document *doc, int number) fz_var(data); fz_var(page); + fz_var(image); + fz_var(pixmap); fz_try(ctx) { - page = fz_malloc_struct(doc->ctx, cbz_page); + page = fz_malloc_struct(ctx, cbz_page); page->image = NULL; data = cbz_read_zip_entry(doc, doc->entry[number].offset, &size); if (data[0] == 0xff && data[1] == 0xd8) - page->image = fz_load_jpeg(ctx, data, size); + pixmap = fz_load_jpeg(ctx, data, size); else if (memcmp(data, "\211PNG\r\n\032\n", 8) == 0) - page->image = fz_load_png(ctx, data, size); + pixmap = fz_load_png(ctx, data, size); else fz_throw(ctx, "unknown image format"); + + image = fz_malloc_struct(ctx, cbz_image); + FZ_INIT_STORABLE(&image->base, 1, cbz_free_image); + image->base.w = pixmap->w; + image->base.h = pixmap->h; + image->base.get_pixmap = cbz_image_to_pixmap; + image->xres = pixmap->xres; + image->yres = pixmap->yres; + image->pix = pixmap; + page->image = image; } fz_always(ctx) { @@ -378,9 +421,7 @@ cbz_load_page(cbz_document *doc, int number) } fz_catch(ctx) { - if (page && page->image) - fz_drop_pixmap(ctx, page->image); - fz_free(ctx, page); + cbz_free_page(doc, page); fz_rethrow(ctx); } @@ -390,29 +431,31 @@ cbz_load_page(cbz_document *doc, int number) void cbz_free_page(cbz_document *doc, cbz_page *page) { - fz_drop_pixmap(doc->ctx, page->image); + if (!page) + return; + fz_drop_image(doc->ctx, &page->image->base); fz_free(doc->ctx, page); } fz_rect cbz_bound_page(cbz_document *doc, cbz_page *page) { - fz_pixmap *image = page->image; + cbz_image *image = page->image; fz_rect bbox; bbox.x0 = bbox.y0 = 0; - bbox.x1 = image->w * DPI / image->xres; - bbox.y1 = image->h * DPI / image->yres; + bbox.x1 = image->base.w * DPI / image->xres; + bbox.y1 = image->base.h * DPI / image->yres; return bbox; } void cbz_run_page(cbz_document *doc, cbz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) { - fz_pixmap *image = page->image; - float w = image->w * DPI / image->xres; - float h = image->h * DPI / image->yres; + cbz_image *image = page->image; + float w = image->base.w * DPI / image->xres; + float h = image->base.h * DPI / image->yres; ctm = fz_concat(fz_scale(w, h), ctm); - fz_fill_image(dev, image, ctm, 1); + fz_fill_image(dev, &image->base, ctm, 1); } /* Document interface wrappers */ diff --git a/draw/draw_device.c b/draw/draw_device.c index 279ef64d..65ee5a39 100644 --- a/draw/draw_device.c +++ b/draw/draw_device.c @@ -878,11 +878,13 @@ fz_transform_pixmap(fz_context *ctx, fz_pixmap *image, fz_matrix *ctm, int x, in } static void -fz_draw_fill_image(fz_device *devp, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_draw_fill_image(fz_device *devp, fz_image *image, fz_matrix ctm, float alpha) { fz_draw_device *dev = devp->user; fz_pixmap *converted = NULL; fz_pixmap *scaled = NULL; + fz_pixmap *pixmap; + fz_pixmap *orig_pixmap; int after; int dx, dy; fz_context *ctx = dev->ctx; @@ -903,87 +905,93 @@ fz_draw_fill_image(fz_device *devp, fz_pixmap *image, fz_matrix ctm, float alpha if (image->w == 0 || image->h == 0) return; + dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); + dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); + + pixmap = fz_image_to_pixmap(ctx, image, dx, dy); + orig_pixmap = pixmap; + /* convert images with more components (cmyk->rgb) before scaling */ /* convert images with fewer components (gray->rgb after scaling */ /* convert images with expensive colorspace transforms after scaling */ - if (state->blendmode & FZ_BLEND_KNOCKOUT) - state = fz_knockout_begin(dev); + fz_try(ctx) + { + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); - after = 0; - if (image->colorspace == fz_device_gray) - after = 1; + after = 0; + if (pixmap->colorspace == fz_device_gray) + after = 1; - if (image->colorspace != model && !after) - { - converted = fz_new_pixmap_with_rect(ctx, model, fz_bound_pixmap(image)); - fz_convert_pixmap(ctx, image, converted); - image = converted; - } + if (pixmap->colorspace != model && !after) + { + converted = fz_new_pixmap_with_rect(ctx, model, fz_bound_pixmap(pixmap)); + fz_convert_pixmap(ctx, pixmap, converted); + pixmap = converted; + } - fz_try(ctx) - { - dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); - dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); - if (dx < image->w && dy < image->h) + if (dx < pixmap->w && dy < pixmap->h) { int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); - scaled = fz_transform_pixmap(ctx, image, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); + scaled = fz_transform_pixmap(ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); if (!scaled) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; - scaled = fz_scale_pixmap(ctx, image, image->x, image->y, dx, dy, NULL); + scaled = fz_scale_pixmap(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); } if (scaled) - image = scaled; + pixmap = scaled; } - if (image->colorspace != model) + if (pixmap->colorspace != model) { - if ((image->colorspace == fz_device_gray && model == fz_device_rgb) || - (image->colorspace == fz_device_gray && model == fz_device_bgr)) + if ((pixmap->colorspace == fz_device_gray && model == fz_device_rgb) || + (pixmap->colorspace == fz_device_gray && model == fz_device_bgr)) { /* We have special case rendering code for gray -> rgb/bgr */ } else { - converted = fz_new_pixmap_with_rect(ctx, model, fz_bound_pixmap(image)); - fz_convert_pixmap(ctx, image, converted); - image = converted; + converted = fz_new_pixmap_with_rect(ctx, model, fz_bound_pixmap(pixmap)); + fz_convert_pixmap(ctx, pixmap, converted); + pixmap = converted; } } - fz_paint_image(state->dest, state->scissor, state->shape, image, ctm, alpha * 255); + fz_paint_image(state->dest, state->scissor, state->shape, pixmap, ctm, alpha * 255); + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } - fz_catch(ctx) + fz_always(ctx) { fz_drop_pixmap(ctx, scaled); fz_drop_pixmap(ctx, converted); + fz_drop_pixmap(ctx, orig_pixmap); + } + fz_catch(ctx) + { fz_rethrow(ctx); } - - if (scaled) - fz_drop_pixmap(ctx, scaled); - if (converted) - fz_drop_pixmap(ctx, converted); - - if (state->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_end(dev); } static void -fz_draw_fill_image_mask(fz_device *devp, fz_pixmap *image, fz_matrix ctm, +fz_draw_fill_image_mask(fz_device *devp, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_draw_device *dev = devp->user; unsigned char colorbv[FZ_MAX_COLORS + 1]; float colorfv[FZ_MAX_COLORS]; fz_pixmap *scaled = NULL; + fz_pixmap *pixmap; + fz_pixmap *orig_pixmap; int dx, dy; int i; + fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; fz_bbox clip = fz_bound_pixmap(state->dest); @@ -993,43 +1001,57 @@ fz_draw_fill_image_mask(fz_device *devp, fz_pixmap *image, fz_matrix ctm, if (image->w == 0 || image->h == 0) return; - if (state->blendmode & FZ_BLEND_KNOCKOUT) - state = fz_knockout_begin(dev); - dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); - if (dx < image->w && dy < image->h) + pixmap = fz_image_to_pixmap(ctx, image, dx, dy); + orig_pixmap = pixmap; + + fz_try(ctx) { - int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); - scaled = fz_transform_pixmap(dev->ctx, image, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); - if (!scaled) + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + if (dx < pixmap->w && dy < pixmap->h) { - if (dx < 1) - dx = 1; - if (dy < 1) - dy = 1; - scaled = fz_scale_pixmap(dev->ctx, image, image->x, image->y, dx, dy, NULL); + int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); + scaled = fz_transform_pixmap(dev->ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); + if (!scaled) + { + if (dx < 1) + dx = 1; + if (dy < 1) + dy = 1; + scaled = fz_scale_pixmap(dev->ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); + } + if (scaled) + pixmap = scaled; } - if (scaled) - image = scaled; - } - fz_convert_color(dev->ctx, colorspace, color, model, colorfv); - for (i = 0; i < model->n; i++) - colorbv[i] = colorfv[i] * 255; - colorbv[i] = alpha * 255; + fz_convert_color(dev->ctx, colorspace, color, model, colorfv); + for (i = 0; i < model->n; i++) + colorbv[i] = colorfv[i] * 255; + colorbv[i] = alpha * 255; - fz_paint_image_with_color(state->dest, state->scissor, state->shape, image, ctm, colorbv); + fz_paint_image_with_color(state->dest, state->scissor, state->shape, pixmap, ctm, colorbv); - if (scaled) - fz_drop_pixmap(dev->ctx, scaled); + if (scaled) + fz_drop_pixmap(dev->ctx, scaled); - if (state->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_end(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); + } + fz_always(ctx) + { + fz_drop_pixmap(dev->ctx, orig_pixmap); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } } static void -fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) +fz_draw_clip_image_mask(fz_device *devp, fz_image *image, fz_rect *rect, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_context *ctx = dev->ctx; @@ -1038,6 +1060,8 @@ fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_mat fz_pixmap *dest = NULL; fz_pixmap *shape = NULL; fz_pixmap *scaled = NULL; + fz_pixmap *pixmap; + fz_pixmap *orig_pixmap; int dx, dy; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; @@ -1068,10 +1092,16 @@ fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_mat if (rect) bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect)); - mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); - fz_clear_pixmap(dev->ctx, mask); + dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); + dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); + pixmap = fz_image_to_pixmap(ctx, image, dx, dy); + orig_pixmap = pixmap; + fz_try(ctx) { + mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); + fz_clear_pixmap(dev->ctx, mask); + dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, dest); if (state->shape) @@ -1080,23 +1110,28 @@ fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_mat fz_clear_pixmap(dev->ctx, shape); } - dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); - dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); - if (dx < image->w && dy < image->h) + if (dx < pixmap->w && dy < pixmap->h) { int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); - scaled = fz_transform_pixmap(dev->ctx, image, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); + scaled = fz_transform_pixmap(dev->ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); if (!scaled) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; - scaled = fz_scale_pixmap(dev->ctx, image, image->x, image->y, dx, dy, NULL); + scaled = fz_scale_pixmap(dev->ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); } if (scaled) - image = scaled; + pixmap = scaled; } + fz_paint_image(mask, bbox, state->shape, pixmap, ctm, 255); + + } + fz_always(ctx) + { + fz_drop_pixmap(ctx, scaled); + fz_drop_pixmap(ctx, orig_pixmap); } fz_catch(ctx) { @@ -1106,11 +1141,6 @@ fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_mat fz_rethrow(ctx); } - fz_paint_image(mask, bbox, state->shape, image, ctm, 255); - - if (scaled) - fz_drop_pixmap(dev->ctx, scaled); - state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; diff --git a/draw/draw_glyph.c b/draw/draw_glyph.c index 68ae3b80..110a7a25 100644 --- a/draw/draw_glyph.c +++ b/draw/draw_glyph.c @@ -30,7 +30,7 @@ fz_new_glyph_cache_context(fz_context *ctx) cache = fz_malloc_struct(ctx, fz_glyph_cache); fz_try(ctx) { - cache->hash = fz_new_hash_table(ctx, 509, sizeof(fz_glyph_key)); + cache->hash = fz_new_hash_table(ctx, 509, sizeof(fz_glyph_key), FZ_LOCK_GLYPHCACHE); } fz_catch(ctx) { diff --git a/fitz/base_hash.c b/fitz/base_hash.c index 630f6b6a..0fda86cd 100644 --- a/fitz/base_hash.c +++ b/fitz/base_hash.c @@ -22,6 +22,7 @@ struct fz_hash_table_s int keylen; int size; int load; + int lock; /* -1 or the lock used to protect this hash table */ fz_hash_entry *ents; }; @@ -42,7 +43,7 @@ static unsigned hash(unsigned char *s, int len) } fz_hash_table * -fz_new_hash_table(fz_context *ctx, int initialsize, int keylen) +fz_new_hash_table(fz_context *ctx, int initialsize, int keylen, int lock) { fz_hash_table *table; @@ -52,6 +53,7 @@ fz_new_hash_table(fz_context *ctx, int initialsize, int keylen) table->keylen = keylen; table->size = initialsize; table->load = 0; + table->lock = lock; fz_try(ctx) { table->ents = fz_malloc_array(ctx, table->size, sizeof(fz_hash_entry)); @@ -98,10 +100,45 @@ fz_free_hash(fz_context *ctx, fz_hash_table *table) fz_free(ctx, table); } +static void * +do_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val) +{ + fz_hash_entry *ents; + unsigned size; + unsigned pos; + + ents = table->ents; + size = table->size; + pos = hash(key, table->keylen) % size; + + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + + while (1) + { + if (!ents[pos].val) + { + memcpy(ents[pos].key, key, table->keylen); + ents[pos].val = val; + table->load ++; + return NULL; + } + + if (memcmp(key, ents[pos].key, table->keylen) == 0) + { + fz_warn(ctx, "assert: overwrite hash slot"); + return ents[pos].val = val; + } + + pos = (pos + 1) % size; + } +} + static void fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) { fz_hash_entry *oldents = table->ents; + fz_hash_entry *newents = table->ents; int oldsize = table->size; int oldload = table->load; int i; @@ -112,7 +149,22 @@ fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) return; } - table->ents = fz_malloc_array(ctx, newsize, sizeof(fz_hash_entry)); + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, FZ_LOCK_ALLOC); + newents = fz_malloc_array(ctx, newsize, sizeof(fz_hash_entry)); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, FZ_LOCK_ALLOC); + if (table->lock >= 0) + { + if (table->size >= newsize) + { + /* Someone else fixed it before we could lock! */ + fz_unlock(ctx, table->lock); + fz_free(ctx, newents); + return; + } + } + table->ents = newents; memset(table->ents, 0, sizeof(fz_hash_entry) * newsize); table->size = newsize; table->load = 0; @@ -121,11 +173,15 @@ fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) { if (oldents[i].val) { - fz_hash_insert(ctx, table, oldents[i].key, oldents[i].val); + do_hash_insert(ctx, table, oldents[i].key, oldents[i].val); } } + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, FZ_LOCK_ALLOC); fz_free(ctx, oldents); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, FZ_LOCK_ALLOC); } void * @@ -135,6 +191,9 @@ fz_hash_find(fz_context *ctx, fz_hash_table *table, void *key) unsigned size = table->size; unsigned pos = hash(key, table->keylen) % size; + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + while (1) { if (!ents[pos].val) @@ -150,37 +209,12 @@ fz_hash_find(fz_context *ctx, fz_hash_table *table, void *key) void * fz_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val) { - fz_hash_entry *ents; - unsigned size; - unsigned pos; - if (table->load > table->size * 8 / 10) { fz_resize_hash(ctx, table, table->size * 2); } - ents = table->ents; - size = table->size; - pos = hash(key, table->keylen) % size; - - while (1) - { - if (!ents[pos].val) - { - memcpy(ents[pos].key, key, table->keylen); - ents[pos].val = val; - table->load ++; - return NULL; - } - - if (memcmp(key, ents[pos].key, table->keylen) == 0) - { - fz_warn(ctx, "assert: overwrite hash slot"); - return ents[pos].val; - } - - pos = (pos + 1) % size; - } + return do_hash_insert(ctx, table, key, val); } void @@ -191,11 +225,14 @@ fz_hash_remove(fz_context *ctx, fz_hash_table *table, void *key) unsigned pos = hash(key, table->keylen) % size; unsigned hole, look, code; + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + while (1) { if (!ents[pos].val) { - fz_warn(ctx, "assert: remove inexistent hash entry"); + fz_warn(ctx, "assert: remove non-existent hash entry"); return; } diff --git a/fitz/dev_bbox.c b/fitz/dev_bbox.c index 636ceb94..b015c585 100644 --- a/fitz/dev_bbox.c +++ b/fitz/dev_bbox.c @@ -47,7 +47,7 @@ fz_bbox_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } static void -fz_bbox_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_bbox_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { fz_bbox *result = dev->user; fz_bbox bbox = fz_round_rect(fz_transform_rect(ctm, fz_unit_rect)); @@ -55,7 +55,7 @@ fz_bbox_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) } static void -fz_bbox_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_bbox_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_bbox_fill_image(dev, image, ctm, alpha); diff --git a/fitz/dev_list.c b/fitz/dev_list.c index adc691d0..1d82421e 100644 --- a/fitz/dev_list.c +++ b/fitz/dev_list.c @@ -37,7 +37,7 @@ struct fz_display_node_s fz_path *path; fz_text *text; fz_shade *shade; - fz_pixmap *image; + fz_image *image; int blendmode; } item; fz_stroke_state *stroke; @@ -207,7 +207,7 @@ fz_free_display_node(fz_context *ctx, fz_display_node *node) case FZ_CMD_FILL_IMAGE: case FZ_CMD_FILL_IMAGE_MASK: case FZ_CMD_CLIP_IMAGE_MASK: - fz_drop_pixmap(ctx, node->item.image); + fz_drop_image(ctx, node->item.image); break; case FZ_CMD_POP_CLIP: case FZ_CMD_BEGIN_MASK: @@ -435,35 +435,35 @@ fz_list_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } static void -fz_list_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_list_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { fz_display_node *node; node = fz_new_display_node(dev->ctx, FZ_CMD_FILL_IMAGE, ctm, NULL, NULL, alpha); node->rect = fz_transform_rect(ctm, fz_unit_rect); - node->item.image = fz_keep_pixmap(dev->ctx, image); + node->item.image = fz_keep_image(dev->ctx, image); fz_append_display_node(dev->user, node); } static void -fz_list_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_list_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_display_node *node; node = fz_new_display_node(dev->ctx, FZ_CMD_FILL_IMAGE_MASK, ctm, colorspace, color, alpha); node->rect = fz_transform_rect(ctm, fz_unit_rect); - node->item.image = fz_keep_pixmap(dev->ctx, image); + node->item.image = fz_keep_image(dev->ctx, image); fz_append_display_node(dev->user, node); } static void -fz_list_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) +fz_list_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm) { fz_display_node *node; node = fz_new_display_node(dev->ctx, FZ_CMD_CLIP_IMAGE_MASK, ctm, NULL, NULL, 0); node->rect = fz_transform_rect(ctm, fz_unit_rect); if (rect) node->rect = fz_intersect_rect(node->rect, *rect); - node->item.image = fz_keep_pixmap(dev->ctx, image); + node->item.image = fz_keep_image(dev->ctx, image); fz_append_display_node(dev->user, node); } diff --git a/fitz/dev_null.c b/fitz/dev_null.c index b4ba5cbe..d817932d 100644 --- a/fitz/dev_null.c +++ b/fitz/dev_null.c @@ -103,14 +103,14 @@ fz_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } void -fz_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { if (dev->fill_image) dev->fill_image(dev, image, ctm, alpha); } void -fz_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { if (dev->fill_image_mask) @@ -118,7 +118,7 @@ fz_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, } void -fz_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) +fz_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm) { if (dev->clip_image_mask) dev->clip_image_mask(dev, image, rect, ctm); diff --git a/fitz/dev_trace.c b/fitz/dev_trace.c index 1c2e1ed1..9faffc35 100644 --- a/fitz/dev_trace.c +++ b/fitz/dev_trace.c @@ -197,7 +197,7 @@ fz_trace_ignore_text(fz_device *dev, fz_text *text, fz_matrix ctm) } static void -fz_trace_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_trace_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { printf("<fill_image alpha=\"%g\" ", alpha); fz_trace_matrix(ctm); @@ -213,7 +213,7 @@ fz_trace_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } static void -fz_trace_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_trace_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { printf("<fill_image_mask "); @@ -223,7 +223,7 @@ fz_colorspace *colorspace, float *color, float alpha) } static void -fz_trace_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) +fz_trace_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm) { printf("<clip_image_mask "); fz_trace_matrix(ctm); diff --git a/fitz/filt_dctd.c b/fitz/filt_dctd.c index 4357f3d7..3abe0468 100644 --- a/fitz/filt_dctd.c +++ b/fitz/filt_dctd.c @@ -12,6 +12,7 @@ struct fz_dctd_s int color_transform; int init; int stride; + int factor; unsigned char *scanline; unsigned char *rp, *wp; struct jpeg_decompress_struct cinfo; @@ -150,6 +151,9 @@ read_dctd(fz_stream *stm, unsigned char *buf, int len) break; } + cinfo->scale_num = 8/state->factor; + cinfo->scale_denom = 8; + jpeg_start_decompress(cinfo); state->stride = cinfo->output_width * cinfo->output_components; @@ -216,6 +220,12 @@ skip: fz_stream * fz_open_dctd(fz_stream *chain, int color_transform) { + return fz_open_resized_dctd(chain, color_transform, 1); +} + +fz_stream * +fz_open_resized_dctd(fz_stream *chain, int color_transform, int factor) +{ fz_context *ctx = chain->ctx; fz_dctd *state = NULL; @@ -228,6 +238,7 @@ fz_open_dctd(fz_stream *chain, int color_transform) state->chain = chain; state->color_transform = color_transform; state->init = 0; + state->factor = factor; } fz_catch(ctx) { diff --git a/fitz/fitz.h b/fitz/fitz.h index d2672031..856df7e5 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -504,7 +504,7 @@ extern char *fz_optarg; typedef struct fz_hash_table_s fz_hash_table; -fz_hash_table *fz_new_hash_table(fz_context *ctx, int initialsize, int keylen); +fz_hash_table *fz_new_hash_table(fz_context *ctx, int initialsize, int keylen, int lock); void fz_debug_hash(fz_context *ctx, fz_hash_table *table); void fz_empty_hash(fz_context *ctx, fz_hash_table *table); void fz_free_hash(fz_context *ctx, fz_hash_table *table); @@ -810,6 +810,36 @@ enum { FZ_STORE_DEFAULT = 256 << 20, }; +typedef struct fz_store_hash_s fz_store_hash; + +struct fz_store_hash_s +{ + fz_store_free_fn *free; + union + { + struct + { + int i0; + int i1; + } i; + struct + { + void *ptr; + int i; + } pi; + } u; +}; + +typedef struct fz_store_type_s fz_store_type; + +struct fz_store_type_s +{ + int (*make_hash_key)(fz_store_hash *, void *); + void *(*keep_key)(fz_context *,void *); + void (*drop_key)(fz_context *,void *); + int (*cmp_key)(void *, void *); +}; + void fz_new_store_context(fz_context *ctx, unsigned int max); void fz_drop_store_context(fz_context *ctx); fz_store *fz_store_keep(fz_context *ctx); @@ -818,9 +848,9 @@ void fz_debug_store(fz_context *ctx); void *fz_keep_storable(fz_context *, fz_storable *); void fz_drop_storable(fz_context *, fz_storable *); -void fz_store_item(fz_context *ctx, fz_obj *key, void *val, unsigned int itemsize); -void *fz_find_item(fz_context *ctx, fz_store_free_fn *freefn, fz_obj *key); -void fz_remove_item(fz_context *ctx, fz_store_free_fn *freefn, fz_obj *key); +void *fz_store_item(fz_context *ctx, void *key, void *val, unsigned int itemsize, fz_store_type *type); +void *fz_find_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type); +void fz_remove_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type); void fz_empty_store(fz_context *ctx); int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase); @@ -959,6 +989,7 @@ fz_stream *fz_open_a85d(fz_stream *chain); fz_stream *fz_open_ahxd(fz_stream *chain); fz_stream *fz_open_rld(fz_stream *chain); fz_stream *fz_open_dctd(fz_stream *chain, int color_transform); +fz_stream *fz_open_resized_dctd(fz_stream *chain, int color_transform, int factor); fz_stream *fz_open_faxd(fz_stream *chain, int k, int end_of_line, int encoded_byte_align, int columns, int rows, int end_of_block, int black_is_1); @@ -1023,6 +1054,27 @@ void fz_write_pnm(fz_context *ctx, fz_pixmap *pixmap, char *filename); void fz_write_pam(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha); void fz_write_png(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha); +/* + * Images are either a reference to a pixmap, or details of how to make + * a pixmap. To know how to make a pixmap we need a block of compressed + * data (typically a stream decoded all the way until the image filter) + * and then the details of the params for the filter to do the final step. + */ +typedef struct fz_image_s fz_image; + +struct fz_image_s +{ + fz_storable storable; + int w, h; + fz_image *mask; + fz_colorspace *colorspace; + fz_pixmap *(*get_pixmap)(fz_context *, fz_image *, int w, int h); +}; + +fz_pixmap *fz_image_to_pixmap(fz_context *, fz_image *, int w, int h); +void fz_drop_image(fz_context *ctx, fz_image *image); +fz_image *fz_keep_image(fz_context *ctx, fz_image *image); + fz_pixmap *fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *cs); fz_pixmap *fz_load_jpeg(fz_context *doc, unsigned char *data, int size); fz_pixmap *fz_load_png(fz_context *doc, unsigned char *data, int size); @@ -1413,9 +1465,9 @@ struct fz_device_s void (*ignore_text)(fz_device *, fz_text *, fz_matrix); void (*fill_shade)(fz_device *, fz_shade *shd, fz_matrix ctm, float alpha); - void (*fill_image)(fz_device *, fz_pixmap *img, fz_matrix ctm, float alpha); - void (*fill_image_mask)(fz_device *, fz_pixmap *img, fz_matrix ctm, fz_colorspace *, float *color, float alpha); - void (*clip_image_mask)(fz_device *, fz_pixmap *img, fz_rect *rect, fz_matrix ctm); + void (*fill_image)(fz_device *, fz_image *img, fz_matrix ctm, float alpha); + void (*fill_image_mask)(fz_device *, fz_image *img, fz_matrix ctm, fz_colorspace *, float *color, float alpha); + void (*clip_image_mask)(fz_device *, fz_image *img, fz_rect *rect, fz_matrix ctm); void (*pop_clip)(fz_device *); @@ -1439,9 +1491,9 @@ void fz_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, void fz_ignore_text(fz_device *dev, fz_text *text, fz_matrix ctm); void fz_pop_clip(fz_device *dev); void fz_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha); -void fz_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha); -void fz_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); -void fz_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm); +void fz_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha); +void fz_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); +void fz_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm); void fz_begin_mask(fz_device *dev, fz_rect area, int luminosity, fz_colorspace *colorspace, float *bc); void fz_end_mask(fz_device *dev); void fz_begin_group(fz_device *dev, fz_rect area, int isolated, int knockout, int blendmode, float alpha); diff --git a/fitz/res_colorspace.c b/fitz/res_colorspace.c index 413da3ae..c25fc99f 100644 --- a/fitz/res_colorspace.c +++ b/fitz/res_colorspace.c @@ -465,7 +465,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) fz_hash_table *lookup; unsigned char *color; - lookup = fz_new_hash_table(ctx, 509, srcn); + lookup = fz_new_hash_table(ctx, 509, srcn, -1); for (y = 0; y < src->h; y++) { diff --git a/fitz/res_pixmap.c b/fitz/res_pixmap.c index 500eb4be..328a8c2d 100644 --- a/fitz/res_pixmap.c +++ b/fitz/res_pixmap.c @@ -551,3 +551,23 @@ fz_pixmap_size(fz_context *ctx, fz_pixmap * pix) return 0; return sizeof(*pix) + pix->n * pix->w * pix->h; } + +fz_pixmap * +fz_image_to_pixmap(fz_context *ctx, fz_image *image, int w, int h) +{ + if (image == NULL) + return NULL; + return image->get_pixmap(ctx, image, w, h); +} + +fz_image * +fz_keep_image(fz_context *ctx, fz_image *image) +{ + return (fz_image *)fz_keep_storable(ctx, &image->storable); +} + +void +fz_drop_image(fz_context *ctx, fz_image *image) +{ + fz_drop_storable(ctx, &image->storable); +} diff --git a/fitz/res_store.c b/fitz/res_store.c index 5eec1f0d..461ea7c8 100644 --- a/fitz/res_store.c +++ b/fitz/res_store.c @@ -1,21 +1,14 @@ #include "fitz.h" -#include "mupdf.h" struct fz_item_s { - fz_obj *key; + void *key; fz_storable *val; unsigned int size; fz_item *next; fz_item *prev; fz_store *store; -}; - -struct refkey -{ - fz_store_free_fn *free; - int num; - int gen; + fz_store_type *type; }; struct fz_store_s @@ -43,7 +36,7 @@ fz_new_store_context(fz_context *ctx, unsigned int max) store = fz_malloc_struct(ctx, fz_store); fz_try(ctx) { - store->hash = fz_new_hash_table(ctx, 4096, sizeof(struct refkey)); + store->hash = fz_new_hash_table(ctx, 4096, sizeof(fz_store_hash), FZ_LOCK_ALLOC); } fz_catch(ctx) { @@ -114,20 +107,19 @@ evict(fz_context *ctx, fz_item *item) store->head = item->next; /* Drop a reference to the value (freeing if required) */ drop = (item->val->refs > 0 && --item->val->refs == 0); - fz_unlock(ctx, FZ_LOCK_ALLOC); /* Remove from the hash table */ - if (fz_is_indirect(item->key)) + if (item->type->make_hash_key) { - struct refkey refkey; - refkey.free = item->val->free; - refkey.num = fz_to_num(item->key); - refkey.gen = fz_to_gen(item->key); - fz_hash_remove(ctx, store->hash, &refkey); + fz_store_hash hash = { NULL }; + hash.free = item->val->free; + if (item->type->make_hash_key(&hash, item->key)) + fz_hash_remove(ctx, store->hash, &hash); } + fz_unlock(ctx, FZ_LOCK_ALLOC); if (drop) item->val->free(ctx, item->val); /* Always drops the key and free the item */ - fz_drop_obj(item->key); + item->type->drop_key(ctx, item->key); fz_free(ctx, item); fz_lock(ctx, FZ_LOCK_ALLOC); } @@ -194,30 +186,21 @@ ensure_space(fz_context *ctx, unsigned int tofree) return count; } -void -fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) +void * +fz_store_item(fz_context *ctx, void *key, void *val_, unsigned int itemsize, fz_store_type *type) { fz_item *item = NULL; unsigned int size; fz_storable *val = (fz_storable *)val_; fz_store *store = ctx->store; - int indirect; - struct refkey refkey; + fz_store_hash hash = { NULL }; + int use_hash = 0; if (!store) - return; + return NULL; fz_var(item); - /* Form the key before we take the lock */ - indirect = fz_is_indirect(key); - if (indirect) - { - refkey.free = val->free; - refkey.num = fz_to_num(key); - refkey.gen = fz_to_gen(key); - } - /* If we fail for any reason, we swallow the exception and continue. * All that the above program will see is that we failed to store * the item. */ @@ -227,9 +210,16 @@ fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) } fz_catch(ctx) { - return; + return NULL; } + if (type->make_hash_key) + { + hash.free = val->free; + use_hash = type->make_hash_key(&hash, key); + } + + type->keep_key(ctx, key); fz_lock(ctx, FZ_LOCK_ALLOC); if (store->max != FZ_STORE_UNLIMITED) { @@ -242,34 +232,41 @@ fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) /* Failed to free any space */ fz_unlock(ctx, FZ_LOCK_ALLOC); fz_free(ctx, item); - return; + type->drop_key(ctx, key); + return NULL; } } } store->size += itemsize; - item->key = fz_keep_obj(key); + item->key = key; item->val = val; item->size = itemsize; item->next = NULL; + item->type = type; /* If we can index it fast, put it into the hash table */ - if (indirect) + if (use_hash) { - fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_pixmap *existing; + fz_try(ctx) { - fz_hash_insert(ctx, store->hash, &refkey, item); + /* May drop and retake the lock */ + existing = fz_hash_insert(ctx, store->hash, &hash, item); } fz_catch(ctx) { - fz_free(ctx, item); - fz_lock(ctx, FZ_LOCK_ALLOC); store->size -= itemsize; fz_unlock(ctx, FZ_LOCK_ALLOC); - return; + fz_free(ctx, item); + return NULL; + } + if (existing) + { + fz_unlock(ctx, FZ_LOCK_ALLOC); + return existing; } - fz_lock(ctx, FZ_LOCK_ALLOC); } /* Now we can never fail, bump the ref */ if (val->refs > 0) @@ -283,15 +280,17 @@ fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) store->head = item; item->prev = NULL; fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; } void * -fz_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) +fz_find_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type) { - struct refkey refkey; fz_item *item; fz_store *store = ctx->store; - int indirect; + fz_store_hash hash = { NULL }; + int use_hash = 0; if (!store) return NULL; @@ -299,27 +298,24 @@ fz_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) if (!key) return NULL; - /* Form the key before we take the lock */ - indirect = fz_is_indirect(key); - if (indirect) + if (type->make_hash_key) { - refkey.free = free; - refkey.num = fz_to_num(key); - refkey.gen = fz_to_gen(key); + hash.free = free; + use_hash = type->make_hash_key(&hash, key); } fz_lock(ctx, FZ_LOCK_ALLOC); - if (indirect) + if (use_hash) { /* We can find objects keyed on indirected objects quickly */ - item = fz_hash_find(ctx, store->hash, &refkey); + item = fz_hash_find(ctx, store->hash, &hash); } else { /* Others we have to hunt for slowly */ for (item = store->head; item; item = item->next) { - if (item->val->free == free && !fz_objcmp(item->key, key)) + if (item->val->free == free && !type->cmp_key(item->key, key)) break; } } @@ -355,35 +351,33 @@ fz_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) } void -fz_remove_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) +fz_remove_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type) { - struct refkey refkey; fz_item *item; fz_store *store = ctx->store; - int drop, indirect; + int drop; + fz_store_hash hash; + int use_hash = 0; - /* Form the key before we take the lock */ - indirect = fz_is_indirect(key); - if (indirect) + if (type->make_hash_key) { - refkey.free = free; - refkey.num = fz_to_num(key); - refkey.gen = fz_to_gen(key); + hash.free = free; + use_hash = type->make_hash_key(&hash, key); } fz_lock(ctx, FZ_LOCK_ALLOC); - if (indirect) + if (use_hash) { /* We can find objects keyed on indirect objects quickly */ - item = fz_hash_find(ctx, store->hash, &refkey); + item = fz_hash_find(ctx, store->hash, &hash); if (item) - fz_hash_remove(ctx, store->hash, &refkey); + fz_hash_remove(ctx, store->hash, &hash); } else { /* Others we have to hunt for slowly */ for (item = store->head; item; item = item->next) - if (item->val->free == free && !fz_objcmp(item->key, key)) + if (item->val->free == free && !type->cmp_key(item->key, key)) break; } if (item) @@ -400,7 +394,7 @@ fz_remove_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) fz_unlock(ctx, FZ_LOCK_ALLOC); if (drop) item->val->free(ctx, item->val); - fz_drop_obj(item->key); + type->drop_key(ctx, item->key); fz_free(ctx, item); } else diff --git a/fitz/stm_read.c b/fitz/stm_read.c index 2066b14c..81411c54 100644 --- a/fitz/stm_read.c +++ b/fitz/stm_read.c @@ -101,7 +101,7 @@ fz_read_all(fz_stream *stm, int initial) if (initial < 1024) initial = 1024; - buf = fz_new_buffer(ctx, initial); + buf = fz_new_buffer(ctx, initial+1); while (1) { @@ -110,7 +110,6 @@ fz_read_all(fz_stream *stm, int initial) if (buf->len / 200 > initial) { - fz_drop_buffer(ctx, buf); fz_throw(ctx, "compression bomb detected"); } diff --git a/pdf/mupdf.h b/pdf/mupdf.h index 39245177..b233288f 100644 --- a/pdf/mupdf.h +++ b/pdf/mupdf.h @@ -5,6 +5,83 @@ #error "fitz.h must be included before mupdf.h" #endif +/* + * PDF Images + */ + +typedef struct pdf_image_params_s pdf_image_params; + +struct pdf_image_params_s +{ + int type; + fz_colorspace *colorspace; + union + { + struct + { + int columns; + int rows; + int k; + int eol; + int eba; + int eob; + int bi1; + } + fax; + struct + { + int ct; + } + jpeg; + struct + { + int columns; + int colors; + int predictor; + int bpc; + } + flate; + struct + { + int columns; + int colors; + int predictor; + int bpc; + int ec; + } + lzw; + } + u; +}; + + +typedef struct pdf_image_s pdf_image; + +struct pdf_image_s +{ + fz_image base; + fz_pixmap *tile; + int n, bpc; + pdf_image_params params; + fz_buffer *buffer; + int colorkey[FZ_MAX_COLORS * 2]; + float decode[FZ_MAX_COLORS * 2]; + int imagemask; + int interpolate; + int usecolorkey; +}; + +enum +{ + PDF_IMAGE_RAW, + PDF_IMAGE_FAX, + PDF_IMAGE_JPEG, + PDF_IMAGE_RLD, + PDF_IMAGE_FLATE, + PDF_IMAGE_LZW, + PDF_IMAGE_JPX +}; + typedef struct pdf_document_s pdf_document; /* @@ -102,12 +179,15 @@ fz_obj *pdf_load_object(pdf_document *doc, int num, int gen); void pdf_update_object(pdf_document *doc, int num, int gen, fz_obj *newobj); int pdf_is_stream(pdf_document *doc, int num, int gen); -fz_stream *pdf_open_inline_stream(pdf_document *doc, fz_obj *stmobj, int length, fz_stream *chain); +fz_stream *pdf_open_inline_stream(pdf_document *doc, fz_obj *stmobj, int length, fz_stream *chain, pdf_image_params *params); fz_buffer *pdf_load_raw_stream(pdf_document *doc, int num, int gen); fz_buffer *pdf_load_stream(pdf_document *doc, int num, int gen); +fz_buffer *pdf_load_image_stream(pdf_document *doc, int num, int gen, pdf_image_params *params); fz_stream *pdf_open_raw_stream(pdf_document *doc, int num, int gen); +fz_stream *pdf_open_image_stream(pdf_document *doc, int num, int gen, pdf_image_params *params); fz_stream *pdf_open_stream(pdf_document *doc, int num, int gen); fz_stream *pdf_open_stream_with_offset(pdf_document *doc, int num, int gen, fz_obj *dict, int stm_ofs); +fz_stream *pdf_open_image_decomp_stream(fz_context *ctx, fz_buffer *, pdf_image_params *params, int *factor); pdf_document *pdf_open_document_with_stream(fz_stream *file); pdf_document *pdf_open_document(fz_context *ctx, const char *filename); @@ -171,8 +251,8 @@ fz_pixmap *pdf_expand_indexed_pixmap(fz_context *ctx, fz_pixmap *src); fz_shade *pdf_load_shading(pdf_document *doc, fz_obj *obj); -fz_pixmap *pdf_load_inline_image(pdf_document *doc, fz_obj *rdb, fz_obj *dict, fz_stream *file); -fz_pixmap *pdf_load_image(pdf_document *doc, fz_obj *obj); +fz_image *pdf_load_inline_image(pdf_document *doc, fz_obj *rdb, fz_obj *dict, fz_stream *file); +fz_image *pdf_load_image(pdf_document *doc, fz_obj *obj); int pdf_is_jpx_image(fz_context *ctx, fz_obj *dict); /* @@ -471,4 +551,12 @@ void pdf_run_page_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, void pdf_run_page(pdf_document *doc, pdf_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie); void pdf_run_glyph(pdf_document *doc, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm, void *gstate); +/* + * PDF interface to store + */ + +void pdf_store_item(fz_context *ctx, fz_obj *key, void *val, unsigned int itemsize); +void *pdf_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key); +void pdf_remove_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key); + #endif diff --git a/pdf/pdf_cmap_load.c b/pdf/pdf_cmap_load.c index 3257516c..9e6b13ce 100644 --- a/pdf/pdf_cmap_load.c +++ b/pdf/pdf_cmap_load.c @@ -31,7 +31,7 @@ pdf_load_embedded_cmap(pdf_document *xref, fz_obj *stmobj) fz_var(file); fz_var(cmap); - if ((cmap = fz_find_item(ctx, pdf_free_cmap_imp, stmobj))) + if ((cmap = pdf_find_item(ctx, pdf_free_cmap_imp, stmobj))) { return cmap; } @@ -63,7 +63,7 @@ pdf_load_embedded_cmap(pdf_document *xref, fz_obj *stmobj) pdf_drop_cmap(ctx, usecmap); } - fz_store_item(ctx, stmobj, cmap, pdf_cmap_size(ctx, cmap)); + pdf_store_item(ctx, stmobj, cmap, pdf_cmap_size(ctx, cmap)); } fz_catch(ctx) { diff --git a/pdf/pdf_colorspace.c b/pdf/pdf_colorspace.c index 7a410df8..a6515d42 100644 --- a/pdf/pdf_colorspace.c +++ b/pdf/pdf_colorspace.c @@ -395,7 +395,7 @@ pdf_load_colorspace(pdf_document *xref, fz_obj *obj) fz_context *ctx = xref->ctx; fz_colorspace *cs; - if ((cs = fz_find_item(ctx, fz_free_colorspace_imp, obj))) + if ((cs = pdf_find_item(ctx, fz_free_colorspace_imp, obj))) { return cs; } @@ -403,7 +403,7 @@ pdf_load_colorspace(pdf_document *xref, fz_obj *obj) cs = pdf_load_colorspace_imp(xref, obj); /* RJW: "cannot load colorspace (%d %d R)", fz_to_num(obj), fz_to_gen(obj) */ - fz_store_item(ctx, obj, cs, cs->size); + pdf_store_item(ctx, obj, cs, cs->size); return cs; } diff --git a/pdf/pdf_font.c b/pdf/pdf_font.c index 3e30c332..fd2afab2 100644 --- a/pdf/pdf_font.c +++ b/pdf/pdf_font.c @@ -1081,7 +1081,7 @@ pdf_load_font(pdf_document *xref, fz_obj *rdb, fz_obj *dict) fz_context *ctx = xref->ctx; pdf_font_desc *fontdesc; - if ((fontdesc = fz_find_item(ctx, pdf_free_font_imp, dict))) + if ((fontdesc = pdf_find_item(ctx, pdf_free_font_imp, dict))) { return fontdesc; } @@ -1121,7 +1121,7 @@ pdf_load_font(pdf_document *xref, fz_obj *rdb, fz_obj *dict) if (fontdesc->font->ft_substitute && !fontdesc->to_ttf_cmap) pdf_make_width_table(ctx, fontdesc); - fz_store_item(ctx, dict, fontdesc, fontdesc->size); + pdf_store_item(ctx, dict, fontdesc, fontdesc->size); return fontdesc; } diff --git a/pdf/pdf_function.c b/pdf/pdf_function.c index 23acc4db..4478827c 100644 --- a/pdf/pdf_function.c +++ b/pdf/pdf_function.c @@ -1362,7 +1362,7 @@ pdf_load_function(pdf_document *xref, fz_obj *dict) fz_obj *obj; int i; - if ((func = fz_find_item(ctx, pdf_free_function_imp, dict))) + if ((func = pdf_find_item(ctx, pdf_free_function_imp, dict))) { return func; } @@ -1432,7 +1432,7 @@ pdf_load_function(pdf_document *xref, fz_obj *dict) fz_throw(ctx, "unknown function type (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); } - fz_store_item(ctx, dict, func, func->size); + pdf_store_item(ctx, dict, func, func->size); } fz_catch(ctx) { diff --git a/pdf/pdf_image.c b/pdf/pdf_image.c index 3aa26f08..9626d6f0 100644 --- a/pdf/pdf_image.c +++ b/pdf/pdf_image.c @@ -1,10 +1,15 @@ #include "fitz.h" #include "mupdf.h" -/* TODO: store JPEG compressed samples */ -/* TODO: store flate compressed samples */ +typedef struct pdf_image_key_s pdf_image_key; -static fz_pixmap *pdf_load_jpx(pdf_document *xref, fz_obj *dict); +struct pdf_image_key_s { + int refs; + fz_image *image; + int factor; +}; + +static void pdf_load_jpx(pdf_document *xref, fz_obj *dict, pdf_image *image); static void pdf_mask_color_key(fz_pixmap *pix, int n, int *colorkey) @@ -25,49 +30,293 @@ pdf_mask_color_key(fz_pixmap *pix, int n, int *colorkey) } } +static int +pdf_make_hash_image_key(fz_store_hash *hash, void *key_) +{ + pdf_image_key *key = (pdf_image_key *)key_; + + hash->u.pi.ptr = key->image; + hash->u.pi.i = key->factor; + return 1; +} + +static void * +pdf_keep_image_key(fz_context *ctx, void *key_) +{ + pdf_image_key *key = (pdf_image_key *)key_; + + fz_lock(ctx, FZ_LOCK_ALLOC); + key->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return (void *)key; +} + +static void +pdf_drop_image_key(fz_context *ctx, void *key_) +{ + pdf_image_key *key = (pdf_image_key *)key_; + int drop; + + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = --key->refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop == 0) + { + fz_drop_image(ctx, key->image); + fz_free(ctx, key); + } +} + +static int +pdf_cmp_image_key(void *k0_, void *k1_) +{ + pdf_image_key *k0 = (pdf_image_key *)k0_; + pdf_image_key *k1 = (pdf_image_key *)k1_; + + return k0->image == k1->image && k0->factor == k1->factor; +} + +static fz_store_type pdf_image_store_type = +{ + pdf_make_hash_image_key, + pdf_keep_image_key, + pdf_drop_image_key, + pdf_cmp_image_key +}; + static fz_pixmap * +decomp_image_from_stream(fz_context *ctx, fz_stream *stm, pdf_image *image, int in_line, int indexed, int factor) +{ + fz_pixmap *tile = NULL; + fz_pixmap *existing_tile; + int stride, len, i; + unsigned char *samples = NULL; + int w = (image->base.w + (factor-1)) / factor; + int h = (image->base.h + (factor-1)) / factor; + pdf_image_key *key; + + fz_var(tile); + fz_var(samples); + + fz_try(ctx) + { + tile = fz_new_pixmap(ctx, image->base.colorspace, w, h); + tile->mask = fz_image_to_pixmap(ctx, image->base.mask, w, h); + tile->interpolate = image->interpolate; + + stride = (w * image->n * image->bpc + 7) / 8; + + samples = fz_malloc_array(ctx, h, stride); + + len = fz_read(stm, samples, h * stride); + if (len < 0) + { + fz_throw(ctx, "cannot read image data"); + } + + /* Make sure we read the EOF marker (for inline images only) */ + if (in_line) + { + unsigned char tbuf[512]; + fz_try(ctx) + { + int tlen = fz_read(stm, tbuf, sizeof tbuf); + if (tlen > 0) + fz_warn(ctx, "ignoring garbage at end of image"); + } + fz_catch(ctx) + { + fz_warn(ctx, "ignoring error at end of image"); + } + } + + /* Pad truncated images */ + if (len < stride * h) + { + fz_warn(ctx, "padding truncated image"); + memset(samples + len, 0, stride * h - len); + } + + /* Invert 1-bit image masks */ + if (image->imagemask) + { + /* 0=opaque and 1=transparent so we need to invert */ + unsigned char *p = samples; + len = h * stride; + for (i = 0; i < len; i++) + p[i] = ~p[i]; + } + + fz_unpack_tile(tile, samples, image->n, image->bpc, stride, indexed); + + fz_free(ctx, samples); + samples = NULL; + + if (image->usecolorkey) + pdf_mask_color_key(tile, image->n, image->colorkey); + + if (indexed) + { + fz_pixmap *conv; + fz_decode_indexed_tile(tile, image->decode, (1 << image->bpc) - 1); + conv = pdf_expand_indexed_pixmap(ctx, tile); + fz_drop_pixmap(ctx, tile); + tile = conv; + } + else + { + fz_decode_tile(tile, image->decode); + } + } + fz_always(ctx) + { + fz_close(stm); + } + fz_catch(ctx) + { + if (tile) + fz_drop_pixmap(ctx, tile); + fz_free(ctx, samples); + + fz_rethrow(ctx); + } + + /* Now we try to cache the pixmap. Any failure here will just result + * in us not caching. */ + fz_try(ctx) + { + key = fz_malloc_struct(ctx, pdf_image_key); + key->refs = 1; + key->image = fz_keep_image(ctx, &image->base); + key->factor = factor; + existing_tile = fz_store_item(ctx, key, tile, fz_pixmap_size(ctx, tile), &pdf_image_store_type); + if (existing_tile) + { + /* We already have a tile. This must have been produced by a + * racing thread. We'll throw away ours and use that one. */ + fz_drop_pixmap(ctx, tile); + fz_free(ctx, key); + tile = existing_tile; + } + } + fz_always(ctx) + { + pdf_drop_image_key(ctx, key); + } + fz_catch(ctx) + { + /* Do nothing */ + } + + return tile; +} + +static void +pdf_free_image(fz_context *ctx, fz_storable *image_) +{ + pdf_image *image = (pdf_image *)image_; + + if (image == NULL) + return; + fz_drop_pixmap(ctx, image->tile); + fz_drop_buffer(ctx, image->buffer); + fz_drop_colorspace(ctx, image->base.colorspace); + fz_drop_image(ctx, image->base.mask); + fz_free(ctx, image); +} + +static fz_pixmap * +pdf_image_get_pixmap(fz_context *ctx, fz_image *image_, int w, int h) +{ + pdf_image *image = (pdf_image *)image_; + fz_pixmap *tile; + fz_stream *stm; + int factor; + pdf_image_key key; + + /* Check for 'simple' images which are just pixmaps */ + if (image->buffer == NULL) + { + tile = image->tile; + if (!tile) + return NULL; + if (image->base.mask) + { + fz_drop_pixmap(ctx, tile->mask); + tile->mask = fz_image_to_pixmap(ctx, image->base.mask, w, h); + } + return fz_keep_pixmap(ctx, tile); /* That's all we can give you! */ + } + + /* Ensure our expectations for tile size are reasonable */ + if (w > image->base.w) + w = image->base.w; + if (h > image->base.h) + h = image->base.h; + + /* What is our ideal factor? */ + if (w == 0 || h == 0) + factor = 1; + else + for (factor=1; image->base.w/(2*factor) >= w && image->base.h/(2*factor) >= h && factor < 8; factor *= 2); + + /* Can we find any suitable tiles in the cache? */ + key.refs = 1; + key.image = &image->base; + key.factor = factor; + do + { + tile = fz_find_item(ctx, fz_free_pixmap_imp, &key, &pdf_image_store_type); + if (tile) + return tile; + key.factor >>= 1; + } + while (key.factor > 0); + + /* We need to make a new one. */ + stm = pdf_open_image_decomp_stream(ctx, image->buffer, &image->params, &factor); + + return decomp_image_from_stream(ctx, stm, image, 0, 0, factor); +} + +static pdf_image * pdf_load_image_imp(pdf_document *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cstm, int forcemask) { fz_stream *stm = NULL; - fz_pixmap *tile = NULL; + pdf_image *image = NULL; fz_obj *obj, *res; int w, h, bpc, n; int imagemask; int interpolate; int indexed; - fz_colorspace *colorspace = NULL; - fz_pixmap *mask = NULL; /* explicit mask/softmask image */ + fz_image *mask = NULL; /* explicit mask/softmask image */ int usecolorkey; - int colorkey[FZ_MAX_COLORS * 2]; - float decode[FZ_MAX_COLORS * 2]; - int stride; - unsigned char *samples = NULL; - int i, len; + int i; fz_context *ctx = xref->ctx; fz_var(stm); - fz_var(tile); - fz_var(colorspace); fz_var(mask); - fz_var(samples); + + image = fz_malloc_struct(ctx, pdf_image); fz_try(ctx) { /* special case for JPEG2000 images */ if (pdf_is_jpx_image(ctx, dict)) { - tile = pdf_load_jpx(xref, dict); + pdf_load_jpx(xref, dict, image); /* RJW: "cannot load jpx image" */ if (forcemask) { - if (tile->n != 2) + fz_pixmap *mask_pixmap; + if (image->n != 2) fz_throw(ctx, "softmask must be grayscale"); - mask = fz_alpha_from_gray(ctx, tile, 1); - fz_drop_pixmap(ctx, tile); - tile = mask; - mask = NULL; + mask_pixmap = fz_alpha_from_gray(ctx, image->tile, 1); + fz_drop_pixmap(ctx, image->tile); + image->tile = mask_pixmap; } break; /* Out of fz_try */ } @@ -109,13 +358,13 @@ pdf_load_image_imp(pdf_document *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cst obj = res; } - colorspace = pdf_load_colorspace(xref, obj); + image->base.colorspace = pdf_load_colorspace(xref, obj); /* RJW: "cannot load image colorspace" */ - if (!strcmp(colorspace->name, "Indexed")) + if (!strcmp(image->base.colorspace->name, "Indexed")) indexed = 1; - n = colorspace->n; + n = image->base.colorspace->n; } else { @@ -126,13 +375,13 @@ pdf_load_image_imp(pdf_document *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cst if (obj) { for (i = 0; i < n * 2; i++) - decode[i] = fz_to_real(fz_array_get(obj, i)); + image->decode[i] = fz_to_real(fz_array_get(obj, i)); } else { float maxval = indexed ? (1 << bpc) - 1 : 1; for (i = 0; i < n * 2; i++) - decode[i] = i & 1 ? maxval : 0; + image->decode[i] = i & 1 ? maxval : 0; } obj = fz_dict_getsa(dict, "SMask", "Mask"); @@ -141,7 +390,7 @@ pdf_load_image_imp(pdf_document *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cst /* Not allowed for inline images */ if (!cstm) { - mask = pdf_load_image_imp(xref, rdb, obj, NULL, 1); + mask = (fz_image *)pdf_load_image_imp(xref, rdb, obj, NULL, 1); /* RJW: "cannot load image mask/softmask" */ } } @@ -155,36 +404,36 @@ pdf_load_image_imp(pdf_document *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cst fz_warn(ctx, "invalid value in color key mask"); usecolorkey = 0; } - colorkey[i] = fz_to_int(fz_array_get(obj, i)); + image->colorkey[i] = fz_to_int(fz_array_get(obj, i)); } } - /* Allocate now, to fail early if we run out of memory */ - fz_try(ctx) - { - tile = fz_new_pixmap(ctx, colorspace, w, h); - } - fz_catch(ctx) + /* Now, do we load a ref, or do we load the actual thing? */ + image->params.type = PDF_IMAGE_RAW; + FZ_INIT_STORABLE(&image->base, 1, pdf_free_image); + image->base.get_pixmap = pdf_image_get_pixmap; + image->base.w = w; + image->base.h = h; + image->n = n; + image->bpc = bpc; + image->interpolate = interpolate; + image->imagemask = imagemask; + image->usecolorkey = usecolorkey; + image->base.mask = mask; + image->params.colorspace = image->base.colorspace; /* Uses the same ref as for the base one */ + if (!indexed && !cstm) { - fz_drop_colorspace(ctx, colorspace); - fz_rethrow(ctx); - } - - if (colorspace) - { - fz_drop_colorspace(ctx, colorspace); - colorspace = NULL; + /* Just load the compressed image data now and we can + * decode it on demand. */ + image->buffer = pdf_load_image_stream(xref, fz_to_num(dict), fz_to_gen(dict), &image->params); + break; /* Out of fz_try */ } - tile->mask = mask; - mask = NULL; - tile->interpolate = interpolate; - - stride = (w * n * bpc + 7) / 8; - + /* We need to decompress the image now */ if (cstm) { - stm = pdf_open_inline_stream(xref, dict, stride * h, cstm); + int stride = (w * image->n * image->bpc + 7) / 8; + stm = pdf_open_inline_stream(xref, dict, stride * h, cstm, NULL); } else { @@ -192,92 +441,20 @@ pdf_load_image_imp(pdf_document *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cst /* RJW: "cannot open image data stream (%d 0 R)", fz_to_num(dict) */ } - samples = fz_malloc_array(ctx, h, stride); - - len = fz_read(stm, samples, h * stride); - if (len < 0) - { - fz_throw(ctx, "cannot read image data"); - } - - /* Make sure we read the EOF marker (for inline images only) */ - if (cstm) - { - unsigned char tbuf[512]; - fz_try(ctx) - { - int tlen = fz_read(stm, tbuf, sizeof tbuf); - if (tlen > 0) - fz_warn(ctx, "ignoring garbage at end of image"); - } - fz_catch(ctx) - { - fz_warn(ctx, "ignoring error at end of image"); - } - } - - fz_close(stm); - stm = NULL; - - /* Pad truncated images */ - if (len < stride * h) - { - fz_warn(ctx, "padding truncated image (%d 0 R)", fz_to_num(dict)); - memset(samples + len, 0, stride * h - len); - } - - /* Invert 1-bit image masks */ - if (imagemask) - { - /* 0=opaque and 1=transparent so we need to invert */ - unsigned char *p = samples; - len = h * stride; - for (i = 0; i < len; i++) - p[i] = ~p[i]; - } - - fz_unpack_tile(tile, samples, n, bpc, stride, indexed); - - fz_free(ctx, samples); - samples = NULL; - - if (usecolorkey) - pdf_mask_color_key(tile, n, colorkey); - - if (indexed) - { - fz_pixmap *conv; - fz_decode_indexed_tile(tile, decode, (1 << bpc) - 1); - conv = pdf_expand_indexed_pixmap(ctx, tile); - fz_drop_pixmap(ctx, tile); - tile = conv; - } - else - { - fz_decode_tile(tile, decode); - } + image->tile = decomp_image_from_stream(ctx, stm, image, cstm != NULL, indexed, 1); } fz_catch(ctx) { - if (colorspace) - fz_drop_colorspace(ctx, colorspace); - if (mask) - fz_drop_pixmap(ctx, mask); - if (tile) - fz_drop_pixmap(ctx, tile); - fz_close(stm); - fz_free(ctx, samples); - + fz_drop_image(ctx, &image->base); fz_rethrow(ctx); } - - return tile; + return image; } -fz_pixmap * +fz_image * pdf_load_inline_image(pdf_document *xref, fz_obj *rdb, fz_obj *dict, fz_stream *file) { - return pdf_load_image_imp(xref, rdb, dict, file, 0); + return (fz_image *)pdf_load_image_imp(xref, rdb, dict, file, 0); /* RJW: "cannot load inline image" */ } @@ -297,8 +474,8 @@ pdf_is_jpx_image(fz_context *ctx, fz_obj *dict) return 0; } -static fz_pixmap * -pdf_load_jpx(pdf_document *xref, fz_obj *dict) +static void +pdf_load_jpx(pdf_document *xref, fz_obj *dict, pdf_image *image) { fz_buffer *buf = NULL; fz_colorspace *colorspace = NULL; @@ -328,18 +505,13 @@ pdf_load_jpx(pdf_document *xref, fz_obj *dict) img = fz_load_jpx(ctx, buf->data, buf->len, colorspace); /* RJW: "cannot load jpx image" */ - if (colorspace) - { - fz_drop_colorspace(ctx, colorspace); - colorspace = NULL; - } fz_drop_buffer(ctx, buf); buf = NULL; obj = fz_dict_getsa(dict, "SMask", "Mask"); if (fz_is_dict(obj)) { - img->mask = pdf_load_image_imp(xref, NULL, obj, NULL, 1); + image->base.mask = (fz_image *)pdf_load_image_imp(xref, NULL, obj, NULL, 1); /* RJW: "cannot load image mask/softmask" */ } @@ -363,24 +535,44 @@ pdf_load_jpx(pdf_document *xref, fz_obj *dict) fz_drop_pixmap(ctx, img); fz_rethrow(ctx); } - return img; + image->params.type = PDF_IMAGE_RAW; + FZ_INIT_STORABLE(&image->base, 1, pdf_free_image); + image->base.get_pixmap = pdf_image_get_pixmap; + image->base.w = img->w; + image->base.h = img->h; + image->base.colorspace = colorspace; + image->tile = img; + image->n = img->n; + image->bpc = 8; + image->interpolate = 0; + image->imagemask = 0; + image->usecolorkey = 0; + image->params.colorspace = colorspace; /* Uses the same ref as for the base one */ +} + +static int +pdf_image_size(fz_context *ctx, pdf_image *im) +{ + if (im == NULL) + return 0; + return sizeof(*im) + fz_pixmap_size(ctx, im->tile) + (im->buffer ? im->buffer->cap : 0); } -fz_pixmap * +fz_image * pdf_load_image(pdf_document *xref, fz_obj *dict) { fz_context *ctx = xref->ctx; - fz_pixmap *pix; + pdf_image *image; - if ((pix = fz_find_item(ctx, fz_free_pixmap_imp, dict))) + if ((image = pdf_find_item(ctx, pdf_free_image, dict))) { - return pix; + return (fz_image *)image; } - pix = pdf_load_image_imp(xref, NULL, dict, NULL, 0); + image = pdf_load_image_imp(xref, NULL, dict, NULL, 0); /* RJW: "cannot load image (%d 0 R)", fz_to_num(dict) */ - fz_store_item(ctx, dict, pix, fz_pixmap_size(ctx, pix)); + pdf_store_item(ctx, dict, image, pdf_image_size(ctx, image)); - return pix; + return (fz_image *)image; } diff --git a/pdf/pdf_interpret.c b/pdf/pdf_interpret.c index ea7f7692..effea657 100644 --- a/pdf/pdf_interpret.c +++ b/pdf/pdf_interpret.c @@ -362,7 +362,7 @@ pdf_show_shade(pdf_csi *csi, fz_shade *shd) } static void -pdf_show_image(pdf_csi *csi, fz_pixmap *image) +pdf_show_image(pdf_csi *csi, fz_image *image) { pdf_gstate *gstate = csi->gstate + csi->gtop; fz_matrix image_ctm; @@ -1627,7 +1627,7 @@ static void pdf_run_BI(pdf_csi *csi, fz_obj *rdb, fz_stream *file) int ch; char *buf = csi->xref->scratch; int buflen = sizeof(csi->xref->scratch); - fz_pixmap *img; + fz_image *img; fz_obj *obj; obj = pdf_parse_dict(csi->xref, file, buf, buflen); @@ -1645,7 +1645,7 @@ static void pdf_run_BI(pdf_csi *csi, fz_obj *rdb, fz_stream *file) pdf_show_image(csi, img); - fz_drop_pixmap(ctx, img); + fz_drop_image(ctx, img); /* find EI */ ch = fz_read_byte(file); @@ -1798,19 +1798,20 @@ static void pdf_run_Do(pdf_csi *csi, fz_obj *rdb) { if ((csi->dev->hints & FZ_IGNORE_IMAGE) == 0) { - fz_pixmap *img; - img = pdf_load_image(csi->xref, obj); + fz_image *img = pdf_load_image(csi->xref, obj); /* RJW: "cannot load image (%d %d R)", fz_to_num(obj), fz_to_gen(obj) */ fz_try(ctx) { pdf_show_image(csi, img); } + fz_always(ctx) + { + fz_drop_image(ctx, img); + } fz_catch(ctx) { - fz_drop_pixmap(ctx, img); fz_rethrow(ctx); } - fz_drop_pixmap(ctx, img); } } diff --git a/pdf/pdf_pattern.c b/pdf/pdf_pattern.c index 9ffbee02..c1869474 100644 --- a/pdf/pdf_pattern.c +++ b/pdf/pdf_pattern.c @@ -40,7 +40,7 @@ pdf_load_pattern(pdf_document *xref, fz_obj *dict) fz_obj *obj; fz_context *ctx = xref->ctx; - if ((pat = fz_find_item(ctx, pdf_free_pattern_imp, dict))) + if ((pat = pdf_find_item(ctx, pdf_free_pattern_imp, dict))) { return pat; } @@ -51,7 +51,7 @@ pdf_load_pattern(pdf_document *xref, fz_obj *dict) pat->contents = NULL; /* Store pattern now, to avoid possible recursion if objects refer back to this one */ - fz_store_item(ctx, dict, pat, pdf_pattern_size(pat)); + pdf_store_item(ctx, dict, pat, pdf_pattern_size(pat)); pat->ismask = fz_to_int(fz_dict_gets(dict, "PaintType")) == 2; pat->xstep = fz_to_real(fz_dict_gets(dict, "XStep")); @@ -76,7 +76,7 @@ pdf_load_pattern(pdf_document *xref, fz_obj *dict) } fz_catch(ctx) { - fz_remove_item(ctx, pdf_free_pattern_imp, dict); + pdf_remove_item(ctx, pdf_free_pattern_imp, dict); pdf_drop_pattern(ctx, pat); fz_throw(ctx, "cannot load pattern stream (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); } diff --git a/pdf/pdf_shade.c b/pdf/pdf_shade.c index fb5dd72c..1be2bb15 100644 --- a/pdf/pdf_shade.c +++ b/pdf/pdf_shade.c @@ -1100,7 +1100,7 @@ pdf_load_shading(pdf_document *xref, fz_obj *dict) fz_context *ctx = xref->ctx; fz_shade *shade; - if ((shade = fz_find_item(ctx, fz_free_shade_imp, dict))) + if ((shade = pdf_find_item(ctx, fz_free_shade_imp, dict))) { return shade; } @@ -1138,7 +1138,7 @@ pdf_load_shading(pdf_document *xref, fz_obj *dict) /* RJW: "cannot load shading dictionary (%d %d R)", fz_to_num(dict), fz_to_gen(dict) */ } - fz_store_item(ctx, dict, shade, fz_shade_size(shade)); + pdf_store_item(ctx, dict, shade, fz_shade_size(shade)); return shade; } diff --git a/pdf/pdf_store.c b/pdf/pdf_store.c new file mode 100644 index 00000000..4f51526d --- /dev/null +++ b/pdf/pdf_store.c @@ -0,0 +1,59 @@ +#include "fitz.h" +#include "mupdf.h" + +static int +pdf_make_hash_key(fz_store_hash *hash, void *key_) +{ + fz_obj *key = (fz_obj *)key_; + + if (!fz_is_indirect(key)) + return 0; + hash->u.i.i0 = fz_to_num(key); + hash->u.i.i1 = fz_to_gen(key); + return 1; +} + +static void * +pdf_keep_key(fz_context *ctx, void *key) +{ + return (void *)fz_keep_obj((fz_obj *)key); +} + +static void +pdf_drop_key(fz_context *ctx, void *key) +{ + fz_drop_obj((fz_obj *)key); +} + +static int +pdf_cmp_key(void *k0, void *k1) +{ + return fz_objcmp((fz_obj *)k0, (fz_obj *)k1); +} + +static fz_store_type pdf_obj_store_type = +{ + pdf_make_hash_key, + pdf_keep_key, + pdf_drop_key, + pdf_cmp_key +}; + +void +pdf_store_item(fz_context *ctx, fz_obj *key, void *val, unsigned int itemsize) +{ + fz_store_item(ctx, key, val, itemsize, &pdf_obj_store_type); +} + +void * +pdf_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) +{ + return fz_find_item(ctx, free, key, &pdf_obj_store_type); +} + +void +pdf_remove_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) +{ + fz_remove_item(ctx, free, key, &pdf_obj_store_type); +} + diff --git a/pdf/pdf_stream.c b/pdf/pdf_stream.c index c66e703e..28fc616f 100644 --- a/pdf/pdf_stream.c +++ b/pdf/pdf_stream.c @@ -49,7 +49,7 @@ pdf_stream_has_crypt(fz_context *ctx, fz_obj *stm) * Create a filter given a name and param dictionary. */ static fz_stream * -build_filter(fz_stream *chain, pdf_document * xref, fz_obj * f, fz_obj * p, int num, int gen) +build_filter(fz_stream *chain, pdf_document * xref, fz_obj * f, fz_obj * p, int num, int gen, pdf_image_params *params) { fz_context *ctx = chain->ctx; char *s = fz_to_name(f); @@ -79,6 +79,19 @@ build_filter(fz_stream *chain, pdf_document * xref, fz_obj * f, fz_obj * p, int fz_obj *rows = fz_dict_gets(p, "Rows"); fz_obj *eob = fz_dict_gets(p, "EndOfBlock"); fz_obj *bi1 = fz_dict_gets(p, "BlackIs1"); + if (params) + { + /* We will shortstop here */ + params->type = PDF_IMAGE_FAX; + params->u.fax.k = (k ? fz_to_int(k) : 0); + params->u.fax.eol = (eol ? fz_to_bool(eol) : 0); + params->u.fax.eba = (eba ? fz_to_bool(eba) : 0); + params->u.fax.columns = (columns ? fz_to_int(columns) : 1728); + params->u.fax.rows = (rows ? fz_to_int(rows) : 0); + params->u.fax.eob = (eob ? fz_to_bool(eob) : 1); + params->u.fax.bi1 = (bi1 ? fz_to_bool(bi1) : 0); + return chain; + } return fz_open_faxd(chain, k ? fz_to_int(k) : 0, eol ? fz_to_bool(eol) : 0, @@ -92,14 +105,38 @@ build_filter(fz_stream *chain, pdf_document * xref, fz_obj * f, fz_obj * p, int else if (!strcmp(s, "DCTDecode") || !strcmp(s, "DCT")) { fz_obj *ct = fz_dict_gets(p, "ColorTransform"); + if (params) + { + /* We will shortstop here */ + params->type = PDF_IMAGE_JPEG; + params->u.jpeg.ct = (ct ? fz_to_int(ct) : -1); + return chain; + } return fz_open_dctd(chain, ct ? fz_to_int(ct) : -1); } else if (!strcmp(s, "RunLengthDecode") || !strcmp(s, "RL")) + { + if (params) + { + /* We will shortstop here */ + params->type = PDF_IMAGE_RLD; + return chain; + } return fz_open_rld(chain); - + } else if (!strcmp(s, "FlateDecode") || !strcmp(s, "Fl")) { + if (params) + { + /* We will shortstop here */ + params->type = PDF_IMAGE_FLATE; + params->u.flate.predictor = predictor; + params->u.flate.columns = columns; + params->u.flate.colors = colors; + params->u.flate.bpc = bpc; + return chain; + } chain = fz_open_flated(chain); if (predictor > 1) chain = fz_open_predict(chain, predictor, columns, colors, bpc); @@ -109,6 +146,17 @@ build_filter(fz_stream *chain, pdf_document * xref, fz_obj * f, fz_obj * p, int else if (!strcmp(s, "LZWDecode") || !strcmp(s, "LZW")) { fz_obj *ec = fz_dict_gets(p, "EarlyChange"); + if (params) + { + /* We will shortstop here */ + params->type = PDF_IMAGE_LZW; + params->u.lzw.predictor = predictor; + params->u.lzw.columns = columns; + params->u.lzw.colors = colors; + params->u.lzw.bpc = bpc; + params->u.lzw.ec = (ec ? fz_to_int(ec) : 1); + return chain; + } chain = fz_open_lzwd(chain, ec ? fz_to_int(ec) : 1); if (predictor > 1) chain = fz_open_predict(chain, predictor, columns, colors, bpc); @@ -155,7 +203,7 @@ build_filter(fz_stream *chain, pdf_document * xref, fz_obj * f, fz_obj * p, int * Assume ownership of head. */ static fz_stream * -build_filter_chain(fz_stream *chain, pdf_document *xref, fz_obj *fs, fz_obj *ps, int num, int gen) +build_filter_chain(fz_stream *chain, pdf_document *xref, fz_obj *fs, fz_obj *ps, int num, int gen, pdf_image_params *params) { fz_obj *f; fz_obj *p; @@ -166,7 +214,7 @@ build_filter_chain(fz_stream *chain, pdf_document *xref, fz_obj *fs, fz_obj *ps, { f = fz_array_get(fs, i); p = fz_array_get(ps, i); - chain = build_filter(chain, xref, f, p, num, gen); + chain = build_filter(chain, xref, f, p, num, gen, (i == n-1 ? params : NULL)); } return chain; @@ -210,7 +258,7 @@ pdf_open_raw_filter(fz_stream *chain, pdf_document *xref, fz_obj *stmobj, int nu * to stream length and decrypting. */ static fz_stream * -pdf_open_filter(fz_stream *chain, pdf_document *xref, fz_obj *stmobj, int num, int gen) +pdf_open_filter(fz_stream *chain, pdf_document *xref, fz_obj *stmobj, int num, int gen, pdf_image_params *imparams) { fz_obj *filters; fz_obj *params; @@ -221,9 +269,9 @@ pdf_open_filter(fz_stream *chain, pdf_document *xref, fz_obj *stmobj, int num, i chain = pdf_open_raw_filter(chain, xref, stmobj, num, gen); if (fz_is_name(filters)) - chain = build_filter(chain, xref, filters, params, num, gen); + chain = build_filter(chain, xref, filters, params, num, gen, imparams); else if (fz_array_len(filters) > 0) - chain = build_filter_chain(chain, xref, filters, params, num, gen); + chain = build_filter_chain(chain, xref, filters, params, num, gen, imparams); fz_lock_stream(chain); return chain; @@ -234,7 +282,7 @@ pdf_open_filter(fz_stream *chain, pdf_document *xref, fz_obj *stmobj, int num, i * constraining to stream length, and without decryption. */ fz_stream * -pdf_open_inline_stream(pdf_document *xref, fz_obj *stmobj, int length, fz_stream *chain) +pdf_open_inline_stream(pdf_document *xref, fz_obj *stmobj, int length, fz_stream *chain, pdf_image_params *imparams) { fz_obj *filters; fz_obj *params; @@ -246,9 +294,9 @@ pdf_open_inline_stream(pdf_document *xref, fz_obj *stmobj, int length, fz_stream fz_keep_stream(chain); if (fz_is_name(filters)) - return build_filter(chain, xref, filters, params, 0, 0); + return build_filter(chain, xref, filters, params, 0, 0, imparams); if (fz_array_len(filters) > 0) - return build_filter_chain(chain, xref, filters, params, 0, 0); + return build_filter_chain(chain, xref, filters, params, 0, 0, imparams); return fz_open_null(chain, length); } @@ -290,6 +338,12 @@ pdf_open_raw_stream(pdf_document *xref, int num, int gen) fz_stream * pdf_open_stream(pdf_document *xref, int num, int gen) { + return pdf_open_image_stream(xref, num, gen, NULL); +} + +fz_stream * +pdf_open_image_stream(pdf_document *xref, int num, int gen, pdf_image_params *params) +{ pdf_xref_entry *x; fz_stream *stm; @@ -304,12 +358,56 @@ pdf_open_stream(pdf_document *xref, int num, int gen) if (x->stm_ofs == 0) fz_throw(xref->ctx, "object is not a stream"); - stm = pdf_open_filter(xref->file, xref, x->obj, num, gen); + stm = pdf_open_filter(xref->file, xref, x->obj, num, gen, params); fz_seek(xref->file, x->stm_ofs, 0); return stm; } fz_stream * +pdf_open_image_decomp_stream(fz_context *ctx, fz_buffer *buffer, pdf_image_params *params, int *factor) +{ + fz_stream *chain = fz_open_buffer(ctx, buffer); + + switch (params->type) + { + case PDF_IMAGE_FAX: + *factor = 1; + return fz_open_faxd(chain, + params->u.fax.k, + params->u.fax.eol, + params->u.fax.eba, + params->u.fax.columns, + params->u.fax.rows, + params->u.fax.eob, + params->u.fax.bi1); + case PDF_IMAGE_JPEG: + if (*factor > 8) + *factor = 8; + return fz_open_resized_dctd(chain, params->u.jpeg.ct, *factor); + case PDF_IMAGE_RLD: + *factor = 1; + return fz_open_rld(chain); + case PDF_IMAGE_FLATE: + *factor = 1; + chain = fz_open_flated(chain); + if (params->u.flate.predictor > 1) + chain = fz_open_predict(chain, params->u.flate.predictor, params->u.flate.columns, params->u.flate.colors, params->u.flate.bpc); + return chain; + case PDF_IMAGE_LZW: + *factor = 1; + chain = fz_open_lzwd(chain, params->u.lzw.ec); + if (params->u.lzw.predictor > 1) + chain = fz_open_predict(chain, params->u.lzw.predictor, params->u.lzw.columns, params->u.lzw.colors, params->u.lzw.bpc); + return chain; + default: + *factor = 1; + break; + } + + return chain; +} + +fz_stream * pdf_open_stream_with_offset(pdf_document *xref, int num, int gen, fz_obj *dict, int stm_ofs) { fz_stream *stm; @@ -317,7 +415,7 @@ pdf_open_stream_with_offset(pdf_document *xref, int num, int gen, fz_obj *dict, if (stm_ofs == 0) fz_throw(xref->ctx, "object is not a stream"); - stm = pdf_open_filter(xref->file, xref, dict, num, gen); + stm = pdf_open_filter(xref->file, xref, dict, num, gen, NULL); fz_seek(xref->file, stm_ofs, 0); return stm; } @@ -372,6 +470,12 @@ pdf_guess_filter_length(int len, char *filter) fz_buffer * pdf_load_stream(pdf_document *xref, int num, int gen) { + return pdf_load_image_stream(xref, num, gen, NULL); +} + +fz_buffer * +pdf_load_image_stream(pdf_document *xref, int num, int gen, pdf_image_params *params) +{ fz_context *ctx = xref->ctx; fz_stream *stm = NULL; fz_obj *dict, *obj; @@ -392,7 +496,7 @@ pdf_load_stream(pdf_document *xref, int num, int gen) fz_drop_obj(dict); - stm = pdf_open_stream(xref, num, gen); + stm = pdf_open_image_stream(xref, num, gen, params); /* RJW: "cannot open stream (%d %d R)", num, gen */ fz_try(ctx) diff --git a/pdf/pdf_xobject.c b/pdf/pdf_xobject.c index 91bf4db3..9f6d10ce 100644 --- a/pdf/pdf_xobject.c +++ b/pdf/pdf_xobject.c @@ -43,7 +43,7 @@ pdf_load_xobject(pdf_document *xref, fz_obj *dict) fz_obj *obj; fz_context *ctx = xref->ctx; - if ((form = fz_find_item(ctx, pdf_free_xobject_imp, dict))) + if ((form = pdf_find_item(ctx, pdf_free_xobject_imp, dict))) { return form; } @@ -56,7 +56,7 @@ pdf_load_xobject(pdf_document *xref, fz_obj *dict) form->me = NULL; /* Store item immediately, to avoid possible recursion if objects refer back to this one */ - fz_store_item(ctx, dict, form, pdf_xobject_size(form)); + pdf_store_item(ctx, dict, form, pdf_xobject_size(form)); obj = fz_dict_gets(dict, "BBox"); form->bbox = pdf_to_rect(ctx, obj); @@ -102,7 +102,7 @@ pdf_load_xobject(pdf_document *xref, fz_obj *dict) } fz_catch(ctx) { - fz_remove_item(ctx, pdf_free_xobject_imp, dict); + pdf_remove_item(ctx, pdf_free_xobject_imp, dict); pdf_drop_xobject(ctx, form); fz_throw(ctx, "cannot load xobject content stream (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); } diff --git a/win32/libmupdf.vcproj b/win32/libmupdf.vcproj index 9a8bb6e9..e06193f8 100644 --- a/win32/libmupdf.vcproj +++ b/win32/libmupdf.vcproj @@ -310,6 +310,10 @@ > </File> <File + RelativePath="..\pdf\pdf_store.c" + > + </File> + <File RelativePath="..\pdf\pdf_stream.c" > </File> diff --git a/xps/xps_image.c b/xps/xps_image.c index 10d3baee..a2021223 100644 --- a/xps/xps_image.c +++ b/xps/xps_image.c @@ -1,41 +1,93 @@ #include "fitz.h" #include "muxps.h" +typedef struct xps_image_s xps_image; + +struct xps_image_s +{ + fz_image base; + fz_pixmap *pix; + int xres; + int yres; +}; + +static void +xps_free_image(fz_context *ctx, fz_storable *image_) +{ + xps_image *image = (xps_image *)image_; + + if (image == NULL) + return; + + fz_drop_colorspace(ctx, image->base.colorspace); + fz_drop_pixmap(ctx, image->pix); + fz_free(ctx, image); +} + static fz_pixmap * +xps_image_to_pixmap(fz_context *ctx, fz_image *image_, int x, int w) +{ + xps_image *image = (xps_image *)image_; + + return fz_keep_pixmap(ctx, image->pix); +} + +static fz_image * xps_load_image(fz_context *ctx, byte *buf, int len) { - fz_pixmap *image; + fz_pixmap *pix; + xps_image *image; if (len < 8) fz_throw(ctx, "unknown image file format"); if (buf[0] == 0xff && buf[1] == 0xd8) - image = fz_load_jpeg(ctx, buf, len); + pix = fz_load_jpeg(ctx, buf, len); else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) - image = fz_load_png(ctx, buf, len); + pix = fz_load_png(ctx, buf, len); else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) fz_throw(ctx, "JPEG-XR codec is not available"); else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) - image = fz_load_tiff(ctx, buf, len); + pix = fz_load_tiff(ctx, buf, len); else fz_throw(ctx, "unknown image file format"); - return image; + fz_try(ctx) + { + image = fz_malloc_struct(ctx, xps_image); + + FZ_INIT_STORABLE(&image->base, 1, xps_free_image); + image->base.w = pix->w; + image->base.h = pix->h; + image->base.mask = NULL; + image->base.colorspace = pix->colorspace; + image->base.get_pixmap = xps_image_to_pixmap; + image->xres = pix->xres; + image->yres = pix->yres; + image->pix = pix; + } + fz_catch(ctx) + { + fz_drop_pixmap(ctx, pix); + fz_rethrow(ctx); + } + + return &image->base; } static void xps_paint_image_brush(xps_document *doc, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, void *vimage) { - fz_pixmap *pixmap = vimage; + xps_image *image = vimage; float xs, ys; - if (pixmap->xres == 0 || pixmap->yres == 0) + if (image->xres == 0 || image->yres == 0) return; - xs = pixmap->w * 96 / pixmap->xres; - ys = pixmap->h * 96 / pixmap->yres; + xs = image->base.w * 96 / image->xres; + ys = image->base.h * 96 / image->yres; ctm = fz_concat(fz_scale(xs, ys), ctm); - fz_fill_image(doc->dev, pixmap, ctm, doc->opacity[doc->opacity_top]); + fz_fill_image(doc->dev, &image->base, ctm, doc->opacity[doc->opacity_top]); } static xps_part * @@ -93,7 +145,7 @@ xps_parse_image_brush(xps_document *doc, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root) { xps_part *part; - fz_pixmap *image; + fz_image *image; fz_try(doc->ctx) { @@ -119,5 +171,5 @@ xps_parse_image_brush(xps_document *doc, fz_matrix ctm, fz_rect area, xps_parse_tiling_brush(doc, ctm, area, base_uri, dict, root, xps_paint_image_brush, image); - fz_drop_pixmap(doc->ctx, image); + fz_drop_image(doc->ctx, image); } |