1#include "mupdf/fitz.h"
2#include "draw-imp.h"
3
4#include <assert.h>
5#include <math.h>
6
7enum { MAXN = 2 + FZ_MAX_COLORS };
8
9static 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)
10{
11 unsigned char *p;
12 int c[MAXN], dc[MAXN];
13 int k, w;
14 float div, mul;
15 int x0, x1, pa;
16
17 /* Ensure that fx0 is left edge, and fx1 is right */
18 if (fx0 > fx1)
19 {
20 const int *v;
21 int t = fx0; fx0 = fx1; fx1 = t;
22 v = v0; v0 = v1; v1 = v;
23 }
24 else if (fx0 == fx1)
25 return;
26
27 /* Clip fx0, fx1 to range */
28 if (fx0 >= cx1)
29 return;
30 if (fx1 <= cx0)
31 return;
32 x0 = (fx0 > cx0 ? fx0 : cx0);
33 x1 = (fx1 < cx1 ? fx1 : cx1);
34
35 w = x1 - x0;
36 if (w == 0)
37 return;
38
39 div = 1.0f / (fx1 - fx0);
40 mul = (x0 - fx0);
41 for (k = 0; k < n; k++)
42 {
43 dc[k] = (v1[k] - v0[k]) * div;
44 c[k] = v0[k] + dc[k] * mul;
45 }
46
47 p = pix->samples + ((x0 - pix->x) * pix->n) + ((y - pix->y) * pix->stride);
48 pa = pix->alpha;
49 do
50 {
51 for (k = 0; k < n; k++)
52 {
53 *p++ = c[k]>>16;
54 c[k] += dc[k];
55 }
56 if (pa)
57 *p++ = 255;
58 }
59 while (--w);
60}
61
62typedef struct edge_data_s edge_data;
63
64struct edge_data_s
65{
66 float x;
67 float dx;
68 int v[2*MAXN];
69};
70
71static inline void prepare_edge(const float *FZ_RESTRICT vtop, const float *FZ_RESTRICT vbot, edge_data *FZ_RESTRICT edge, float y, int n)
72{
73 float r = 1.0f / (vbot[1] - vtop[1]);
74 float t = (y - vtop[1]) * r;
75 float diff = vbot[0] - vtop[0];
76 int i;
77
78 edge->x = vtop[0] + diff * t;
79 edge->dx = diff * r;
80
81 for (i = 0; i < n; i++)
82 {
83 diff = vbot[i+2] - vtop[i+2];
84 edge->v[i] = (int)(65536.0f * (vtop[i+2] + diff * t));
85 edge->v[i+MAXN] = (int)(65536.0f * diff * r);
86 }
87}
88
89static inline void step_edge(edge_data *edge, int n)
90{
91 int i;
92
93 edge->x += edge->dx;
94
95 for (i = 0; i < n; i++)
96 {
97 edge->v[i] += edge->v[i + MAXN];
98 }
99}
100
101static void
102fz_paint_triangle(fz_pixmap *pix, float *v[3], int n, fz_irect bbox)
103{
104 edge_data e0, e1;
105 int top, mid, bot;
106 float y, y1;
107 int minx, maxx;
108
109 top = bot = 0;
110 if (v[1][1] < v[0][1]) top = 1; else bot = 1;
111 if (v[2][1] < v[top][1]) top = 2;
112 else if (v[2][1] > v[bot][1]) bot = 2;
113 if (v[top][1] == v[bot][1]) return;
114
115 /* Test if the triangle is completely outside the scissor rect */
116 if (v[bot][1] < bbox.y0) return;
117 if (v[top][1] > bbox.y1) return;
118
119 /* Magic! Ensure that mid/top/bot are all different */
120 mid = 3^top^bot;
121
122 assert(top != bot && top != mid && mid != bot);
123
124 minx = fz_maxi(bbox.x0, pix->x);
125 maxx = fz_mini(bbox.x1, pix->x + pix->w);
126
127 y = ceilf(fz_max(bbox.y0, v[top][1]));
128 y1 = ceilf(fz_min(bbox.y1, v[mid][1]));
129
130 n -= 2;
131 prepare_edge(v[top], v[bot], &e0, y, n);
132 if (y < y1)
133 {
134 prepare_edge(v[top], v[mid], &e1, y, n);
135
136 do
137 {
138 paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n);
139 step_edge(&e0, n);
140 step_edge(&e1, n);
141 y ++;
142 }
143 while (y < y1);
144 }
145
146 y1 = ceilf(fz_min(bbox.y1, v[bot][1]));
147 if (y < y1)
148 {
149 prepare_edge(v[mid], v[bot], &e1, y, n);
150
151 do
152 {
153 paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n);
154 y ++;
155 if (y >= y1)
156 break;
157 step_edge(&e0, n);
158 step_edge(&e1, n);
159 }
160 while (1);
161 }
162}
163
164struct paint_tri_data
165{
166 const fz_shade *shade;
167 fz_pixmap *dest;
168 fz_irect bbox;
169 fz_color_converter cc;
170};
171
172static void
173prepare_mesh_vertex(fz_context *ctx, void *arg, fz_vertex *v, const float *input)
174{
175 struct paint_tri_data *ptd = (struct paint_tri_data *)arg;
176 const fz_shade *shade = ptd->shade;
177 fz_pixmap *dest = ptd->dest;
178 float *output = v->c;
179 int i;
180
181 if (shade->use_function)
182 output[0] = input[0] * 255;
183 else
184 {
185 int n = fz_colorspace_n(ctx, dest->colorspace);
186 int a = dest->alpha;
187 int m = dest->n - a;
188 if (ptd->cc.convert)
189 ptd->cc.convert(ctx, &ptd->cc, input, output);
190 for (i = 0; i < n; i++)
191 output[i] *= 255;
192 for (; i < m; i++)
193 output[i] = 0;
194 if (a)
195 output[i] = 255;
196 }
197}
198
199static void
200do_paint_tri(fz_context *ctx, void *arg, fz_vertex *av, fz_vertex *bv, fz_vertex *cv)
201{
202 struct paint_tri_data *ptd = (struct paint_tri_data *)arg;
203 float *vertices[3];
204 fz_pixmap *dest;
205
206 vertices[0] = (float *)av;
207 vertices[1] = (float *)bv;
208 vertices[2] = (float *)cv;
209
210 dest = ptd->dest;
211 fz_paint_triangle(dest, vertices, 2 + dest->n - dest->alpha, ptd->bbox);
212}
213
214/*
215 Render a shade to a given pixmap.
216
217 shade: The shade to paint.
218
219 override_cs: NULL, or colorspace to override the shades
220 inbuilt colorspace.
221
222 ctm: The transform to apply.
223
224 dest: The pixmap to render into.
225
226 color_params: The color rendering settings
227
228 bbox: Pointer to a bounding box to limit the rendering
229 of the shade.
230
231 op: NULL, or pointer to overprint bitmap.
232*/
233void
234fz_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)
235{
236 unsigned char clut[256][FZ_MAX_COLORS];
237 fz_pixmap *temp = NULL;
238 fz_pixmap *conv = NULL;
239 fz_color_converter cc = { 0 };
240 float color[FZ_MAX_COLORS];
241 struct paint_tri_data ptd = { 0 };
242 int i, k;
243 fz_matrix local_ctm;
244
245 fz_var(temp);
246 fz_var(conv);
247
248 if (colorspace == NULL)
249 colorspace = shade->colorspace;
250
251 fz_try(ctx)
252 {
253 local_ctm = fz_concat(shade->matrix, ctm);
254
255 if (shade->use_function)
256 {
257 /* We need to use alpha = 1 here, because the shade might not fill the bbox. */
258 temp = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 1);
259 fz_clear_pixmap(ctx, temp);
260 }
261 else
262 {
263 temp = dest;
264 }
265
266 ptd.dest = temp;
267 ptd.shade = shade;
268 ptd.bbox = bbox;
269
270 if (temp->colorspace)
271 fz_init_cached_color_converter(ctx, &ptd.cc, colorspace, temp->colorspace, NULL, color_params);
272
273 fz_process_shade(ctx, shade, local_ctm, fz_rect_from_irect(bbox), prepare_mesh_vertex, &do_paint_tri, &ptd);
274
275 if (shade->use_function)
276 {
277 /* If the shade is defined in a deviceN (or separation,
278 * which is the same internally to MuPDF) space, then
279 * we need to render it in deviceN before painting it
280 * to the destination. If not, we are free to render it
281 * direct to the target. */
282 if (fz_colorspace_is_device_n(ctx, colorspace))
283 {
284 /* We've drawn it as greyscale, with the values being
285 * the input to the function. Now make DevN version
286 * by mapping that greyscale through the function.
287 * This seems inefficient, but it's actually required,
288 * because we need to apply the function lookup POST
289 * interpolation in the do_paint_tri routines, not
290 * before it to avoid problems with some test files
291 * (tests/GhentV3.0/061_Shading_x1a.pdf for example).
292 */
293 unsigned char *s = temp->samples;
294 unsigned char *d;
295 int hh = temp->h;
296 int n = fz_colorspace_n(ctx, colorspace);
297
298 /* alpha = 1 here for the same reason as earlier */
299 conv = fz_new_pixmap_with_bbox(ctx, colorspace, bbox, NULL, 1);
300 d = conv->samples;
301 while (hh--)
302 {
303 int len = temp->w;
304 while (len--)
305 {
306 int v = *s++;
307 int a = *s++;
308 const float *f = shade->function[v];
309 for (k = 0; k < n; k++)
310 *d++ = fz_clampi(255 * f[k], 0, 255);
311 *d++ = a;
312 }
313 d += conv->stride - conv->w * conv->n;
314 s += temp->stride - temp->w * temp->n;
315 }
316 fz_drop_pixmap(ctx, temp);
317 temp = conv;
318 conv = NULL;
319
320 /* Now Change from our device_n colorspace into the target colorspace/spots. */
321 conv = fz_clone_pixmap_area_with_different_seps(ctx, temp, NULL, dest->colorspace, dest->seps, color_params, NULL);
322 }
323 else
324 {
325 unsigned char *s = temp->samples;
326 unsigned char *d;
327 int da;
328 int sa = temp->alpha;
329 int hh = temp->h;
330 int cn = fz_colorspace_n(ctx, colorspace);
331 int m = dest->n - dest->alpha;
332 int n = fz_colorspace_n(ctx, dest->colorspace);
333
334 if (dest->colorspace)
335 {
336 fz_find_color_converter(ctx, &cc, colorspace, dest->colorspace, NULL, color_params);
337 for (i = 0; i < 256; i++)
338 {
339 cc.convert(ctx, &cc, shade->function[i], color);
340 for (k = 0; k < n; k++)
341 clut[i][k] = color[k] * 255;
342 for (; k < m; k++)
343 clut[i][k] = 0;
344 clut[i][k] = shade->function[i][cn] * 255;
345 }
346 fz_drop_color_converter(ctx, &cc);
347 }
348 else
349 {
350 for (i = 0; i < 256; i++)
351 {
352 for (k = 0; k < m; k++)
353 clut[i][k] = 0;
354 clut[i][k] = shade->function[i][cn] * 255;
355 }
356 }
357
358 conv = fz_new_pixmap_with_bbox(ctx, dest->colorspace, bbox, dest->seps, 1);
359 d = conv->samples;
360 da = conv->alpha;
361 while (hh--)
362 {
363 int len = temp->w;
364 while (len--)
365 {
366 int v = *s++;
367 int a = (da ? clut[v][conv->n - 1] : 255);
368 if (sa)
369 a = fz_mul255(*s++, a);
370 for (k = 0; k < conv->n - da; k++)
371 *d++ = fz_mul255(clut[v][k], a);
372 if (da)
373 *d++ = a;
374 }
375 d += conv->stride - conv->w * conv->n;
376 s += temp->stride - temp->w * temp->n;
377 }
378 }
379 fz_paint_pixmap_with_overprint(dest, conv, eop);
380 }
381 }
382 fz_always(ctx)
383 {
384 if (shade->use_function)
385 {
386 fz_drop_color_converter(ctx, &cc);
387 fz_drop_pixmap(ctx, temp);
388 fz_drop_pixmap(ctx, conv);
389 }
390 fz_fin_cached_color_converter(ctx, &ptd.cc);
391 }
392 fz_catch(ctx)
393 fz_rethrow(ctx);
394}
395