Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/fitz/draw-mesh.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2021 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 | |
| 25 #include "color-imp.h" | |
| 26 #include "draw-imp.h" | |
| 27 #include "pixmap-imp.h" | |
| 28 | |
| 29 #include <assert.h> | |
| 30 #include <math.h> | |
| 31 | |
| 32 enum { MAXN = 2 + FZ_MAX_COLORS }; | |
| 33 | |
| 34 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) | |
| 35 { | |
| 36 unsigned char *p; | |
| 37 int c[MAXN], dc[MAXN]; | |
| 38 int k, w; | |
| 39 float div, mul; | |
| 40 int x0, x1, pa; | |
| 41 | |
| 42 /* Ensure that fx0 is left edge, and fx1 is right */ | |
| 43 if (fx0 > fx1) | |
| 44 { | |
| 45 const int *v; | |
| 46 int t = fx0; fx0 = fx1; fx1 = t; | |
| 47 v = v0; v0 = v1; v1 = v; | |
| 48 } | |
| 49 else if (fx0 == fx1) | |
| 50 return; | |
| 51 | |
| 52 /* Clip fx0, fx1 to range */ | |
| 53 if (fx0 >= cx1) | |
| 54 return; | |
| 55 if (fx1 <= cx0) | |
| 56 return; | |
| 57 x0 = (fx0 > cx0 ? fx0 : cx0); | |
| 58 x1 = (fx1 < cx1 ? fx1 : cx1); | |
| 59 | |
| 60 w = x1 - x0; | |
| 61 if (w == 0) | |
| 62 return; | |
| 63 | |
| 64 div = 1.0f / (fx1 - fx0); | |
| 65 mul = (x0 - fx0); | |
| 66 for (k = 0; k < n; k++) | |
| 67 { | |
| 68 dc[k] = (v1[k] - v0[k]) * div; | |
| 69 c[k] = v0[k] + dc[k] * mul; | |
| 70 } | |
| 71 | |
| 72 p = pix->samples + ((x0 - pix->x) * pix->n) + ((y - pix->y) * pix->stride); | |
| 73 pa = pix->alpha; | |
| 74 do | |
| 75 { | |
| 76 for (k = 0; k < n; k++) | |
| 77 { | |
| 78 *p++ = c[k]>>16; | |
| 79 c[k] += dc[k]; | |
| 80 } | |
| 81 if (pa) | |
| 82 *p++ = 255; | |
| 83 } | |
| 84 while (--w); | |
| 85 } | |
| 86 | |
| 87 typedef struct | |
| 88 { | |
| 89 float x; | |
| 90 float dx; | |
| 91 int v[2*MAXN]; | |
| 92 } edge_data; | |
| 93 | |
| 94 static inline void prepare_edge(const float *FZ_RESTRICT vtop, const float *FZ_RESTRICT vbot, edge_data *FZ_RESTRICT edge, float y, int n) | |
| 95 { | |
| 96 float r = 1.0f / (vbot[1] - vtop[1]); | |
| 97 float t = (y - vtop[1]) * r; | |
| 98 float diff = vbot[0] - vtop[0]; | |
| 99 int i; | |
| 100 | |
| 101 edge->x = vtop[0] + diff * t; | |
| 102 edge->dx = diff * r; | |
| 103 | |
| 104 for (i = 0; i < n; i++) | |
| 105 { | |
| 106 diff = vbot[i+2] - vtop[i+2]; | |
| 107 edge->v[i] = (int)(65536.0f * (vtop[i+2] + diff * t)); | |
| 108 edge->v[i+MAXN] = (int)(65536.0f * diff * r); | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 static inline void step_edge(edge_data *edge, int n) | |
| 113 { | |
| 114 int i; | |
| 115 | |
| 116 edge->x += edge->dx; | |
| 117 | |
| 118 for (i = 0; i < n; i++) | |
| 119 { | |
| 120 edge->v[i] += edge->v[i + MAXN]; | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 static void | |
| 125 fz_paint_triangle(fz_pixmap *pix, float *v[3], int n, fz_irect bbox) | |
| 126 { | |
| 127 edge_data e0, e1; | |
| 128 int top, mid, bot; | |
| 129 float y, y1; | |
| 130 int minx, maxx; | |
| 131 | |
| 132 top = bot = 0; | |
| 133 if (v[1][1] < v[0][1]) top = 1; else bot = 1; | |
| 134 if (v[2][1] < v[top][1]) top = 2; | |
| 135 else if (v[2][1] > v[bot][1]) bot = 2; | |
| 136 if (v[top][1] == v[bot][1]) return; | |
| 137 | |
| 138 /* Test if the triangle is completely outside the scissor rect */ | |
| 139 if (v[bot][1] < bbox.y0) return; | |
| 140 if (v[top][1] > bbox.y1) return; | |
| 141 | |
| 142 /* Magic! Ensure that mid/top/bot are all different */ | |
| 143 mid = 3^top^bot; | |
| 144 | |
| 145 assert(top != bot && top != mid && mid != bot); | |
| 146 | |
| 147 minx = fz_maxi(bbox.x0, pix->x); | |
| 148 maxx = fz_mini(bbox.x1, pix->x + pix->w); | |
| 149 | |
| 150 y = ceilf(fz_max(bbox.y0, v[top][1])); | |
| 151 y1 = ceilf(fz_min(bbox.y1, v[mid][1])); | |
| 152 | |
| 153 n -= 2; | |
| 154 prepare_edge(v[top], v[bot], &e0, y, n); | |
| 155 if (y < y1) | |
| 156 { | |
| 157 prepare_edge(v[top], v[mid], &e1, y, n); | |
| 158 | |
| 159 do | |
| 160 { | |
| 161 paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n); | |
| 162 step_edge(&e0, n); | |
| 163 step_edge(&e1, n); | |
| 164 y ++; | |
| 165 } | |
| 166 while (y < y1); | |
| 167 } | |
| 168 | |
| 169 y1 = ceilf(fz_min(bbox.y1, v[bot][1])); | |
| 170 if (y < y1) | |
| 171 { | |
| 172 prepare_edge(v[mid], v[bot], &e1, y, n); | |
| 173 | |
| 174 do | |
| 175 { | |
| 176 paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n); | |
| 177 y ++; | |
| 178 if (y >= y1) | |
| 179 break; | |
| 180 step_edge(&e0, n); | |
| 181 step_edge(&e1, n); | |
| 182 } | |
| 183 while (1); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 struct paint_tri_data | |
| 188 { | |
| 189 const fz_shade *shade; | |
| 190 fz_pixmap *dest; | |
| 191 fz_irect bbox; | |
| 192 fz_color_converter cc; | |
| 193 }; | |
| 194 | |
| 195 static void | |
| 196 prepare_mesh_vertex(fz_context *ctx, void *arg, fz_vertex *v, const float *input) | |
| 197 { | |
| 198 struct paint_tri_data *ptd = (struct paint_tri_data *)arg; | |
| 199 const fz_shade *shade = ptd->shade; | |
| 200 fz_pixmap *dest = ptd->dest; | |
| 201 float *output = v->c; | |
| 202 int i; | |
| 203 | |
| 204 if (shade->function_stride) | |
| 205 { | |
| 206 float f = input[0]; | |
| 207 if (shade->type >= 4 && shade->type <= 7) | |
| 208 f = (f - shade->u.m.c0[0]) / (shade->u.m.c1[0] - shade->u.m.c0[0]); | |
| 209 output[0] = f * 255; | |
| 210 } | |
| 211 else | |
| 212 { | |
| 213 int n = fz_colorspace_n(ctx, dest->colorspace); | |
| 214 int a = dest->alpha; | |
| 215 int m = dest->n - a; | |
| 216 for (i = n; i < m; i++) | |
| 217 output[i] = 0; | |
| 218 if (ptd->cc.convert) | |
| 219 ptd->cc.convert(ctx, &ptd->cc, input, output); | |
| 220 for (i = 0; i < m; i++) | |
| 221 output[i] *= 255; | |
| 222 if (a) | |
| 223 output[m] = 255; | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 static void | |
| 228 do_paint_tri(fz_context *ctx, void *arg, fz_vertex *av, fz_vertex *bv, fz_vertex *cv) | |
| 229 { | |
| 230 struct paint_tri_data *ptd = (struct paint_tri_data *)arg; | |
| 231 float *vertices[3]; | |
| 232 fz_pixmap *dest; | |
| 233 | |
| 234 vertices[0] = (float *)av; | |
| 235 vertices[1] = (float *)bv; | |
| 236 vertices[2] = (float *)cv; | |
| 237 | |
| 238 dest = ptd->dest; | |
| 239 fz_paint_triangle(dest, vertices, 2 + dest->n - dest->alpha, ptd->bbox); | |
| 240 } | |
| 241 | |
| 242 struct fz_shade_color_cache | |
| 243 { | |
| 244 fz_colorspace *src; | |
| 245 fz_colorspace *dst; | |
| 246 fz_color_params params; | |
| 247 int full; | |
| 248 fz_color_converter cached; | |
| 249 fz_colorspace *src2; | |
| 250 fz_colorspace *dst2; | |
| 251 fz_color_params params2; | |
| 252 int full2; | |
| 253 fz_color_converter cached2; | |
| 254 }; | |
| 255 | |
| 256 void | |
| 257 fz_drop_shade_color_cache(fz_context *ctx, fz_shade_color_cache *cache) | |
| 258 { | |
| 259 if (cache == NULL) | |
| 260 return; | |
| 261 | |
| 262 fz_drop_colorspace(ctx, cache->src); | |
| 263 fz_drop_colorspace(ctx, cache->dst); | |
| 264 if (cache->full) | |
| 265 fz_fin_cached_color_converter(ctx, &cache->cached); | |
| 266 | |
| 267 fz_drop_colorspace(ctx, cache->src2); | |
| 268 fz_drop_colorspace(ctx, cache->dst2); | |
| 269 if (cache->full2) | |
| 270 fz_drop_color_converter(ctx, &cache->cached2); | |
| 271 | |
| 272 fz_free(ctx, cache); | |
| 273 } | |
| 274 | |
| 275 void | |
| 276 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) | |
| 277 { | |
| 278 unsigned char clut[256][FZ_MAX_COLORS]; | |
| 279 fz_pixmap *temp = NULL; | |
| 280 fz_pixmap *conv = NULL; | |
| 281 fz_color_converter cc = { 0 }; | |
| 282 float color[FZ_MAX_COLORS]; | |
| 283 struct paint_tri_data ptd = { 0 }; | |
| 284 int i, k; | |
| 285 fz_matrix local_ctm; | |
| 286 fz_shade_color_cache *cache = NULL; | |
| 287 int recache = 0; | |
| 288 int recache2 = 0; | |
| 289 int stride = shade->function_stride; | |
| 290 | |
| 291 fz_var(temp); | |
| 292 fz_var(conv); | |
| 293 fz_var(recache); | |
| 294 fz_var(recache2); | |
| 295 fz_var(cc); | |
| 296 | |
| 297 if (colorspace == NULL) | |
| 298 colorspace = shade->colorspace; | |
| 299 | |
| 300 if (color_cache) | |
| 301 { | |
| 302 cache = *color_cache; | |
| 303 if (cache == NULL) | |
| 304 *color_cache = cache = fz_malloc_struct(ctx, fz_shade_color_cache); | |
| 305 } | |
| 306 | |
| 307 fz_try(ctx) | |
| 308 { | |
| 309 local_ctm = fz_concat(shade->matrix, ctm); | |
| 310 | |
| 311 if (stride) | |
| 312 { | |
| 313 /* We need to use alpha = 1 here, because the shade might not fill the bbox. */ | |
| 314 temp = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 1); | |
| 315 fz_clear_pixmap(ctx, temp); | |
| 316 } | |
| 317 else | |
| 318 { | |
| 319 temp = dest; | |
| 320 } | |
| 321 | |
| 322 ptd.dest = temp; | |
| 323 ptd.shade = shade; | |
| 324 ptd.bbox = bbox; | |
| 325 | |
| 326 if (temp->colorspace) | |
| 327 { | |
| 328 if (cache && cache->full && cache->src == colorspace && cache->dst == temp->colorspace && | |
| 329 cache->params.op == color_params.op && | |
| 330 cache->params.opm == color_params.opm && | |
| 331 cache->params.ri == color_params.ri) | |
| 332 { | |
| 333 ptd.cc = cache->cached; | |
| 334 cache->full = 0; | |
| 335 } | |
| 336 else | |
| 337 fz_init_cached_color_converter(ctx, &ptd.cc, colorspace, temp->colorspace, temp->seps, NULL, color_params); | |
| 338 | |
| 339 /* Drop the existing contents of the cache. */ | |
| 340 if (cache) | |
| 341 { | |
| 342 fz_drop_colorspace(ctx, cache->src); | |
| 343 cache->src = NULL; | |
| 344 fz_drop_colorspace(ctx, cache->dst); | |
| 345 cache->dst = NULL; | |
| 346 if (cache->full) | |
| 347 fz_fin_cached_color_converter(ctx, &cache->cached); | |
| 348 cache->full = 0; | |
| 349 | |
| 350 /* Remember that we can put stuff back into the cache. */ | |
| 351 recache = 1; | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 fz_process_shade(ctx, shade, local_ctm, fz_rect_from_irect(bbox), prepare_mesh_vertex, &do_paint_tri, &ptd); | |
| 356 | |
| 357 if (stride) | |
| 358 { | |
| 359 /* If the shade is defined in a deviceN (or separation, | |
| 360 * which is the same internally to MuPDF) space, then | |
| 361 * we need to render it in deviceN before painting it | |
| 362 * to the destination. If not, we are free to render it | |
| 363 * direct to the target. */ | |
| 364 if (fz_colorspace_is_device_n(ctx, colorspace)) | |
| 365 { | |
| 366 /* We've drawn it as greyscale, with the values being | |
| 367 * the input to the function. Now make DevN version | |
| 368 * by mapping that greyscale through the function. | |
| 369 * This seems inefficient, but it's actually required, | |
| 370 * because we need to apply the function lookup POST | |
| 371 * interpolation in the do_paint_tri routines, not | |
| 372 * before it to avoid problems with some test files | |
| 373 * (tests/GhentV3.0/061_Shading_x1a.pdf for example). | |
| 374 */ | |
| 375 unsigned char *s = temp->samples; | |
| 376 unsigned char *d; | |
| 377 int hh = temp->h; | |
| 378 int n = fz_colorspace_n(ctx, colorspace); | |
| 379 | |
| 380 /* alpha = 1 here for the same reason as earlier */ | |
| 381 conv = fz_new_pixmap_with_bbox(ctx, colorspace, bbox, NULL, 1); | |
| 382 d = conv->samples; | |
| 383 while (hh--) | |
| 384 { | |
| 385 int len = temp->w; | |
| 386 while (len--) | |
| 387 { | |
| 388 int v = *s++; | |
| 389 int a = *s++; | |
| 390 const float *f = &shade->function[v*stride]; | |
| 391 for (k = 0; k < n; k++) | |
| 392 *d++ = fz_clampi(255 * f[k], 0, 255); | |
| 393 *d++ = a; | |
| 394 } | |
| 395 d += conv->stride - conv->w * (size_t)conv->n; | |
| 396 s += temp->stride - temp->w * (size_t)temp->n; | |
| 397 } | |
| 398 fz_drop_pixmap(ctx, temp); | |
| 399 temp = conv; | |
| 400 conv = NULL; | |
| 401 | |
| 402 /* Now Change from our device_n colorspace into the target colorspace/spots. */ | |
| 403 conv = fz_clone_pixmap_area_with_different_seps(ctx, temp, NULL, dest->colorspace, dest->seps, color_params, NULL); | |
| 404 } | |
| 405 else | |
| 406 { | |
| 407 unsigned char *s = temp->samples; | |
| 408 unsigned char *d; | |
| 409 int da; | |
| 410 int sa = temp->alpha; | |
| 411 int hh = temp->h; | |
| 412 int cn = fz_colorspace_n(ctx, colorspace); | |
| 413 int m = dest->n - dest->alpha; | |
| 414 int n = fz_colorspace_n(ctx, dest->colorspace); | |
| 415 | |
| 416 if (dest->colorspace) | |
| 417 { | |
| 418 if (cache && cache->full2 && cache->src2 == colorspace && cache->dst2 == dest->colorspace && | |
| 419 cache->params2.op == color_params.op && | |
| 420 cache->params2.opm == color_params.opm && | |
| 421 cache->params2.ri == color_params.ri) | |
| 422 { | |
| 423 cc = cache->cached2; | |
| 424 cache->full2 = 0; | |
| 425 } | |
| 426 else | |
| 427 fz_find_color_converter(ctx, &cc, colorspace, dest->colorspace, dest->seps, NULL, color_params); | |
| 428 | |
| 429 /* Drop the existing contents of the cache */ | |
| 430 if (cache) | |
| 431 { | |
| 432 fz_drop_colorspace(ctx, cache->src2); | |
| 433 cache->src2 = NULL; | |
| 434 fz_drop_colorspace(ctx, cache->dst2); | |
| 435 cache->dst2 = NULL; | |
| 436 if (cache->full2) | |
| 437 fz_drop_color_converter(ctx, &cache->cached2); | |
| 438 cache->full2 = 0; | |
| 439 | |
| 440 /* Remember that we can put stuff back into the cache. */ | |
| 441 recache2 = 1; | |
| 442 } | |
| 443 for (i = 0; i < 256; i++) | |
| 444 { | |
| 445 cc.convert(ctx, &cc, &shade->function[i*stride], color); | |
| 446 for (k = 0; k < n; k++) | |
| 447 clut[i][k] = color[k] * 255; | |
| 448 for (; k < m; k++) | |
| 449 clut[i][k] = 0; | |
| 450 clut[i][k] = shade->function[i*stride + cn] * 255; | |
| 451 } | |
| 452 } | |
| 453 else | |
| 454 { | |
| 455 for (i = 0; i < 256; i++) | |
| 456 { | |
| 457 for (k = 0; k < m; k++) | |
| 458 clut[i][k] = 0; | |
| 459 clut[i][k] = shade->function[i*stride + cn] * 255; | |
| 460 } | |
| 461 } | |
| 462 | |
| 463 conv = fz_new_pixmap_with_bbox(ctx, dest->colorspace, bbox, dest->seps, 1); | |
| 464 d = conv->samples; | |
| 465 da = conv->alpha; | |
| 466 while (hh--) | |
| 467 { | |
| 468 int len = temp->w; | |
| 469 while (len--) | |
| 470 { | |
| 471 int v = *s++; | |
| 472 int a = (da ? clut[v][conv->n - 1] : 255); | |
| 473 if (sa) | |
| 474 a = fz_mul255(*s++, a); | |
| 475 for (k = 0; k < conv->n - da; k++) | |
| 476 *d++ = fz_mul255(clut[v][k], a); | |
| 477 if (da) | |
| 478 *d++ = a; | |
| 479 } | |
| 480 d += conv->stride - conv->w * (size_t)conv->n; | |
| 481 s += temp->stride - temp->w * (size_t)temp->n; | |
| 482 } | |
| 483 } | |
| 484 fz_paint_pixmap_with_overprint(dest, conv, eop); | |
| 485 } | |
| 486 } | |
| 487 fz_always(ctx) | |
| 488 { | |
| 489 if (recache) | |
| 490 { | |
| 491 cache->src = fz_keep_colorspace(ctx, colorspace); | |
| 492 cache->dst = fz_keep_colorspace(ctx, temp->colorspace); | |
| 493 cache->params = color_params; | |
| 494 cache->cached = ptd.cc; | |
| 495 cache->full = 1; | |
| 496 } | |
| 497 else | |
| 498 fz_fin_cached_color_converter(ctx, &ptd.cc); | |
| 499 if (stride) | |
| 500 { | |
| 501 if (recache2) | |
| 502 { | |
| 503 cache->src2 = fz_keep_colorspace(ctx, colorspace); | |
| 504 cache->dst2 = fz_keep_colorspace(ctx, dest->colorspace); | |
| 505 cache->params2 = color_params; | |
| 506 cache->cached2 = cc; | |
| 507 cache->full2 = 1; | |
| 508 } | |
| 509 else | |
| 510 fz_drop_color_converter(ctx, &cc); | |
| 511 fz_drop_pixmap(ctx, temp); | |
| 512 fz_drop_pixmap(ctx, conv); | |
| 513 } | |
| 514 } | |
| 515 fz_catch(ctx) | |
| 516 fz_rethrow(ctx); | |
| 517 } |
