| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- // Copyright (C) 2004-2021 Artifex Software, Inc.
- //
- // This file is part of MuPDF.
- //
- // MuPDF is free software: you can redistribute it and/or modify it under the
- // terms of the GNU Affero General Public License as published by the Free
- // Software Foundation, either version 3 of the License, or (at your option)
- // any later version.
- //
- // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
- // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- // details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
- //
- // Alternative licensing terms are available from the licensor.
- // For commercial licensing, see <https://www.artifex.com/> or contact
- // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- // CA 94129, USA, for further information.
- #include "mupdf/fitz.h"
- #include "mupdf/pdf.h"
- /* Sample various functions into lookup tables */
- static void
- pdf_sample_composite_shade_function(fz_context *ctx, float *shade, int n, pdf_function *func, float t0, float t1)
- {
- int i;
- float t;
- for (i = 0; i < 256; i++)
- {
- t = t0 + (i / 255.0f) * (t1 - t0);
- pdf_eval_function(ctx, func, &t, 1, shade, n);
- shade += n;
- *shade++ = 1;
- }
- }
- static void
- pdf_sample_component_shade_function(fz_context *ctx, float *shade, int funcs, pdf_function **func, float t0, float t1)
- {
- int i, k;
- float t;
- for (i = 0; i < 256; i++)
- {
- t = t0 + (i / 255.0f) * (t1 - t0);
- for (k = 0; k < funcs; k++)
- pdf_eval_function(ctx, func[k], &t, 1, shade++, 1);
- *shade++ = 1;
- }
- }
- void
- pdf_sample_shade_function(fz_context *ctx, float *samples, int n, int funcs, pdf_function **func, float t0, float t1)
- {
- if (funcs == 1)
- pdf_sample_composite_shade_function(ctx, samples, n, func[0], t0, t1);
- else
- pdf_sample_component_shade_function(ctx, samples, funcs, func, t0, t1);
- }
- static void
- make_sampled_shade_function(fz_context *ctx, fz_shade *shade, int funcs, pdf_function **func, float t0, float t1)
- {
- int n = shade->colorspace->n;
- if (funcs != 1)
- n = funcs;
- shade->function_stride = n + 1;
- shade->function = Memento_label(fz_malloc(ctx, sizeof(float) * 256 * shade->function_stride), "shade samples");
- pdf_sample_shade_function(ctx, shade->function, n, funcs, func, t0, t1);
- }
- /* Type 1-3 -- Function-based, linear and radial shadings */
- #define FUNSEGS 64 /* size of sampled mesh for function-based shadings */
- static void
- pdf_load_function_based_shading(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, int funcs, pdf_function **func)
- {
- pdf_obj *obj;
- float x0, y0, x1, y1;
- float fv[2];
- int xx, yy, zz;
- float *p;
- int n = fz_colorspace_n(ctx, shade->colorspace);
- x0 = y0 = 0;
- x1 = y1 = 1;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Domain));
- if (obj)
- {
- x0 = pdf_array_get_real(ctx, obj, 0);
- x1 = pdf_array_get_real(ctx, obj, 1);
- y0 = pdf_array_get_real(ctx, obj, 2);
- y1 = pdf_array_get_real(ctx, obj, 3);
- }
- shade->u.f.matrix = pdf_dict_get_matrix(ctx, dict, PDF_NAME(Matrix));
- shade->u.f.xdivs = FUNSEGS;
- shade->u.f.ydivs = FUNSEGS;
- shade->u.f.fn_vals = Memento_label(fz_malloc(ctx, (FUNSEGS+1)*(FUNSEGS+1)*n*sizeof(float)), "shade_fn_vals");
- shade->u.f.domain[0][0] = x0;
- shade->u.f.domain[0][1] = y0;
- shade->u.f.domain[1][0] = x1;
- shade->u.f.domain[1][1] = y1;
- p = shade->u.f.fn_vals;
- if (funcs == 1)
- {
- for (yy = 0; yy <= FUNSEGS; yy++)
- {
- fv[1] = y0 + (y1 - y0) * yy / FUNSEGS;
- for (xx = 0; xx <= FUNSEGS; xx++)
- {
- fv[0] = x0 + (x1 - x0) * xx / FUNSEGS;
- pdf_eval_function(ctx, func[0], fv, 2, p, n);
- p += n;
- }
- }
- }
- else
- {
- if (funcs != n)
- fz_throw(ctx, FZ_ERROR_SYNTAX, "Expected 1 2in, n-out function, or n 2 in, 1-out functions");
- for (yy = 0; yy <= FUNSEGS; yy++)
- {
- fv[1] = y0 + (y1 - y0) * yy / FUNSEGS;
- for (xx = 0; xx <= FUNSEGS; xx++)
- {
- fv[0] = x0 + (x1 - x0) * xx / FUNSEGS;
- for (zz = 0; zz < n; zz++)
- {
- pdf_eval_function(ctx, func[zz], fv, 2, p, 1);
- p ++;
- }
- }
- }
- }
- }
- static void
- pdf_load_linear_shading(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, int funcs, pdf_function **func)
- {
- pdf_obj *obj;
- float d0, d1;
- int e0, e1;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Coords));
- shade->u.l_or_r.coords[0][0] = pdf_array_get_real(ctx, obj, 0);
- shade->u.l_or_r.coords[0][1] = pdf_array_get_real(ctx, obj, 1);
- shade->u.l_or_r.coords[1][0] = pdf_array_get_real(ctx, obj, 2);
- shade->u.l_or_r.coords[1][1] = pdf_array_get_real(ctx, obj, 3);
- d0 = 0;
- d1 = 1;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Domain));
- if (obj)
- {
- d0 = pdf_array_get_real(ctx, obj, 0);
- d1 = pdf_array_get_real(ctx, obj, 1);
- }
- e0 = e1 = 0;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Extend));
- if (obj)
- {
- e0 = pdf_array_get_bool(ctx, obj, 0);
- e1 = pdf_array_get_bool(ctx, obj, 1);
- }
- make_sampled_shade_function(ctx, shade, funcs, func, d0, d1);
- shade->u.l_or_r.extend[0] = e0;
- shade->u.l_or_r.extend[1] = e1;
- }
- static void
- pdf_load_radial_shading(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, int funcs, pdf_function **func)
- {
- pdf_obj *obj;
- float d0, d1;
- int e0, e1;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Coords));
- shade->u.l_or_r.coords[0][0] = pdf_array_get_real(ctx, obj, 0);
- shade->u.l_or_r.coords[0][1] = pdf_array_get_real(ctx, obj, 1);
- shade->u.l_or_r.coords[0][2] = pdf_array_get_real(ctx, obj, 2);
- shade->u.l_or_r.coords[1][0] = pdf_array_get_real(ctx, obj, 3);
- shade->u.l_or_r.coords[1][1] = pdf_array_get_real(ctx, obj, 4);
- shade->u.l_or_r.coords[1][2] = pdf_array_get_real(ctx, obj, 5);
- d0 = 0;
- d1 = 1;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Domain));
- if (obj)
- {
- d0 = pdf_array_get_real(ctx, obj, 0);
- d1 = pdf_array_get_real(ctx, obj, 1);
- }
- e0 = e1 = 0;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Extend));
- if (obj)
- {
- e0 = pdf_array_get_bool(ctx, obj, 0);
- e1 = pdf_array_get_bool(ctx, obj, 1);
- }
- make_sampled_shade_function(ctx, shade, funcs, func, d0, d1);
- shade->u.l_or_r.extend[0] = e0;
- shade->u.l_or_r.extend[1] = e1;
- }
- /* Type 4-7 -- Triangle and patch mesh shadings */
- struct mesh_params
- {
- int vprow;
- int bpflag;
- int bpcoord;
- int bpcomp;
- float x0, x1;
- float y0, y1;
- float c0[FZ_MAX_COLORS];
- float c1[FZ_MAX_COLORS];
- };
- static void
- pdf_load_mesh_params(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict)
- {
- pdf_obj *obj;
- int i, n;
- shade->u.m.x0 = shade->u.m.y0 = 0;
- shade->u.m.x1 = shade->u.m.y1 = 1;
- for (i = 0; i < FZ_MAX_COLORS; i++)
- {
- shade->u.m.c0[i] = 0;
- shade->u.m.c1[i] = 1;
- }
- shade->u.m.vprow = pdf_dict_get_int(ctx, dict, PDF_NAME(VerticesPerRow));
- shade->u.m.bpflag = pdf_dict_get_int(ctx, dict, PDF_NAME(BitsPerFlag));
- shade->u.m.bpcoord = pdf_dict_get_int(ctx, dict, PDF_NAME(BitsPerCoordinate));
- shade->u.m.bpcomp = pdf_dict_get_int(ctx, dict, PDF_NAME(BitsPerComponent));
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Decode));
- if (pdf_array_len(ctx, obj) >= 6)
- {
- n = fz_mini(FZ_MAX_COLORS, (pdf_array_len(ctx, obj) - 4) / 2);
- shade->u.m.x0 = pdf_array_get_real(ctx, obj, 0);
- shade->u.m.x1 = pdf_array_get_real(ctx, obj, 1);
- shade->u.m.y0 = pdf_array_get_real(ctx, obj, 2);
- shade->u.m.y1 = pdf_array_get_real(ctx, obj, 3);
- for (i = 0; i < n; i++)
- {
- shade->u.m.c0[i] = pdf_array_get_real(ctx, obj, 4 + i * 2);
- shade->u.m.c1[i] = pdf_array_get_real(ctx, obj, 5 + i * 2);
- }
- }
- if (shade->u.m.vprow < 2 && shade->type == 5)
- {
- fz_warn(ctx, "Too few vertices per row (%d)", shade->u.m.vprow);
- shade->u.m.vprow = 2;
- }
- if (shade->u.m.bpflag != 2 && shade->u.m.bpflag != 4 && shade->u.m.bpflag != 8 &&
- shade->type != 5)
- {
- fz_warn(ctx, "Invalid number of bits per flag (%d)", shade->u.m.bpflag);
- shade->u.m.bpflag = 8;
- }
- if (shade->u.m.bpcoord != 1 && shade->u.m.bpcoord != 2 && shade->u.m.bpcoord != 4 &&
- shade->u.m.bpcoord != 8 && shade->u.m.bpcoord != 12 && shade->u.m.bpcoord != 16 &&
- shade->u.m.bpcoord != 24 && shade->u.m.bpcoord != 32)
- {
- fz_warn(ctx, "Invalid number of bits per coordinate (%d)", shade->u.m.bpcoord);
- shade->u.m.bpcoord = 8;
- }
- if (shade->u.m.bpcomp != 1 && shade->u.m.bpcomp != 2 && shade->u.m.bpcomp != 4 &&
- shade->u.m.bpcomp != 8 && shade->u.m.bpcomp != 12 && shade->u.m.bpcomp != 16)
- {
- fz_warn(ctx, "Invalid number of bits per component (%d)", shade->u.m.bpcomp);
- shade->u.m.bpcomp = 8;
- }
- }
- static void
- pdf_load_type4_shade(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, int funcs, pdf_function **func)
- {
- pdf_load_mesh_params(ctx, doc, shade, dict);
- if (funcs > 0)
- make_sampled_shade_function(ctx, shade, funcs, func, shade->u.m.c0[0], shade->u.m.c1[0]);
- shade->buffer = pdf_load_compressed_stream(ctx, doc, pdf_to_num(ctx, dict), 0);
- }
- static void
- pdf_load_type5_shade(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, int funcs, pdf_function **func)
- {
- pdf_load_mesh_params(ctx, doc, shade, dict);
- if (funcs > 0)
- make_sampled_shade_function(ctx, shade, funcs, func, shade->u.m.c0[0], shade->u.m.c1[0]);
- shade->buffer = pdf_load_compressed_stream(ctx, doc, pdf_to_num(ctx, dict), 0);
- }
- /* Type 6 & 7 -- Patch mesh shadings */
- static void
- pdf_load_type6_shade(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, int funcs, pdf_function **func)
- {
- pdf_load_mesh_params(ctx, doc, shade, dict);
- if (funcs > 0)
- make_sampled_shade_function(ctx, shade, funcs, func, shade->u.m.c0[0], shade->u.m.c1[0]);
- shade->buffer = pdf_load_compressed_stream(ctx, doc, pdf_to_num(ctx, dict), 0);
- }
- static void
- pdf_load_type7_shade(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, int funcs, pdf_function **func)
- {
- pdf_load_mesh_params(ctx, doc, shade, dict);
- if (funcs > 0)
- make_sampled_shade_function(ctx, shade, funcs, func, shade->u.m.c0[0], shade->u.m.c1[0]);
- shade->buffer = pdf_load_compressed_stream(ctx, doc, pdf_to_num(ctx, dict), 0);
- }
- /* Load all of the shading dictionary parameters, then switch on the shading type. */
- static fz_shade *
- pdf_load_shading_dict(fz_context *ctx, pdf_document *doc, pdf_obj *dict, fz_matrix transform)
- {
- fz_shade *shade = NULL;
- pdf_function *func[FZ_MAX_COLORS] = { NULL };
- pdf_obj *obj;
- int funcs = 0;
- int type = 0;
- int i, in, out, n;
- fz_var(shade);
- fz_var(func);
- fz_var(funcs);
- fz_var(type);
- fz_try(ctx)
- {
- shade = fz_malloc_struct(ctx, fz_shade);
- FZ_INIT_STORABLE(shade, 1, fz_drop_shade_imp);
- shade->type = FZ_MESH_TYPE4;
- shade->use_background = 0;
- shade->function_stride = 0;
- shade->matrix = transform;
- shade->bbox = fz_infinite_rect;
- shade->colorspace = NULL;
- funcs = 0;
- obj = pdf_dict_get(ctx, dict, PDF_NAME(ShadingType));
- type = pdf_to_int(ctx, obj);
- obj = pdf_dict_get(ctx, dict, PDF_NAME(ColorSpace));
- if (!obj)
- fz_throw(ctx, FZ_ERROR_SYNTAX, "shading colorspace is missing");
- shade->colorspace = pdf_load_colorspace(ctx, obj);
- n = fz_colorspace_n(ctx, shade->colorspace);
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Background));
- if (obj)
- {
- shade->use_background = 1;
- for (i = 0; i < n; i++)
- shade->background[i] = pdf_array_get_real(ctx, obj, i);
- }
- obj = pdf_dict_get(ctx, dict, PDF_NAME(BBox));
- if (pdf_is_array(ctx, obj))
- shade->bbox = pdf_to_rect(ctx, obj);
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Function));
- if (pdf_is_dict(ctx, obj))
- {
- funcs = 1;
- if (type == 1)
- in = 2;
- else
- in = 1;
- out = n;
- func[0] = pdf_load_function(ctx, obj, in, out);
- if (!func[0])
- fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj));
- }
- else if (pdf_is_array(ctx, obj))
- {
- funcs = pdf_array_len(ctx, obj);
- if (funcs != 1 && funcs != n)
- {
- funcs = 0;
- fz_throw(ctx, FZ_ERROR_SYNTAX, "incorrect number of shading functions");
- }
- if (funcs > FZ_MAX_COLORS)
- {
- funcs = 0;
- fz_throw(ctx, FZ_ERROR_SYNTAX, "too many shading functions");
- }
- if (type == 1)
- in = 2;
- else
- in = 1;
- out = 1;
- for (i = 0; i < funcs; i++)
- {
- func[i] = pdf_load_function(ctx, pdf_array_get(ctx, obj, i), in, out);
- if (!func[i])
- fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj));
- }
- }
- else if (type < 4)
- {
- /* Functions are compulsory for types 1,2,3 */
- fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj));
- }
- shade->type = type;
- switch (type)
- {
- case 1: pdf_load_function_based_shading(ctx, doc, shade, dict, funcs, func); break;
- case 2: pdf_load_linear_shading(ctx, doc, shade, dict, funcs, func); break;
- case 3: pdf_load_radial_shading(ctx, doc, shade, dict, funcs, func); break;
- case 4: pdf_load_type4_shade(ctx, doc, shade, dict, funcs, func); break;
- case 5: pdf_load_type5_shade(ctx, doc, shade, dict, funcs, func); break;
- case 6: pdf_load_type6_shade(ctx, doc, shade, dict, funcs, func); break;
- case 7: pdf_load_type7_shade(ctx, doc, shade, dict, funcs, func); break;
- default:
- fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown shading type: %d", type);
- }
- }
- fz_always(ctx)
- {
- for (i = 0; i < funcs; i++)
- pdf_drop_function(ctx, func[i]);
- }
- fz_catch(ctx)
- {
- fz_drop_shade(ctx, shade);
- fz_rethrow(ctx);
- }
- return shade;
- }
- static size_t
- fz_shade_size(fz_context *ctx, fz_shade *s)
- {
- size_t f = 0;
- if (s == NULL)
- return 0;
- if (s->function_stride)
- f = sizeof(float) * s->function_stride * 256;
- if (s->type == FZ_FUNCTION_BASED)
- return sizeof(*s) + sizeof(float) * s->u.f.xdivs * s->u.f.ydivs * fz_colorspace_n(ctx, s->colorspace) + f;
- return sizeof(*s) + fz_compressed_buffer_size(s->buffer) + f;
- }
- fz_shade *
- pdf_load_shading(fz_context *ctx, pdf_document *doc, pdf_obj *dict)
- {
- fz_matrix mat;
- pdf_obj *obj;
- fz_shade *shade;
- if ((shade = pdf_find_item(ctx, fz_drop_shade_imp, dict)) != NULL)
- {
- return shade;
- }
- /* Type 2 pattern dictionary */
- if (pdf_dict_get(ctx, dict, PDF_NAME(PatternType)))
- {
- mat = pdf_dict_get_matrix(ctx, dict, PDF_NAME(Matrix));
- obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState));
- if (obj)
- {
- if (pdf_dict_get(ctx, obj, PDF_NAME(CA)) || pdf_dict_get(ctx, obj, PDF_NAME(ca)))
- {
- fz_warn(ctx, "shading with alpha not supported");
- }
- }
- obj = pdf_dict_get(ctx, dict, PDF_NAME(Shading));
- if (!obj)
- fz_throw(ctx, FZ_ERROR_SYNTAX, "missing shading dictionary");
- shade = pdf_load_shading_dict(ctx, doc, obj, mat);
- }
- /* Naked shading dictionary */
- else
- {
- shade = pdf_load_shading_dict(ctx, doc, dict, fz_identity);
- }
- pdf_store_item(ctx, dict, shade, fz_shade_size(ctx, shade));
- return shade;
- }
|