| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- // 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 "color-imp.h"
- #include "draw-imp.h"
- #include "pixmap-imp.h"
- #include <assert.h>
- #include <math.h>
- enum { MAXN = 2 + FZ_MAX_COLORS };
- static void paint_scan(fz_pixmap *FZ_RESTRICT pix, int y, int fx0, int fx1, int cx0, int cx1, const int *FZ_RESTRICT v0, const int *FZ_RESTRICT v1, int n)
- {
- unsigned char *p;
- int c[MAXN], dc[MAXN];
- int k, w;
- float div, mul;
- int x0, x1, pa;
- /* Ensure that fx0 is left edge, and fx1 is right */
- if (fx0 > fx1)
- {
- const int *v;
- int t = fx0; fx0 = fx1; fx1 = t;
- v = v0; v0 = v1; v1 = v;
- }
- else if (fx0 == fx1)
- return;
- /* Clip fx0, fx1 to range */
- if (fx0 >= cx1)
- return;
- if (fx1 <= cx0)
- return;
- x0 = (fx0 > cx0 ? fx0 : cx0);
- x1 = (fx1 < cx1 ? fx1 : cx1);
- w = x1 - x0;
- if (w == 0)
- return;
- div = 1.0f / (fx1 - fx0);
- mul = (x0 - fx0);
- for (k = 0; k < n; k++)
- {
- dc[k] = (v1[k] - v0[k]) * div;
- c[k] = v0[k] + dc[k] * mul;
- }
- p = pix->samples + ((x0 - pix->x) * pix->n) + ((y - pix->y) * pix->stride);
- pa = pix->alpha;
- do
- {
- for (k = 0; k < n; k++)
- {
- *p++ = c[k]>>16;
- c[k] += dc[k];
- }
- if (pa)
- *p++ = 255;
- }
- while (--w);
- }
- typedef struct
- {
- float x;
- float dx;
- int v[2*MAXN];
- } edge_data;
- static inline void prepare_edge(const float *FZ_RESTRICT vtop, const float *FZ_RESTRICT vbot, edge_data *FZ_RESTRICT edge, float y, int n)
- {
- float r = 1.0f / (vbot[1] - vtop[1]);
- float t = (y - vtop[1]) * r;
- float diff = vbot[0] - vtop[0];
- int i;
- edge->x = vtop[0] + diff * t;
- edge->dx = diff * r;
- for (i = 0; i < n; i++)
- {
- diff = vbot[i+2] - vtop[i+2];
- edge->v[i] = (int)(65536.0f * (vtop[i+2] + diff * t));
- edge->v[i+MAXN] = (int)(65536.0f * diff * r);
- }
- }
- static inline void step_edge(edge_data *edge, int n)
- {
- int i;
- edge->x += edge->dx;
- for (i = 0; i < n; i++)
- {
- edge->v[i] += edge->v[i + MAXN];
- }
- }
- static void
- fz_paint_triangle(fz_pixmap *pix, float *v[3], int n, fz_irect bbox)
- {
- edge_data e0, e1;
- int top, mid, bot;
- float y, y1;
- int minx, maxx;
- top = bot = 0;
- if (v[1][1] < v[0][1]) top = 1; else bot = 1;
- if (v[2][1] < v[top][1]) top = 2;
- else if (v[2][1] > v[bot][1]) bot = 2;
- if (v[top][1] == v[bot][1]) return;
- /* Test if the triangle is completely outside the scissor rect */
- if (v[bot][1] < bbox.y0) return;
- if (v[top][1] > bbox.y1) return;
- /* Magic! Ensure that mid/top/bot are all different */
- mid = 3^top^bot;
- assert(top != bot && top != mid && mid != bot);
- minx = fz_maxi(bbox.x0, pix->x);
- maxx = fz_mini(bbox.x1, pix->x + pix->w);
- y = ceilf(fz_max(bbox.y0, v[top][1]));
- y1 = ceilf(fz_min(bbox.y1, v[mid][1]));
- n -= 2;
- prepare_edge(v[top], v[bot], &e0, y, n);
- if (y < y1)
- {
- prepare_edge(v[top], v[mid], &e1, y, n);
- do
- {
- paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n);
- step_edge(&e0, n);
- step_edge(&e1, n);
- y ++;
- }
- while (y < y1);
- }
- y1 = ceilf(fz_min(bbox.y1, v[bot][1]));
- if (y < y1)
- {
- prepare_edge(v[mid], v[bot], &e1, y, n);
- do
- {
- paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n);
- y ++;
- if (y >= y1)
- break;
- step_edge(&e0, n);
- step_edge(&e1, n);
- }
- while (1);
- }
- }
- struct paint_tri_data
- {
- const fz_shade *shade;
- fz_pixmap *dest;
- fz_irect bbox;
- fz_color_converter cc;
- };
- static void
- prepare_mesh_vertex(fz_context *ctx, void *arg, fz_vertex *v, const float *input)
- {
- struct paint_tri_data *ptd = (struct paint_tri_data *)arg;
- const fz_shade *shade = ptd->shade;
- fz_pixmap *dest = ptd->dest;
- float *output = v->c;
- int i;
- if (shade->function_stride)
- {
- float f = input[0];
- if (shade->type >= 4 && shade->type <= 7)
- f = (f - shade->u.m.c0[0]) / (shade->u.m.c1[0] - shade->u.m.c0[0]);
- output[0] = f * 255;
- }
- else
- {
- int n = fz_colorspace_n(ctx, dest->colorspace);
- int a = dest->alpha;
- int m = dest->n - a;
- for (i = n; i < m; i++)
- output[i] = 0;
- if (ptd->cc.convert)
- ptd->cc.convert(ctx, &ptd->cc, input, output);
- for (i = 0; i < m; i++)
- output[i] *= 255;
- if (a)
- output[m] = 255;
- }
- }
- static void
- do_paint_tri(fz_context *ctx, void *arg, fz_vertex *av, fz_vertex *bv, fz_vertex *cv)
- {
- struct paint_tri_data *ptd = (struct paint_tri_data *)arg;
- float *vertices[3];
- fz_pixmap *dest;
- vertices[0] = (float *)av;
- vertices[1] = (float *)bv;
- vertices[2] = (float *)cv;
- dest = ptd->dest;
- fz_paint_triangle(dest, vertices, 2 + dest->n - dest->alpha, ptd->bbox);
- }
- struct fz_shade_color_cache
- {
- fz_colorspace *src;
- fz_colorspace *dst;
- fz_color_params params;
- int full;
- fz_color_converter cached;
- fz_colorspace *src2;
- fz_colorspace *dst2;
- fz_color_params params2;
- int full2;
- fz_color_converter cached2;
- };
- void
- fz_drop_shade_color_cache(fz_context *ctx, fz_shade_color_cache *cache)
- {
- if (cache == NULL)
- return;
- fz_drop_colorspace(ctx, cache->src);
- fz_drop_colorspace(ctx, cache->dst);
- if (cache->full)
- fz_fin_cached_color_converter(ctx, &cache->cached);
- fz_drop_colorspace(ctx, cache->src2);
- fz_drop_colorspace(ctx, cache->dst2);
- if (cache->full2)
- fz_drop_color_converter(ctx, &cache->cached2);
- fz_free(ctx, cache);
- }
- void
- fz_paint_shade(fz_context *ctx, fz_shade *shade, fz_colorspace *colorspace, fz_matrix ctm, fz_pixmap *dest, fz_color_params color_params, fz_irect bbox, const fz_overprint *eop, fz_shade_color_cache **color_cache)
- {
- unsigned char clut[256][FZ_MAX_COLORS];
- fz_pixmap *temp = NULL;
- fz_pixmap *conv = NULL;
- fz_color_converter cc = { 0 };
- float color[FZ_MAX_COLORS];
- struct paint_tri_data ptd = { 0 };
- int i, k;
- fz_matrix local_ctm;
- fz_shade_color_cache *cache = NULL;
- int recache = 0;
- int recache2 = 0;
- int stride = shade->function_stride;
- fz_var(temp);
- fz_var(conv);
- fz_var(recache);
- fz_var(recache2);
- fz_var(cc);
- if (colorspace == NULL)
- colorspace = shade->colorspace;
- if (color_cache)
- {
- cache = *color_cache;
- if (cache == NULL)
- *color_cache = cache = fz_malloc_struct(ctx, fz_shade_color_cache);
- }
- fz_try(ctx)
- {
- local_ctm = fz_concat(shade->matrix, ctm);
- if (stride)
- {
- /* We need to use alpha = 1 here, because the shade might not fill the bbox. */
- temp = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 1);
- fz_clear_pixmap(ctx, temp);
- }
- else
- {
- temp = dest;
- }
- ptd.dest = temp;
- ptd.shade = shade;
- ptd.bbox = bbox;
- if (temp->colorspace)
- {
- if (cache && cache->full && cache->src == colorspace && cache->dst == temp->colorspace &&
- cache->params.op == color_params.op &&
- cache->params.opm == color_params.opm &&
- cache->params.ri == color_params.ri)
- {
- ptd.cc = cache->cached;
- cache->full = 0;
- }
- else
- fz_init_cached_color_converter(ctx, &ptd.cc, colorspace, temp->colorspace, temp->seps, NULL, color_params);
- /* Drop the existing contents of the cache. */
- if (cache)
- {
- fz_drop_colorspace(ctx, cache->src);
- cache->src = NULL;
- fz_drop_colorspace(ctx, cache->dst);
- cache->dst = NULL;
- if (cache->full)
- fz_fin_cached_color_converter(ctx, &cache->cached);
- cache->full = 0;
- /* Remember that we can put stuff back into the cache. */
- recache = 1;
- }
- }
- fz_process_shade(ctx, shade, local_ctm, fz_rect_from_irect(bbox), prepare_mesh_vertex, &do_paint_tri, &ptd);
- if (stride)
- {
- /* If the shade is defined in a deviceN (or separation,
- * which is the same internally to MuPDF) space, then
- * we need to render it in deviceN before painting it
- * to the destination. If not, we are free to render it
- * direct to the target. */
- if (fz_colorspace_is_device_n(ctx, colorspace))
- {
- /* We've drawn it as greyscale, with the values being
- * the input to the function. Now make DevN version
- * by mapping that greyscale through the function.
- * This seems inefficient, but it's actually required,
- * because we need to apply the function lookup POST
- * interpolation in the do_paint_tri routines, not
- * before it to avoid problems with some test files
- * (tests/GhentV3.0/061_Shading_x1a.pdf for example).
- */
- unsigned char *s = temp->samples;
- unsigned char *d;
- int hh = temp->h;
- int n = fz_colorspace_n(ctx, colorspace);
- /* alpha = 1 here for the same reason as earlier */
- conv = fz_new_pixmap_with_bbox(ctx, colorspace, bbox, NULL, 1);
- d = conv->samples;
- while (hh--)
- {
- int len = temp->w;
- while (len--)
- {
- int v = *s++;
- int a = *s++;
- const float *f = &shade->function[v*stride];
- for (k = 0; k < n; k++)
- *d++ = fz_clampi(255 * f[k], 0, 255);
- *d++ = a;
- }
- d += conv->stride - conv->w * (size_t)conv->n;
- s += temp->stride - temp->w * (size_t)temp->n;
- }
- fz_drop_pixmap(ctx, temp);
- temp = conv;
- conv = NULL;
- /* Now Change from our device_n colorspace into the target colorspace/spots. */
- conv = fz_clone_pixmap_area_with_different_seps(ctx, temp, NULL, dest->colorspace, dest->seps, color_params, NULL);
- }
- else
- {
- unsigned char *s = temp->samples;
- unsigned char *d;
- int da;
- int sa = temp->alpha;
- int hh = temp->h;
- int cn = fz_colorspace_n(ctx, colorspace);
- int m = dest->n - dest->alpha;
- int n = fz_colorspace_n(ctx, dest->colorspace);
- if (dest->colorspace)
- {
- if (cache && cache->full2 && cache->src2 == colorspace && cache->dst2 == dest->colorspace &&
- cache->params2.op == color_params.op &&
- cache->params2.opm == color_params.opm &&
- cache->params2.ri == color_params.ri)
- {
- cc = cache->cached2;
- cache->full2 = 0;
- }
- else
- fz_find_color_converter(ctx, &cc, colorspace, dest->colorspace, dest->seps, NULL, color_params);
- /* Drop the existing contents of the cache */
- if (cache)
- {
- fz_drop_colorspace(ctx, cache->src2);
- cache->src2 = NULL;
- fz_drop_colorspace(ctx, cache->dst2);
- cache->dst2 = NULL;
- if (cache->full2)
- fz_drop_color_converter(ctx, &cache->cached2);
- cache->full2 = 0;
- /* Remember that we can put stuff back into the cache. */
- recache2 = 1;
- }
- for (i = 0; i < 256; i++)
- {
- cc.convert(ctx, &cc, &shade->function[i*stride], color);
- for (k = 0; k < n; k++)
- clut[i][k] = color[k] * 255;
- for (; k < m; k++)
- clut[i][k] = 0;
- clut[i][k] = shade->function[i*stride + cn] * 255;
- }
- }
- else
- {
- for (i = 0; i < 256; i++)
- {
- for (k = 0; k < m; k++)
- clut[i][k] = 0;
- clut[i][k] = shade->function[i*stride + cn] * 255;
- }
- }
- conv = fz_new_pixmap_with_bbox(ctx, dest->colorspace, bbox, dest->seps, 1);
- d = conv->samples;
- da = conv->alpha;
- while (hh--)
- {
- int len = temp->w;
- while (len--)
- {
- int v = *s++;
- int a = (da ? clut[v][conv->n - 1] : 255);
- if (sa)
- a = fz_mul255(*s++, a);
- for (k = 0; k < conv->n - da; k++)
- *d++ = fz_mul255(clut[v][k], a);
- if (da)
- *d++ = a;
- }
- d += conv->stride - conv->w * (size_t)conv->n;
- s += temp->stride - temp->w * (size_t)temp->n;
- }
- }
- fz_paint_pixmap_with_overprint(dest, conv, eop);
- }
- }
- fz_always(ctx)
- {
- if (recache)
- {
- cache->src = fz_keep_colorspace(ctx, colorspace);
- cache->dst = fz_keep_colorspace(ctx, temp->colorspace);
- cache->params = color_params;
- cache->cached = ptd.cc;
- cache->full = 1;
- }
- else
- fz_fin_cached_color_converter(ctx, &ptd.cc);
- if (stride)
- {
- if (recache2)
- {
- cache->src2 = fz_keep_colorspace(ctx, colorspace);
- cache->dst2 = fz_keep_colorspace(ctx, dest->colorspace);
- cache->params2 = color_params;
- cache->cached2 = cc;
- cache->full2 = 1;
- }
- else
- fz_drop_color_converter(ctx, &cc);
- fz_drop_pixmap(ctx, temp);
- fz_drop_pixmap(ctx, conv);
- }
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
|