kolibrios-gitea/contrib/media/updf/pdf/pdf_image.c

355 lines
7.5 KiB
C
Raw Normal View History

#include "fitz.h"
#include "mupdf.h"
/* TODO: store JPEG compressed samples */
/* TODO: store flate compressed samples */
static fz_error pdf_load_jpx_image(fz_pixmap **imgp, pdf_xref *xref, fz_obj *dict);
static void
pdf_mask_color_key(fz_pixmap *pix, int n, int *colorkey)
{
unsigned char *p = pix->samples;
int len = pix->w * pix->h;
int k, t;
while (len--)
{
t = 1;
for (k = 0; k < n; k++)
if (p[k] < colorkey[k * 2] || p[k] > colorkey[k * 2 + 1])
t = 0;
if (t)
for (k = 0; k < pix->n; k++)
p[k] = 0;
p += pix->n;
}
}
static fz_error
pdf_load_image_imp(fz_pixmap **imgp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cstm, int forcemask)
{
fz_stream *stm;
fz_pixmap *tile;
fz_obj *obj, *res;
fz_error error;
int w, h, bpc, n;
int imagemask;
int interpolate;
int indexed;
fz_colorspace *colorspace;
fz_pixmap *mask; /* explicit mask/softmask image */
int usecolorkey;
int colorkey[FZ_MAX_COLORS * 2];
float decode[FZ_MAX_COLORS * 2];
int stride;
unsigned char *samples;
int i, len;
/* special case for JPEG2000 images */
if (pdf_is_jpx_image(dict))
{
tile = NULL;
error = pdf_load_jpx_image(&tile, xref, dict);
if (error)
return fz_rethrow(error, "cannot load jpx image");
if (forcemask)
{
if (tile->n != 2)
{
fz_drop_pixmap(tile);
return fz_throw("softmask must be grayscale");
}
mask = fz_alpha_from_gray(tile, 1);
fz_drop_pixmap(tile);
*imgp = mask;
return fz_okay;
}
*imgp = tile;
return fz_okay;
}
w = fz_to_int(fz_dict_getsa(dict, "Width", "W"));
h = fz_to_int(fz_dict_getsa(dict, "Height", "H"));
bpc = fz_to_int(fz_dict_getsa(dict, "BitsPerComponent", "BPC"));
imagemask = fz_to_bool(fz_dict_getsa(dict, "ImageMask", "IM"));
interpolate = fz_to_bool(fz_dict_getsa(dict, "Interpolate", "I"));
indexed = 0;
usecolorkey = 0;
colorspace = NULL;
mask = NULL;
if (imagemask)
bpc = 1;
if (w == 0)
return fz_throw("image width is zero");
if (h == 0)
return fz_throw("image height is zero");
if (bpc == 0)
return fz_throw("image depth is zero");
if (bpc > 16)
return fz_throw("image depth is too large: %d", bpc);
if (w > (1 << 16))
return fz_throw("image is too wide");
if (h > (1 << 16))
return fz_throw("image is too high");
obj = fz_dict_getsa(dict, "ColorSpace", "CS");
if (obj && !imagemask && !forcemask)
{
/* colorspace resource lookup is only done for inline images */
if (fz_is_name(obj))
{
res = fz_dict_get(fz_dict_gets(rdb, "ColorSpace"), obj);
if (res)
obj = res;
}
error = pdf_load_colorspace(&colorspace, xref, obj);
if (error)
return fz_rethrow(error, "cannot load image colorspace");
if (!strcmp(colorspace->name, "Indexed"))
indexed = 1;
n = colorspace->n;
}
else
{
n = 1;
}
obj = fz_dict_getsa(dict, "Decode", "D");
if (obj)
{
for (i = 0; i < n * 2; i++)
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;
}
obj = fz_dict_getsa(dict, "SMask", "Mask");
if (fz_is_dict(obj))
{
/* Not allowed for inline images */
if (!cstm)
{
error = pdf_load_image_imp(&mask, xref, rdb, obj, NULL, 1);
if (error)
{
if (colorspace)
fz_drop_colorspace(colorspace);
return fz_rethrow(error, "cannot load image mask/softmask");
}
}
}
else if (fz_is_array(obj))
{
usecolorkey = 1;
for (i = 0; i < n * 2; i++)
colorkey[i] = fz_to_int(fz_array_get(obj, i));
}
/* Allocate now, to fail early if we run out of memory */
tile = fz_new_pixmap_with_limit(colorspace, w, h);
if (!tile)
{
if (colorspace)
fz_drop_colorspace(colorspace);
if (mask)
fz_drop_pixmap(mask);
return fz_throw("out of memory");
}
if (colorspace)
fz_drop_colorspace(colorspace);
tile->mask = mask;
tile->interpolate = interpolate;
stride = (w * n * bpc + 7) / 8;
if (cstm)
{
stm = pdf_open_inline_stream(cstm, xref, dict, stride * h);
}
else
{
error = pdf_open_stream(&stm, xref, fz_to_num(dict), fz_to_gen(dict));
if (error)
{
fz_drop_pixmap(tile);
return fz_rethrow(error, "cannot open image data stream (%d 0 R)", fz_to_num(dict));
}
}
samples = fz_calloc(h, stride);
len = fz_read(stm, samples, h * stride);
if (len < 0)
{
fz_close(stm);
fz_free(samples);
fz_drop_pixmap(tile);
return fz_rethrow(len, "cannot read image data");
}
/* Make sure we read the EOF marker (for inline images only) */
if (cstm)
{
unsigned char tbuf[512];
int tlen = fz_read(stm, tbuf, sizeof tbuf);
if (tlen < 0)
fz_catch(tlen, "ignoring error at end of image");
if (tlen > 0)
fz_warn("ignoring garbage at end of image");
}
fz_close(stm);
/* Pad truncated images */
if (len < stride * h)
{
fz_warn("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(samples);
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(tile);
fz_drop_pixmap(tile);
tile = conv;
}
else
{
fz_decode_tile(tile, decode);
}
*imgp = tile;
return fz_okay;
}
fz_error
pdf_load_inline_image(fz_pixmap **pixp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *file)
{
fz_error error;
error = pdf_load_image_imp(pixp, xref, rdb, dict, file, 0);
if (error)
return fz_rethrow(error, "cannot load inline image");
return fz_okay;
}
int
pdf_is_jpx_image(fz_obj *dict)
{
fz_obj *filter;
int i;
filter = fz_dict_gets(dict, "Filter");
if (!strcmp(fz_to_name(filter), "JPXDecode"))
return 1;
for (i = 0; i < fz_array_len(filter); i++)
if (!strcmp(fz_to_name(fz_array_get(filter, i)), "JPXDecode"))
return 1;
return 0;
}
static fz_error
pdf_load_jpx_image(fz_pixmap **imgp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
fz_buffer *buf;
fz_colorspace *colorspace;
fz_pixmap *img;
fz_obj *obj;
colorspace = NULL;
error = pdf_load_stream(&buf, xref, fz_to_num(dict), fz_to_gen(dict));
if (error)
return fz_rethrow(error, "cannot load jpx image data");
obj = fz_dict_gets(dict, "ColorSpace");
if (obj)
{
error = pdf_load_colorspace(&colorspace, xref, obj);
if (error)
fz_catch(error, "cannot load image colorspace");
}
error = fz_load_jpx_image(&img, buf->data, buf->len, colorspace);
if (error)
{
if (colorspace)
fz_drop_colorspace(colorspace);
fz_drop_buffer(buf);
return fz_rethrow(error, "cannot load jpx image");
}
if (colorspace)
fz_drop_colorspace(colorspace);
fz_drop_buffer(buf);
obj = fz_dict_getsa(dict, "SMask", "Mask");
if (fz_is_dict(obj))
{
error = pdf_load_image_imp(&img->mask, xref, NULL, obj, NULL, 1);
if (error)
{
fz_drop_pixmap(img);
return fz_rethrow(error, "cannot load image mask/softmask");
}
}
*imgp = img;
return fz_okay;
}
fz_error
pdf_load_image(fz_pixmap **pixp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
if ((*pixp = pdf_find_item(xref->store, fz_drop_pixmap, dict)))
{
fz_keep_pixmap(*pixp);
return fz_okay;
}
error = pdf_load_image_imp(pixp, xref, NULL, dict, NULL, 0);
if (error)
return fz_rethrow(error, "cannot load image (%d 0 R)", fz_to_num(dict));
pdf_store_item(xref->store, fz_keep_pixmap, fz_drop_pixmap, dict, *pixp);
return fz_okay;
}