1 | #include "mupdf/fitz.h" |
2 | |
3 | #include <assert.h> |
4 | #include <limits.h> |
5 | #include <string.h> |
6 | #include <math.h> |
7 | |
8 | fz_pixmap * |
9 | fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix) |
10 | { |
11 | return fz_keep_storable(ctx, &pix->storable); |
12 | } |
13 | |
14 | void |
15 | fz_drop_pixmap(fz_context *ctx, fz_pixmap *pix) |
16 | { |
17 | fz_drop_storable(ctx, &pix->storable); |
18 | } |
19 | |
20 | void |
21 | fz_drop_pixmap_imp(fz_context *ctx, fz_storable *pix_) |
22 | { |
23 | fz_pixmap *pix = (fz_pixmap *)pix_; |
24 | |
25 | fz_drop_colorspace(ctx, pix->colorspace); |
26 | fz_drop_separations(ctx, pix->seps); |
27 | if (pix->flags & FZ_PIXMAP_FLAG_FREE_SAMPLES) |
28 | fz_free(ctx, pix->samples); |
29 | fz_drop_pixmap(ctx, pix->underlying); |
30 | fz_free(ctx, pix); |
31 | } |
32 | |
33 | /* |
34 | Create a new pixmap, with its origin at |
35 | (0,0) using the supplied data block. |
36 | |
37 | cs: The colorspace to use for the pixmap, or NULL for an alpha |
38 | plane/mask. |
39 | |
40 | w: The width of the pixmap (in pixels) |
41 | |
42 | h: The height of the pixmap (in pixels) |
43 | |
44 | seps: Details of separations. |
45 | |
46 | alpha: 0 for no alpha, 1 for alpha. |
47 | |
48 | stride: The byte offset from the pixel data in a row to the pixel |
49 | data in the next row. |
50 | |
51 | samples: The data block to keep the samples in. |
52 | |
53 | Returns a pointer to the new pixmap. Throws exception on failure to |
54 | allocate. |
55 | */ |
56 | fz_pixmap * |
57 | fz_new_pixmap_with_data(fz_context *ctx, fz_colorspace *colorspace, int w, int h, fz_separations *seps, int alpha, int stride, unsigned char *samples) |
58 | { |
59 | fz_pixmap *pix; |
60 | int s = fz_count_active_separations(ctx, seps); |
61 | int n; |
62 | |
63 | if (w < 0 || h < 0) |
64 | fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal dimensions for pixmap %d %d" , w, h); |
65 | |
66 | n = alpha + s + fz_colorspace_n(ctx, colorspace); |
67 | if (stride < n*w && stride > -n*w) |
68 | fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal stride for pixmap (n=%d w=%d, stride=%d)" , n, w, stride); |
69 | if (samples == NULL && stride < n*w) |
70 | fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal -ve stride for pixmap without data" ); |
71 | if (n > FZ_MAX_COLORS) |
72 | fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal number of colorants" ); |
73 | |
74 | pix = fz_malloc_struct(ctx, fz_pixmap); |
75 | FZ_INIT_STORABLE(pix, 1, fz_drop_pixmap_imp); |
76 | pix->x = 0; |
77 | pix->y = 0; |
78 | pix->w = w; |
79 | pix->h = h; |
80 | pix->alpha = alpha = !!alpha; |
81 | pix->flags = FZ_PIXMAP_FLAG_INTERPOLATE; |
82 | pix->xres = 96; |
83 | pix->yres = 96; |
84 | pix->colorspace = NULL; |
85 | pix->n = n; |
86 | pix->s = s; |
87 | pix->seps = fz_keep_separations(ctx, seps); |
88 | pix->stride = stride; |
89 | |
90 | if (colorspace) |
91 | { |
92 | pix->colorspace = fz_keep_colorspace(ctx, colorspace); |
93 | } |
94 | else |
95 | { |
96 | assert(alpha || s); |
97 | } |
98 | |
99 | pix->samples = samples; |
100 | if (!samples) |
101 | { |
102 | fz_try(ctx) |
103 | { |
104 | if (pix->stride - 1 > INT_MAX / pix->n) |
105 | fz_throw(ctx, FZ_ERROR_GENERIC, "overly wide image" ); |
106 | pix->samples = fz_malloc(ctx, pix->h * pix->stride); |
107 | } |
108 | fz_catch(ctx) |
109 | { |
110 | fz_drop_colorspace(ctx, pix->colorspace); |
111 | fz_free(ctx, pix); |
112 | fz_rethrow(ctx); |
113 | } |
114 | pix->flags |= FZ_PIXMAP_FLAG_FREE_SAMPLES; |
115 | } |
116 | |
117 | return pix; |
118 | } |
119 | |
120 | /* |
121 | Create a new pixmap, with its origin at (0,0) |
122 | |
123 | cs: The colorspace to use for the pixmap, or NULL for an alpha |
124 | plane/mask. |
125 | |
126 | w: The width of the pixmap (in pixels) |
127 | |
128 | h: The height of the pixmap (in pixels) |
129 | |
130 | seps: Details of separations. |
131 | |
132 | alpha: 0 for no alpha, 1 for alpha. |
133 | |
134 | Returns a pointer to the new pixmap. Throws exception on failure to |
135 | allocate. |
136 | */ |
137 | fz_pixmap * |
138 | fz_new_pixmap(fz_context *ctx, fz_colorspace *colorspace, int w, int h, fz_separations *seps, int alpha) |
139 | { |
140 | int stride; |
141 | int s = fz_count_active_separations(ctx, seps); |
142 | if (!colorspace && s == 0) alpha = 1; |
143 | stride = (fz_colorspace_n(ctx, colorspace) + s + alpha) * w; |
144 | return fz_new_pixmap_with_data(ctx, colorspace, w, h, seps, alpha, stride, NULL); |
145 | } |
146 | |
147 | /* |
148 | Create a pixmap of a given size, |
149 | location and pixel format. |
150 | |
151 | The bounding box specifies the size of the created pixmap and |
152 | where it will be located. The colorspace determines the number |
153 | of components per pixel. Alpha is always present. Pixmaps are |
154 | reference counted, so drop references using fz_drop_pixmap. |
155 | |
156 | colorspace: Colorspace format used for the created pixmap. The |
157 | pixmap will keep a reference to the colorspace. |
158 | |
159 | bbox: Bounding box specifying location/size of created pixmap. |
160 | |
161 | seps: Details of separations. |
162 | |
163 | alpha: 0 for no alpha, 1 for alpha. |
164 | |
165 | Returns a pointer to the new pixmap. Throws exception on failure to |
166 | allocate. |
167 | */ |
168 | fz_pixmap * |
169 | fz_new_pixmap_with_bbox(fz_context *ctx, fz_colorspace *colorspace, fz_irect bbox, fz_separations *seps, int alpha) |
170 | { |
171 | fz_pixmap *pixmap; |
172 | pixmap = fz_new_pixmap(ctx, colorspace, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, seps, alpha); |
173 | pixmap->x = bbox.x0; |
174 | pixmap->y = bbox.y0; |
175 | return pixmap; |
176 | } |
177 | |
178 | /* |
179 | Create a pixmap of a given size, |
180 | location and pixel format, using the supplied data block. |
181 | |
182 | The bounding box specifies the size of the created pixmap and |
183 | where it will be located. The colorspace determines the number |
184 | of components per pixel. Alpha is always present. Pixmaps are |
185 | reference counted, so drop references using fz_drop_pixmap. |
186 | |
187 | colorspace: Colorspace format used for the created pixmap. The |
188 | pixmap will keep a reference to the colorspace. |
189 | |
190 | rect: Bounding box specifying location/size of created pixmap. |
191 | |
192 | seps: Details of separations. |
193 | |
194 | alpha: Number of alpha planes (0 or 1). |
195 | |
196 | samples: The data block to keep the samples in. |
197 | |
198 | Returns a pointer to the new pixmap. Throws exception on failure to |
199 | allocate. |
200 | */ |
201 | fz_pixmap * |
202 | fz_new_pixmap_with_bbox_and_data(fz_context *ctx, fz_colorspace *colorspace, fz_irect bbox, fz_separations *seps, int alpha, unsigned char *samples) |
203 | { |
204 | int w = bbox.x1 - bbox.x0; |
205 | int stride; |
206 | int s = fz_count_active_separations(ctx, seps); |
207 | fz_pixmap *pixmap; |
208 | if (!colorspace && s == 0) alpha = 1; |
209 | stride = (fz_colorspace_n(ctx, colorspace) + s + alpha) * w; |
210 | pixmap = fz_new_pixmap_with_data(ctx, colorspace, w, bbox.y1 - bbox.y0, seps, alpha, stride, samples); |
211 | pixmap->x = bbox.x0; |
212 | pixmap->y = bbox.y0; |
213 | return pixmap; |
214 | } |
215 | |
216 | /* |
217 | Create a new pixmap that represents |
218 | a subarea of the specified pixmap. A reference is taken to his |
219 | pixmap that will be dropped on destruction. |
220 | |
221 | The supplied rectangle must be wholly contained within the original |
222 | pixmap. |
223 | |
224 | Returns a pointer to the new pixmap. Throws exception on failure to |
225 | allocate. |
226 | */ |
227 | fz_pixmap *fz_new_pixmap_from_pixmap(fz_context *ctx, fz_pixmap *pixmap, const fz_irect *rect) |
228 | { |
229 | fz_irect local_rect; |
230 | fz_pixmap *subpix; |
231 | |
232 | if (!pixmap) |
233 | return NULL; |
234 | |
235 | if (rect == NULL) |
236 | { |
237 | rect = &local_rect; |
238 | local_rect.x0 = pixmap->x; |
239 | local_rect.y0 = pixmap->y; |
240 | local_rect.x1 = pixmap->x + pixmap->w; |
241 | local_rect.y1 = pixmap->y + pixmap->h; |
242 | } |
243 | else if (rect->x0 < pixmap->x || rect->y0 < pixmap->y || rect->x1 > pixmap->x + pixmap->w || rect->y1 > pixmap->y + pixmap->h) |
244 | fz_throw(ctx, FZ_ERROR_GENERIC, "Pixmap region is not a subarea" ); |
245 | |
246 | subpix = fz_malloc_struct(ctx, fz_pixmap); |
247 | *subpix = *pixmap; |
248 | subpix->storable.refs = 1; |
249 | subpix->x = rect->x0; |
250 | subpix->y = rect->y0; |
251 | subpix->w = rect->x1 - rect->x0; |
252 | subpix->h = rect->y1 - rect->y0; |
253 | subpix->samples += (rect->x0 - pixmap->x) + (rect->y0 - pixmap->y) * pixmap->stride; |
254 | subpix->underlying = fz_keep_pixmap(ctx, pixmap); |
255 | subpix->colorspace = fz_keep_colorspace(ctx, pixmap->colorspace); |
256 | subpix->seps = fz_keep_separations(ctx, pixmap->seps); |
257 | subpix->flags &= ~FZ_PIXMAP_FLAG_FREE_SAMPLES; |
258 | |
259 | return subpix; |
260 | } |
261 | |
262 | fz_pixmap *fz_clone_pixmap(fz_context *ctx, fz_pixmap *old) |
263 | { |
264 | fz_pixmap *pix = fz_new_pixmap_with_bbox(ctx, old->colorspace, fz_make_irect(old->x, old->y, old->w, old->h), old->seps, old->alpha); |
265 | memcpy(pix->samples, old->samples, pix->stride * pix->h); |
266 | return pix; |
267 | } |
268 | |
269 | /* |
270 | Return the bounding box for a pixmap. |
271 | */ |
272 | fz_irect |
273 | fz_pixmap_bbox(fz_context *ctx, const fz_pixmap *pix) |
274 | { |
275 | fz_irect bbox; |
276 | bbox.x0 = pix->x; |
277 | bbox.y0 = pix->y; |
278 | bbox.x1 = pix->x + pix->w; |
279 | bbox.y1 = pix->y + pix->h; |
280 | return bbox; |
281 | } |
282 | |
283 | fz_irect |
284 | fz_pixmap_bbox_no_ctx(const fz_pixmap *pix) |
285 | { |
286 | fz_irect bbox; |
287 | bbox.x0 = pix->x; |
288 | bbox.y0 = pix->y; |
289 | bbox.x1 = pix->x + pix->w; |
290 | bbox.y1 = pix->y + pix->h; |
291 | return bbox; |
292 | } |
293 | |
294 | /* |
295 | Return the colorspace of a pixmap |
296 | |
297 | Returns colorspace. |
298 | */ |
299 | fz_colorspace * |
300 | fz_pixmap_colorspace(fz_context *ctx, fz_pixmap *pix) |
301 | { |
302 | if (!pix) |
303 | return NULL; |
304 | return pix->colorspace; |
305 | } |
306 | |
307 | /* |
308 | Return the x value of the pixmap in pixels. |
309 | */ |
310 | int |
311 | fz_pixmap_x(fz_context *ctx, fz_pixmap *pix) |
312 | { |
313 | return pix->x; |
314 | } |
315 | |
316 | /* |
317 | Return the y value of the pixmap in pixels. |
318 | */ |
319 | int |
320 | fz_pixmap_y(fz_context *ctx, fz_pixmap *pix) |
321 | { |
322 | return pix->y; |
323 | } |
324 | |
325 | /* |
326 | Return the width of the pixmap in pixels. |
327 | */ |
328 | int |
329 | fz_pixmap_width(fz_context *ctx, fz_pixmap *pix) |
330 | { |
331 | return pix->w; |
332 | } |
333 | |
334 | /* |
335 | Return the height of the pixmap in pixels. |
336 | */ |
337 | int |
338 | fz_pixmap_height(fz_context *ctx, fz_pixmap *pix) |
339 | { |
340 | return pix->h; |
341 | } |
342 | |
343 | /* |
344 | Return the number of components in a pixmap. |
345 | |
346 | Returns the number of components (including spots and alpha). |
347 | */ |
348 | int |
349 | fz_pixmap_components(fz_context *ctx, fz_pixmap *pix) |
350 | { |
351 | return pix->n; |
352 | } |
353 | |
354 | /* |
355 | Return the number of colorants in a pixmap. |
356 | |
357 | Returns the number of colorants (components, less any spots and alpha). |
358 | */ |
359 | int |
360 | fz_pixmap_colorants(fz_context *ctx, fz_pixmap *pix) |
361 | { |
362 | return pix->n - pix->alpha - pix->s; |
363 | } |
364 | |
365 | /* |
366 | Return the number of spots in a pixmap. |
367 | |
368 | Returns the number of spots (components, less colorants and alpha). Does not throw exceptions. |
369 | */ |
370 | int |
371 | fz_pixmap_spots(fz_context *ctx, fz_pixmap *pix) |
372 | { |
373 | return pix->s; |
374 | } |
375 | |
376 | /* |
377 | Return the number of alpha planes in a pixmap. |
378 | |
379 | Returns the number of alphas. Does not throw exceptions. |
380 | */ |
381 | int |
382 | fz_pixmap_alpha(fz_context *ctx, fz_pixmap *pix) |
383 | { |
384 | return pix->alpha; |
385 | } |
386 | |
387 | /* |
388 | Return the number of bytes in a row in the pixmap. |
389 | */ |
390 | int |
391 | fz_pixmap_stride(fz_context *ctx, fz_pixmap *pix) |
392 | { |
393 | return pix->stride; |
394 | } |
395 | |
396 | /* |
397 | Returns a pointer to the pixel data of a pixmap. |
398 | |
399 | Returns the pointer. |
400 | */ |
401 | unsigned char * |
402 | fz_pixmap_samples(fz_context *ctx, fz_pixmap *pix) |
403 | { |
404 | if (!pix) |
405 | return NULL; |
406 | return pix->samples; |
407 | } |
408 | |
409 | /* |
410 | The slowest routine in most CMYK rendering profiles. |
411 | We therefore spend some effort to improve it. Rather than |
412 | writing bytes, we write uint32_t's. |
413 | */ |
414 | #ifdef ARCH_ARM |
415 | static void |
416 | clear_cmyka_bitmap_ARM(uint32_t *samples, int c, int value) |
417 | __attribute__((naked)); |
418 | |
419 | static void |
420 | clear_cmyka_bitmap_ARM(uint32_t *samples, int c, int value) |
421 | { |
422 | asm volatile( |
423 | ENTER_ARM |
424 | "stmfd r13!,{r4-r6,r14} \n" |
425 | "@ r0 = samples \n" |
426 | "@ r1 = c \n" |
427 | "@ r2 = value \n" |
428 | "mov r3, #255 \n" |
429 | "mov r12,#0 @ r12= 0 \n" |
430 | "subs r1, r1, #3 \n" |
431 | "ble 2f \n" |
432 | "str r12,[r13,#-20]! \n" |
433 | "str r12,[r13,#4] \n" |
434 | "str r12,[r13,#8] \n" |
435 | "str r12,[r13,#12] \n" |
436 | "str r12,[r13,#16] \n" |
437 | "strb r2, [r13,#3] \n" |
438 | "strb r3, [r13,#4] \n" |
439 | "strb r2, [r13,#8] \n" |
440 | "strb r3, [r13,#9] \n" |
441 | "strb r2, [r13,#13] \n" |
442 | "strb r3, [r13,#14] \n" |
443 | "strb r2, [r13,#18] \n" |
444 | "strb r3, [r13,#19] \n" |
445 | "ldmfd r13!,{r4,r5,r6,r12,r14} \n" |
446 | "1: \n" |
447 | "stmia r0!,{r4,r5,r6,r12,r14} \n" |
448 | "subs r1, r1, #4 \n" |
449 | "bgt 1b \n" |
450 | "2: \n" |
451 | "adds r1, r1, #3 \n" |
452 | "ble 4f \n" |
453 | "3: \n" |
454 | "strb r12,[r0], #1 \n" |
455 | "strb r12,[r0], #1 \n" |
456 | "strb r12,[r0], #1 \n" |
457 | "strb r2, [r0], #1 \n" |
458 | "strb r3, [r0], #1 \n" |
459 | "subs r1, r1, #1 \n" |
460 | "bgt 3b \n" |
461 | "4: \n" |
462 | "ldmfd r13!,{r4-r6,PC} \n" |
463 | ENTER_THUMB |
464 | ); |
465 | } |
466 | #endif |
467 | |
468 | static void |
469 | clear_cmyk_bitmap(unsigned char *samples, int w, int h, int spots, int stride, int value, int alpha) |
470 | { |
471 | uint32_t *s = (uint32_t *)(void *)samples; |
472 | uint8_t *t; |
473 | |
474 | if (w < 0 || h < 0) |
475 | return; |
476 | |
477 | if (spots) |
478 | { |
479 | int x, i; |
480 | spots += 4; |
481 | stride -= w * (spots + alpha); |
482 | for (; h > 0; h--) |
483 | { |
484 | for (x = w; x > 0; x--) |
485 | { |
486 | for (i = spots; i > 0; i--) |
487 | *samples++ = value; |
488 | if (alpha) |
489 | *samples++ = 255; |
490 | } |
491 | samples += stride; |
492 | } |
493 | return; |
494 | } |
495 | |
496 | if (alpha) |
497 | { |
498 | int c = w; |
499 | stride -= w*5; |
500 | if (stride == 0) |
501 | { |
502 | #ifdef ARCH_ARM |
503 | clear_cmyka_bitmap_ARM(s, c, alpha); |
504 | return; |
505 | #else |
506 | /* We can do it all fast (except for maybe a few stragglers) */ |
507 | union |
508 | { |
509 | uint8_t bytes[20]; |
510 | uint32_t words[5]; |
511 | } d; |
512 | |
513 | c *= h; |
514 | h = 1; |
515 | |
516 | d.words[0] = 0; |
517 | d.words[1] = 0; |
518 | d.words[2] = 0; |
519 | d.words[3] = 0; |
520 | d.words[4] = 0; |
521 | d.bytes[3] = value; |
522 | d.bytes[4] = 255; |
523 | d.bytes[8] = value; |
524 | d.bytes[9] = 255; |
525 | d.bytes[13] = value; |
526 | d.bytes[14] = 255; |
527 | d.bytes[18] = value; |
528 | d.bytes[19] = 255; |
529 | |
530 | c -= 3; |
531 | { |
532 | const uint32_t a0 = d.words[0]; |
533 | const uint32_t a1 = d.words[1]; |
534 | const uint32_t a2 = d.words[2]; |
535 | const uint32_t a3 = d.words[3]; |
536 | const uint32_t a4 = d.words[4]; |
537 | while (c > 0) |
538 | { |
539 | *s++ = a0; |
540 | *s++ = a1; |
541 | *s++ = a2; |
542 | *s++ = a3; |
543 | *s++ = a4; |
544 | c -= 4; |
545 | } |
546 | } |
547 | c += 3; |
548 | #endif |
549 | } |
550 | t = (unsigned char *)s; |
551 | w = c; |
552 | while (h--) |
553 | { |
554 | c = w; |
555 | while (c > 0) |
556 | { |
557 | *t++ = 0; |
558 | *t++ = 0; |
559 | *t++ = 0; |
560 | *t++ = value; |
561 | *t++ = 255; |
562 | c--; |
563 | } |
564 | t += stride; |
565 | } |
566 | } |
567 | else |
568 | { |
569 | stride -= w*4; |
570 | if ((stride & 3) == 0) |
571 | { |
572 | size_t W = w; |
573 | if (stride == 0) |
574 | { |
575 | W *= h; |
576 | h = 1; |
577 | } |
578 | W *= 4; |
579 | if (value == 0) |
580 | { |
581 | while (h--) |
582 | { |
583 | memset(s, 0, W); |
584 | s += (stride>>2); |
585 | } |
586 | } |
587 | else |
588 | { |
589 | /* We can do it all fast */ |
590 | union |
591 | { |
592 | uint8_t bytes[4]; |
593 | uint32_t word; |
594 | } d; |
595 | |
596 | d.word = 0; |
597 | d.bytes[3] = value; |
598 | { |
599 | const uint32_t a0 = d.word; |
600 | while (h--) |
601 | { |
602 | size_t WW = W >> 2; |
603 | while (WW--) |
604 | { |
605 | *s++ = a0; |
606 | } |
607 | s += (stride>>2); |
608 | } |
609 | } |
610 | } |
611 | } |
612 | else |
613 | { |
614 | t = (unsigned char *)s; |
615 | while (h--) |
616 | { |
617 | int c = w; |
618 | while (c > 0) |
619 | { |
620 | *t++ = 0; |
621 | *t++ = 0; |
622 | *t++ = 0; |
623 | *t++ = value; |
624 | c--; |
625 | } |
626 | t += stride; |
627 | } |
628 | } |
629 | } |
630 | } |
631 | |
632 | /* |
633 | Sets all components (including alpha) of |
634 | all pixels in a pixmap to 0. |
635 | |
636 | pix: The pixmap to clear. |
637 | */ |
638 | void |
639 | fz_clear_pixmap(fz_context *ctx, fz_pixmap *pix) |
640 | { |
641 | int stride = pix->w * pix->n; |
642 | int h = pix->h; |
643 | unsigned char *s = pix->samples; |
644 | if (stride == pix->stride) |
645 | { |
646 | stride *= h; |
647 | h = 1; |
648 | } |
649 | if (pix->alpha || fz_colorspace_is_subtractive(ctx, pix->colorspace)) |
650 | { |
651 | while (h--) |
652 | { |
653 | memset(s, 0, (unsigned int)stride); |
654 | s += pix->stride; |
655 | } |
656 | } |
657 | else if (pix->s == 0) |
658 | { |
659 | while (h--) |
660 | { |
661 | memset(s, 0xff, (unsigned int)stride); |
662 | s += pix->stride; |
663 | } |
664 | } |
665 | else |
666 | { |
667 | /* Horrible, slow case: additive with spots */ |
668 | int w = stride/pix->n; |
669 | int spots = pix->s; |
670 | int colorants = pix->n - spots; /* We know there is no alpha */ |
671 | while (h--) |
672 | { |
673 | int w2 = w; |
674 | while (w2--) |
675 | { |
676 | int i = colorants; |
677 | do |
678 | { |
679 | *s++ = 0xff; |
680 | i--; |
681 | } |
682 | while (i != 0); |
683 | |
684 | i = spots; |
685 | do |
686 | { |
687 | *s++ = 0; |
688 | i--; |
689 | } |
690 | while (i != 0); |
691 | } |
692 | } |
693 | } |
694 | } |
695 | |
696 | /* |
697 | Clears a pixmap with the given value. |
698 | |
699 | pix: The pixmap to clear. |
700 | |
701 | value: Values in the range 0 to 255 are valid. Each component |
702 | sample for each pixel in the pixmap will be set to this value, |
703 | while alpha will always be set to 255 (non-transparent). |
704 | */ |
705 | /* This function is horrible, and should be removed from the |
706 | * API and replaced with a less magic one. */ |
707 | void |
708 | fz_clear_pixmap_with_value(fz_context *ctx, fz_pixmap *pix, int value) |
709 | { |
710 | unsigned char *s; |
711 | int w, h, n, stride, len; |
712 | int alpha = pix->alpha; |
713 | |
714 | w = pix->w; |
715 | h = pix->h; |
716 | if (w < 0 || h < 0) |
717 | return; |
718 | |
719 | /* CMYK needs special handling (and potentially any other subtractive colorspaces) */ |
720 | if (fz_colorspace_n(ctx, pix->colorspace) == 4) |
721 | { |
722 | clear_cmyk_bitmap(pix->samples, w, h, pix->s, pix->stride, 255-value, pix->alpha); |
723 | return; |
724 | } |
725 | |
726 | n = pix->n; |
727 | stride = pix->stride; |
728 | len = w * n; |
729 | |
730 | s = pix->samples; |
731 | if (value == 255 || !alpha) |
732 | { |
733 | if (stride == len) |
734 | { |
735 | len *= h; |
736 | h = 1; |
737 | } |
738 | while (h--) |
739 | { |
740 | memset(s, value, (unsigned int)len); |
741 | s += stride; |
742 | } |
743 | } |
744 | else |
745 | { |
746 | int k, x, y; |
747 | stride -= len; |
748 | for (y = 0; y < pix->h; y++) |
749 | { |
750 | for (x = 0; x < pix->w; x++) |
751 | { |
752 | for (k = 0; k < pix->n - 1; k++) |
753 | *s++ = value; |
754 | if (alpha) |
755 | *s++ = 255; |
756 | } |
757 | s += stride; |
758 | } |
759 | } |
760 | } |
761 | |
762 | /* |
763 | Fill pixmap with solid color. |
764 | */ |
765 | void |
766 | fz_fill_pixmap_with_color(fz_context *ctx, fz_pixmap *pix, fz_colorspace *colorspace, float *color, fz_color_params color_params) |
767 | { |
768 | float colorfv[FZ_MAX_COLORS]; |
769 | unsigned char colorbv[FZ_MAX_COLORS]; |
770 | int i, n, a, s, x, y, w, h; |
771 | |
772 | n = fz_colorspace_n(ctx, pix->colorspace); |
773 | a = pix->alpha; |
774 | s = pix->s; |
775 | fz_convert_color(ctx, colorspace, color, pix->colorspace, colorfv, NULL, color_params); |
776 | for (i = 0; i < n; ++i) |
777 | colorbv[i] = colorfv[i] * 255; |
778 | |
779 | w = pix->w; |
780 | h = pix->h; |
781 | for (y = 0; y < h; ++y) |
782 | { |
783 | unsigned char *p = pix->samples + y * pix->stride; |
784 | for (x = 0; x < w; ++x) |
785 | { |
786 | for (i = 0; i < n; ++i) |
787 | *p++ = colorbv[i]; |
788 | for (i = 0; i < s; ++i) |
789 | *p++ = 0; |
790 | if (a) |
791 | *p++ = 255; |
792 | } |
793 | } |
794 | } |
795 | |
796 | void |
797 | fz_copy_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_pixmap *src, fz_irect b, const fz_default_colorspaces *default_cs) |
798 | { |
799 | unsigned char *srcp; |
800 | unsigned char *destp; |
801 | int y, w, destspan, srcspan; |
802 | |
803 | b = fz_intersect_irect(b, fz_pixmap_bbox(ctx, dest)); |
804 | b = fz_intersect_irect(b, fz_pixmap_bbox(ctx, src)); |
805 | w = b.x1 - b.x0; |
806 | y = b.y1 - b.y0; |
807 | if (w <= 0 || y <= 0) |
808 | return; |
809 | |
810 | srcspan = src->stride; |
811 | srcp = src->samples + (unsigned int)(srcspan * (b.y0 - src->y) + src->n * (b.x0 - src->x)); |
812 | destspan = dest->stride; |
813 | destp = dest->samples + (unsigned int)(destspan * (b.y0 - dest->y) + dest->n * (b.x0 - dest->x)); |
814 | |
815 | if (src->n == dest->n) |
816 | { |
817 | w *= src->n; |
818 | do |
819 | { |
820 | memcpy(destp, srcp, w); |
821 | srcp += srcspan; |
822 | destp += destspan; |
823 | } |
824 | while (--y); |
825 | } |
826 | else |
827 | { |
828 | fz_pixmap fake_src = *src; |
829 | fake_src.x = b.x0; |
830 | fake_src.y = b.y0; |
831 | fake_src.w = w; |
832 | fake_src.h = y; |
833 | fake_src.samples = srcp; |
834 | fz_convert_pixmap_samples(ctx, dest, &fake_src, NULL, default_cs, fz_default_color_params, 0); |
835 | } |
836 | } |
837 | |
838 | /* |
839 | Clears a subrect of a pixmap with the given value. |
840 | |
841 | pix: The pixmap to clear. |
842 | |
843 | value: Values in the range 0 to 255 are valid. Each component |
844 | sample for each pixel in the pixmap will be set to this value, |
845 | while alpha will always be set to 255 (non-transparent). |
846 | |
847 | r: the rectangle. |
848 | */ |
849 | void |
850 | fz_clear_pixmap_rect_with_value(fz_context *ctx, fz_pixmap *dest, int value, fz_irect b) |
851 | { |
852 | unsigned char *destp; |
853 | int x, y, w, k, destspan; |
854 | |
855 | b = fz_intersect_irect(b, fz_pixmap_bbox(ctx, dest)); |
856 | w = b.x1 - b.x0; |
857 | y = b.y1 - b.y0; |
858 | if (w <= 0 || y <= 0) |
859 | return; |
860 | |
861 | destspan = dest->stride; |
862 | destp = dest->samples + (unsigned int)(destspan * (b.y0 - dest->y) + dest->n * (b.x0 - dest->x)); |
863 | |
864 | /* CMYK needs special handling (and potentially any other subtractive colorspaces) */ |
865 | if (fz_colorspace_n(ctx, dest->colorspace) == 4) |
866 | { |
867 | value = 255 - value; |
868 | do |
869 | { |
870 | unsigned char *s = destp; |
871 | for (x = 0; x < w; x++) |
872 | { |
873 | *s++ = 0; |
874 | *s++ = 0; |
875 | *s++ = 0; |
876 | *s++ = value; |
877 | *s++ = 255; |
878 | } |
879 | destp += destspan; |
880 | } |
881 | while (--y); |
882 | return; |
883 | } |
884 | |
885 | if (value == 255) |
886 | { |
887 | do |
888 | { |
889 | memset(destp, 255, (unsigned int)(w * dest->n)); |
890 | destp += destspan; |
891 | } |
892 | while (--y); |
893 | } |
894 | else |
895 | { |
896 | do |
897 | { |
898 | unsigned char *s = destp; |
899 | for (x = 0; x < w; x++) |
900 | { |
901 | for (k = 0; k < dest->n - 1; k++) |
902 | *s++ = value; |
903 | *s++ = 255; |
904 | } |
905 | destp += destspan; |
906 | } |
907 | while (--y); |
908 | } |
909 | } |
910 | |
911 | void |
912 | fz_premultiply_pixmap(fz_context *ctx, fz_pixmap *pix) |
913 | { |
914 | unsigned char *s = pix->samples; |
915 | unsigned char a; |
916 | int k, x, y; |
917 | int stride = pix->stride - pix->w * pix->n; |
918 | |
919 | if (!pix->alpha) |
920 | return; |
921 | |
922 | for (y = 0; y < pix->h; y++) |
923 | { |
924 | for (x = 0; x < pix->w; x++) |
925 | { |
926 | a = s[pix->n - 1]; |
927 | for (k = 0; k < pix->n - 1; k++) |
928 | s[k] = fz_mul255(s[k], a); |
929 | s += pix->n; |
930 | } |
931 | s += stride; |
932 | } |
933 | } |
934 | |
935 | fz_pixmap * |
936 | fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray) |
937 | { |
938 | fz_pixmap *alpha; |
939 | unsigned char *sp, *dp; |
940 | int w, h, sstride, dstride; |
941 | |
942 | assert(gray->n == 1); |
943 | |
944 | alpha = fz_new_pixmap_with_bbox(ctx, NULL, fz_pixmap_bbox(ctx, gray), 0, 1); |
945 | dp = alpha->samples; |
946 | dstride = alpha->stride; |
947 | sp = gray->samples; |
948 | sstride = gray->stride; |
949 | |
950 | h = gray->h; |
951 | w = gray->w; |
952 | while (h--) |
953 | { |
954 | memcpy(dp, sp, w); |
955 | sp += sstride; |
956 | dp += dstride; |
957 | } |
958 | |
959 | return alpha; |
960 | } |
961 | |
962 | /* |
963 | Tint all the pixels in an RGB, BGR, or Gray pixmap. |
964 | |
965 | black: Map black to this hexadecimal RGB color. |
966 | white: Map white to this hexadecimal RGB color. |
967 | */ |
968 | void |
969 | fz_tint_pixmap(fz_context *ctx, fz_pixmap *pix, int black, int white) |
970 | { |
971 | unsigned char *s = pix->samples; |
972 | int n = pix->n; |
973 | int x, y, save; |
974 | int rb = (black>>16)&255; |
975 | int gb = (black>>8)&255; |
976 | int bb = (black)&255; |
977 | int rw = (white>>16)&255; |
978 | int gw = (white>>8)&255; |
979 | int bw = (white)&255; |
980 | int rm = (rw - rb); |
981 | int gm = (gw - gb); |
982 | int bm = (bw - bb); |
983 | |
984 | switch (fz_colorspace_type(ctx, pix->colorspace)) |
985 | { |
986 | case FZ_COLORSPACE_GRAY: |
987 | gw = (rw + gw + bw) / 3; |
988 | gb = (rb + gb + bb) / 3; |
989 | gm = gw - gb; |
990 | for (y = 0; y < pix->h; y++) |
991 | { |
992 | for (x = 0; x < pix->w; x++) |
993 | { |
994 | *s = gb + fz_mul255(*s, gm); |
995 | s += n; |
996 | } |
997 | s += pix->stride - pix->w * n; |
998 | } |
999 | break; |
1000 | |
1001 | case FZ_COLORSPACE_BGR: |
1002 | save = rm; rm = bm; bm = save; |
1003 | save = rb; rb = bb; bb = save; |
1004 | /* fall through */ |
1005 | case FZ_COLORSPACE_RGB: |
1006 | for (y = 0; y < pix->h; y++) |
1007 | { |
1008 | for (x = 0; x < pix->w; x++) |
1009 | { |
1010 | s[0] = rb + fz_mul255(s[0], rm); |
1011 | s[1] = gb + fz_mul255(s[1], gm); |
1012 | s[2] = bb + fz_mul255(s[2], bm); |
1013 | s += n; |
1014 | } |
1015 | s += pix->stride - pix->w * n; |
1016 | } |
1017 | break; |
1018 | |
1019 | default: |
1020 | fz_throw(ctx, FZ_ERROR_GENERIC, "can only tint RGB, BGR and Gray pixmaps" ); |
1021 | break; |
1022 | } |
1023 | } |
1024 | |
1025 | /* Invert luminance in RGB pixmap, but keep the colors as is. */ |
1026 | static void invert_luminance_rgb(unsigned char *rgb) |
1027 | { |
1028 | int r, g, b, y, u, v, c, d, e; |
1029 | |
1030 | /* Convert to YUV */ |
1031 | r = rgb[0]; |
1032 | g = rgb[1]; |
1033 | b = rgb[2]; |
1034 | y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16; |
1035 | u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; |
1036 | v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; |
1037 | |
1038 | /* Invert luminance */ |
1039 | y = 255 - y; |
1040 | |
1041 | /* Convert to RGB */ |
1042 | c = y - 16; |
1043 | d = u - 128; |
1044 | e = v - 128; |
1045 | r = (298 * c + 409 * e + 128) >> 8; |
1046 | g = (298 * c - 100 * d - 208 * e + 128) >> 8; |
1047 | b = (298 * c + 516 * d + 128) >> 8; |
1048 | |
1049 | rgb[0] = r > 255 ? 255 : r < 0 ? 0 : r; |
1050 | rgb[1] = g > 255 ? 255 : g < 0 ? 0 : g; |
1051 | rgb[2] = b > 255 ? 255 : b < 0 ? 0 : b; |
1052 | } |
1053 | |
1054 | void |
1055 | fz_invert_pixmap_luminance(fz_context *ctx, fz_pixmap *pix) |
1056 | { |
1057 | unsigned char *s = pix->samples; |
1058 | int x, y, n = pix->n; |
1059 | |
1060 | if (pix->colorspace->type != FZ_COLORSPACE_RGB) |
1061 | fz_throw(ctx, FZ_ERROR_GENERIC, "can only invert luminance of RGB pixmaps" ); |
1062 | |
1063 | for (y = 0; y < pix->h; y++) |
1064 | { |
1065 | for (x = 0; x < pix->w; x++) |
1066 | { |
1067 | invert_luminance_rgb(s); |
1068 | s += n; |
1069 | } |
1070 | s += pix->stride - pix->w * n; |
1071 | } |
1072 | } |
1073 | |
1074 | /* |
1075 | Invert all the pixels in a pixmap. All components |
1076 | of all pixels are inverted (except alpha, which is unchanged). |
1077 | */ |
1078 | void |
1079 | fz_invert_pixmap(fz_context *ctx, fz_pixmap *pix) |
1080 | { |
1081 | unsigned char *s = pix->samples; |
1082 | int k, x, y; |
1083 | int n1 = pix->n - pix->alpha; |
1084 | int n = pix->n; |
1085 | |
1086 | for (y = 0; y < pix->h; y++) |
1087 | { |
1088 | for (x = 0; x < pix->w; x++) |
1089 | { |
1090 | for (k = 0; k < n1; k++) |
1091 | s[k] = 255 - s[k]; |
1092 | s += n; |
1093 | } |
1094 | s += pix->stride - pix->w * n; |
1095 | } |
1096 | } |
1097 | |
1098 | /* |
1099 | Invert all the pixels in a given rectangle of a |
1100 | pixmap. All components of all pixels in the rectangle are inverted |
1101 | (except alpha, which is unchanged). |
1102 | */ |
1103 | void fz_invert_pixmap_rect(fz_context *ctx, fz_pixmap *image, fz_irect rect) |
1104 | { |
1105 | unsigned char *p; |
1106 | int x, y, n; |
1107 | |
1108 | int x0 = fz_clampi(rect.x0 - image->x, 0, image->w); |
1109 | int x1 = fz_clampi(rect.x1 - image->x, 0, image->w); |
1110 | int y0 = fz_clampi(rect.y0 - image->y, 0, image->h); |
1111 | int y1 = fz_clampi(rect.y1 - image->y, 0, image->h); |
1112 | |
1113 | for (y = y0; y < y1; y++) |
1114 | { |
1115 | p = image->samples + (unsigned int)((y * image->stride) + (x0 * image->n)); |
1116 | for (x = x0; x < x1; x++) |
1117 | { |
1118 | for (n = image->n; n > 1; n--, p++) |
1119 | *p = 255 - *p; |
1120 | p++; |
1121 | } |
1122 | } |
1123 | } |
1124 | |
1125 | /* |
1126 | Apply gamma correction to a pixmap. All components |
1127 | of all pixels are modified (except alpha, which is unchanged). |
1128 | |
1129 | gamma: The gamma value to apply; 1.0 for no change. |
1130 | */ |
1131 | void |
1132 | fz_gamma_pixmap(fz_context *ctx, fz_pixmap *pix, float gamma) |
1133 | { |
1134 | unsigned char gamma_map[256]; |
1135 | unsigned char *s = pix->samples; |
1136 | int n1 = pix->n - pix->alpha; |
1137 | int n = pix->n; |
1138 | int k, x, y; |
1139 | |
1140 | for (k = 0; k < 256; k++) |
1141 | gamma_map[k] = pow(k / 255.0f, gamma) * 255; |
1142 | |
1143 | for (y = 0; y < pix->h; y++) |
1144 | { |
1145 | for (x = 0; x < pix->w; x++) |
1146 | { |
1147 | for (k = 0; k < n1; k++) |
1148 | s[k] = gamma_map[s[k]]; |
1149 | s += n; |
1150 | } |
1151 | s += pix->stride - pix->w * n; |
1152 | } |
1153 | } |
1154 | |
1155 | size_t |
1156 | fz_pixmap_size(fz_context *ctx, fz_pixmap * pix) |
1157 | { |
1158 | if (pix == NULL) |
1159 | return 0; |
1160 | return sizeof(*pix) + pix->n * pix->w * pix->h; |
1161 | } |
1162 | |
1163 | /* |
1164 | Convert an existing pixmap to a desired |
1165 | colorspace. Other properties of the pixmap, such as resolution |
1166 | and position are copied to the converted pixmap. |
1167 | |
1168 | pix: The pixmap to convert. |
1169 | |
1170 | default_cs: If NULL pix->colorspace is used. It is possible that the data |
1171 | may need to be interpreted as one of the color spaces in default_cs. |
1172 | |
1173 | cs_des: Desired colorspace, may be NULL to denote alpha-only. |
1174 | |
1175 | prf: Proofing color space through which we need to convert. |
1176 | |
1177 | color_params: Parameters that may be used in conversion (e.g. ri). |
1178 | |
1179 | keep_alpha: If 0 any alpha component is removed, otherwise |
1180 | alpha is kept if present in the pixmap. |
1181 | */ |
1182 | fz_pixmap * |
1183 | fz_convert_pixmap(fz_context *ctx, fz_pixmap *pix, fz_colorspace *ds, fz_colorspace *prf, fz_default_colorspaces *default_cs, fz_color_params color_params, int keep_alpha) |
1184 | { |
1185 | fz_pixmap *cvt; |
1186 | |
1187 | if (!ds && !keep_alpha) |
1188 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot both throw away and keep alpha" ); |
1189 | |
1190 | cvt = fz_new_pixmap(ctx, ds, pix->w, pix->h, pix->seps, keep_alpha && pix->alpha); |
1191 | |
1192 | cvt->xres = pix->xres; |
1193 | cvt->yres = pix->yres; |
1194 | cvt->x = pix->x; |
1195 | cvt->y = pix->y; |
1196 | if (pix->flags & FZ_PIXMAP_FLAG_INTERPOLATE) |
1197 | cvt->flags |= FZ_PIXMAP_FLAG_INTERPOLATE; |
1198 | else |
1199 | cvt->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE; |
1200 | |
1201 | fz_try(ctx) |
1202 | { |
1203 | fz_convert_pixmap_samples(ctx, pix, cvt, prf, default_cs, color_params, 1); |
1204 | } |
1205 | fz_catch(ctx) |
1206 | { |
1207 | fz_drop_pixmap(ctx, cvt); |
1208 | fz_rethrow(ctx); |
1209 | } |
1210 | |
1211 | return cvt; |
1212 | } |
1213 | |
1214 | fz_pixmap * |
1215 | fz_new_pixmap_from_8bpp_data(fz_context *ctx, int x, int y, int w, int h, unsigned char *sp, int span) |
1216 | { |
1217 | fz_pixmap *pixmap = fz_new_pixmap(ctx, NULL, w, h, NULL, 1); |
1218 | int stride = pixmap->stride; |
1219 | unsigned char *s = pixmap->samples; |
1220 | pixmap->x = x; |
1221 | pixmap->y = y; |
1222 | |
1223 | for (y = 0; y < h; y++) |
1224 | { |
1225 | memcpy(s, sp + y * span, w); |
1226 | s += stride; |
1227 | } |
1228 | |
1229 | return pixmap; |
1230 | } |
1231 | |
1232 | fz_pixmap * |
1233 | fz_new_pixmap_from_1bpp_data(fz_context *ctx, int x, int y, int w, int h, unsigned char *sp, int span) |
1234 | { |
1235 | fz_pixmap *pixmap = fz_new_pixmap(ctx, NULL, w, h, NULL, 1); |
1236 | int stride = pixmap->stride - pixmap->w; |
1237 | pixmap->x = x; |
1238 | pixmap->y = y; |
1239 | |
1240 | for (y = 0; y < h; y++) |
1241 | { |
1242 | unsigned char *out = pixmap->samples + y * w; |
1243 | unsigned char *in = sp + y * span; |
1244 | unsigned char bit = 0x80; |
1245 | int ww = w; |
1246 | while (ww--) |
1247 | { |
1248 | *out++ = (*in & bit) ? 255 : 0; |
1249 | bit >>= 1; |
1250 | if (bit == 0) |
1251 | bit = 0x80, in++; |
1252 | } |
1253 | out += stride; |
1254 | } |
1255 | |
1256 | return pixmap; |
1257 | } |
1258 | |
1259 | #ifdef ARCH_ARM |
1260 | static void |
1261 | fz_subsample_pixmap_ARM(unsigned char *ptr, int w, int h, int f, int factor, |
1262 | int n, int fwd, int back, int back2, int fwd2, |
1263 | int divX, int back4, int fwd4, int fwd3, |
1264 | int divY, int back5, int divXY) |
1265 | __attribute__((naked)); |
1266 | |
1267 | static void |
1268 | fz_subsample_pixmap_ARM(unsigned char *ptr, int w, int h, int f, int factor, |
1269 | int n, int fwd, int back, int back2, int fwd2, |
1270 | int divX, int back4, int fwd4, int fwd3, |
1271 | int divY, int back5, int divXY) |
1272 | { |
1273 | asm volatile( |
1274 | ENTER_ARM |
1275 | "stmfd r13!,{r1,r4-r11,r14} \n" |
1276 | "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" |
1277 | "@ r0 = src = ptr \n" |
1278 | "@ r1 = w \n" |
1279 | "@ r2 = h \n" |
1280 | "@ r3 = f \n" |
1281 | "mov r9, r0 @ r9 = dst = ptr \n" |
1282 | "ldr r6, [r13,#4*12] @ r6 = fwd \n" |
1283 | "ldr r7, [r13,#4*13] @ r7 = back \n" |
1284 | "subs r2, r2, r3 @ r2 = h -= f \n" |
1285 | "blt 11f @ Skip if less than a full row \n" |
1286 | "1: @ for (y = h; y > 0; y--) { \n" |
1287 | "ldr r1, [r13] @ r1 = w \n" |
1288 | "subs r1, r1, r3 @ r1 = w -= f \n" |
1289 | "blt 6f @ Skip if less than a full col \n" |
1290 | "ldr r4, [r13,#4*10] @ r4 = factor \n" |
1291 | "ldr r8, [r13,#4*14] @ r8 = back2 \n" |
1292 | "ldr r12,[r13,#4*15] @ r12= fwd2 \n" |
1293 | "2: @ for (x = w; x > 0; x--) { \n" |
1294 | "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" |
1295 | "3: @ \n" |
1296 | "mov r14,#0 @ r14= v = 0 \n" |
1297 | "sub r5, r5, r3, LSL #8 @ for (xx = f; xx > 0; x--) { \n" |
1298 | "4: @ \n" |
1299 | "add r5, r5, r3, LSL #16 @ for (yy = f; yy > 0; y--) { \n" |
1300 | "5: @ \n" |
1301 | "ldrb r11,[r0], r6 @ r11= *src src += fwd \n" |
1302 | "subs r5, r5, #1<<16 @ xx-- \n" |
1303 | "add r14,r14,r11 @ v += r11 \n" |
1304 | "bgt 5b @ } \n" |
1305 | "sub r0, r0, r7 @ src -= back \n" |
1306 | "adds r5, r5, #1<<8 @ yy-- \n" |
1307 | "blt 4b @ } \n" |
1308 | "mov r14,r14,LSR r4 @ r14 = v >>= factor \n" |
1309 | "strb r14,[r9], #1 @ *d++ = r14 \n" |
1310 | "sub r0, r0, r8 @ s -= back2 \n" |
1311 | "subs r5, r5, #1 @ n-- \n" |
1312 | "bgt 3b @ } \n" |
1313 | "add r0, r0, r12 @ s += fwd2 \n" |
1314 | "subs r1, r1, r3 @ x -= f \n" |
1315 | "bge 2b @ } \n" |
1316 | "6: @ Less than a full column left \n" |
1317 | "adds r1, r1, r3 @ x += f \n" |
1318 | "beq 11f @ if (x == 0) next row \n" |
1319 | "@ r0 = src \n" |
1320 | "@ r1 = x \n" |
1321 | "@ r2 = y \n" |
1322 | "@ r3 = f \n" |
1323 | "@ r4 = factor \n" |
1324 | "@ r6 = fwd \n" |
1325 | "@ r7 = back \n" |
1326 | "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" |
1327 | "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" |
1328 | "ldr r4, [r13,#4*16] @ r4 = divX \n" |
1329 | "ldr r8, [r13,#4*17] @ r8 = back4 \n" |
1330 | "ldr r12,[r13,#4*18] @ r12= fwd4 \n" |
1331 | "8: @ \n" |
1332 | "mov r14,#0 @ r14= v = 0 \n" |
1333 | "sub r5, r5, r1, LSL #8 @ for (xx = x; xx > 0; x--) { \n" |
1334 | "9: @ \n" |
1335 | "add r5, r5, r3, LSL #16 @ for (yy = f; yy > 0; y--) { \n" |
1336 | "10: @ \n" |
1337 | "ldrb r11,[r0], r6 @ r11= *src src += fwd \n" |
1338 | "subs r5, r5, #1<<16 @ xx-- \n" |
1339 | "add r14,r14,r11 @ v += r11 \n" |
1340 | "bgt 10b @ } \n" |
1341 | "sub r0, r0, r7 @ src -= back \n" |
1342 | "adds r5, r5, #1<<8 @ yy-- \n" |
1343 | "blt 9b @ } \n" |
1344 | "mul r14,r4, r14 @ r14= v *= divX \n" |
1345 | "mov r14,r14,LSR #16 @ r14= v >>= 16 \n" |
1346 | "strb r14,[r9], #1 @ *d++ = r14 \n" |
1347 | "sub r0, r0, r8 @ s -= back4 \n" |
1348 | "subs r5, r5, #1 @ n-- \n" |
1349 | "bgt 8b @ } \n" |
1350 | "add r0, r0, r12 @ s += fwd4 \n" |
1351 | "11: @ \n" |
1352 | "ldr r14,[r13,#4*19] @ r14 = fwd3 \n" |
1353 | "subs r2, r2, r3 @ h -= f \n" |
1354 | "add r0, r0, r14 @ s += fwd3 \n" |
1355 | "bge 1b @ } \n" |
1356 | "adds r2, r2, r3 @ h += f \n" |
1357 | "beq 21f @ if no stray row, end \n" |
1358 | "@ So doing one last (partial) row \n" |
1359 | "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" |
1360 | "@ r0 = src = ptr \n" |
1361 | "@ r1 = w \n" |
1362 | "@ r2 = h \n" |
1363 | "@ r3 = f \n" |
1364 | "@ r4 = factor \n" |
1365 | "@ r5 = n \n" |
1366 | "@ r6 = fwd \n" |
1367 | "12: @ for (y = h; y > 0; y--) { \n" |
1368 | "ldr r1, [r13] @ r1 = w \n" |
1369 | "ldr r7, [r13,#4*21] @ r7 = back5 \n" |
1370 | "ldr r8, [r13,#4*14] @ r8 = back2 \n" |
1371 | "subs r1, r1, r3 @ r1 = w -= f \n" |
1372 | "blt 17f @ Skip if less than a full col \n" |
1373 | "ldr r4, [r13,#4*20] @ r4 = divY \n" |
1374 | "ldr r12,[r13,#4*15] @ r12= fwd2 \n" |
1375 | "13: @ for (x = w; x > 0; x--) { \n" |
1376 | "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" |
1377 | "14: @ \n" |
1378 | "mov r14,#0 @ r14= v = 0 \n" |
1379 | "sub r5, r5, r3, LSL #8 @ for (xx = f; xx > 0; x--) { \n" |
1380 | "15: @ \n" |
1381 | "add r5, r5, r2, LSL #16 @ for (yy = y; yy > 0; y--) { \n" |
1382 | "16: @ \n" |
1383 | "ldrb r11,[r0], r6 @ r11= *src src += fwd \n" |
1384 | "subs r5, r5, #1<<16 @ xx-- \n" |
1385 | "add r14,r14,r11 @ v += r11 \n" |
1386 | "bgt 16b @ } \n" |
1387 | "sub r0, r0, r7 @ src -= back5 \n" |
1388 | "adds r5, r5, #1<<8 @ yy-- \n" |
1389 | "blt 15b @ } \n" |
1390 | "mul r14,r4, r14 @ r14 = x *= divY \n" |
1391 | "mov r14,r14,LSR #16 @ r14 = v >>= 16 \n" |
1392 | "strb r14,[r9], #1 @ *d++ = r14 \n" |
1393 | "sub r0, r0, r8 @ s -= back2 \n" |
1394 | "subs r5, r5, #1 @ n-- \n" |
1395 | "bgt 14b @ } \n" |
1396 | "add r0, r0, r12 @ s += fwd2 \n" |
1397 | "subs r1, r1, r3 @ x -= f \n" |
1398 | "bge 13b @ } \n" |
1399 | "17: @ Less than a full column left \n" |
1400 | "adds r1, r1, r3 @ x += f \n" |
1401 | "beq 21f @ if (x == 0) end \n" |
1402 | "@ r0 = src \n" |
1403 | "@ r1 = x \n" |
1404 | "@ r2 = y \n" |
1405 | "@ r3 = f \n" |
1406 | "@ r4 = factor \n" |
1407 | "@ r6 = fwd \n" |
1408 | "@ r7 = back5 \n" |
1409 | "@ r8 = back2 \n" |
1410 | "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" |
1411 | "ldr r4, [r13,#4*22] @ r4 = divXY \n" |
1412 | "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" |
1413 | "ldr r8, [r13,#4*17] @ r8 = back4 \n" |
1414 | "18: @ \n" |
1415 | "mov r14,#0 @ r14= v = 0 \n" |
1416 | "sub r5, r5, r1, LSL #8 @ for (xx = x; xx > 0; x--) { \n" |
1417 | "19: @ \n" |
1418 | "add r5, r5, r2, LSL #16 @ for (yy = y; yy > 0; y--) { \n" |
1419 | "20: @ \n" |
1420 | "ldrb r11,[r0],r6 @ r11= *src src += fwd \n" |
1421 | "subs r5, r5, #1<<16 @ xx-- \n" |
1422 | "add r14,r14,r11 @ v += r11 \n" |
1423 | "bgt 20b @ } \n" |
1424 | "sub r0, r0, r7 @ src -= back5 \n" |
1425 | "adds r5, r5, #1<<8 @ yy-- \n" |
1426 | "blt 19b @ } \n" |
1427 | "mul r14,r4, r14 @ r14= v *= divX \n" |
1428 | "mov r14,r14,LSR #16 @ r14= v >>= 16 \n" |
1429 | "strb r14,[r9], #1 @ *d++ = r14 \n" |
1430 | "sub r0, r0, r8 @ s -= back4 \n" |
1431 | "subs r5, r5, #1 @ n-- \n" |
1432 | "bgt 18b @ } \n" |
1433 | "21: @ \n" |
1434 | "ldmfd r13!,{r1,r4-r11,PC} @ pop, return to thumb \n" |
1435 | ENTER_THUMB |
1436 | ); |
1437 | } |
1438 | |
1439 | #endif |
1440 | |
1441 | void |
1442 | fz_subsample_pixmap(fz_context *ctx, fz_pixmap *tile, int factor) |
1443 | { |
1444 | int dst_w, dst_h, w, h, fwd, fwd2, fwd3, back, back2, n, f; |
1445 | unsigned char *s, *d; |
1446 | #ifndef ARCH_ARM |
1447 | int x, y, xx, yy, nn; |
1448 | #endif |
1449 | |
1450 | if (!tile) |
1451 | return; |
1452 | |
1453 | assert(tile->stride >= tile->w * tile->n); |
1454 | |
1455 | s = d = tile->samples; |
1456 | f = 1<<factor; |
1457 | w = tile->w; |
1458 | h = tile->h; |
1459 | n = tile->n; |
1460 | dst_w = (w + f-1)>>factor; |
1461 | dst_h = (h + f-1)>>factor; |
1462 | fwd = tile->stride; |
1463 | back = f*fwd-n; |
1464 | back2 = f*n-1; |
1465 | fwd2 = (f-1)*n; |
1466 | fwd3 = (f-1)*fwd + tile->stride - w * n; |
1467 | factor *= 2; |
1468 | #ifdef ARCH_ARM |
1469 | { |
1470 | int strayX = w%f; |
1471 | int divX = (strayX ? 65536/(strayX*f) : 0); |
1472 | int fwd4 = (strayX-1) * n; |
1473 | int back4 = strayX*n-1; |
1474 | int strayY = h%f; |
1475 | int divY = (strayY ? 65536/(strayY*f) : 0); |
1476 | int back5 = fwd * strayY - n; |
1477 | int divXY = (strayY*strayX ? 65536/(strayX*strayY) : 0); |
1478 | fz_subsample_pixmap_ARM(s, w, h, f, factor, n, fwd, back, |
1479 | back2, fwd2, divX, back4, fwd4, fwd3, |
1480 | divY, back5, divXY); |
1481 | } |
1482 | #else |
1483 | for (y = h - f; y >= 0; y -= f) |
1484 | { |
1485 | for (x = w - f; x >= 0; x -= f) |
1486 | { |
1487 | for (nn = n; nn > 0; nn--) |
1488 | { |
1489 | int v = 0; |
1490 | for (xx = f; xx > 0; xx--) |
1491 | { |
1492 | for (yy = f; yy > 0; yy--) |
1493 | { |
1494 | v += *s; |
1495 | s += fwd; |
1496 | } |
1497 | s -= back; |
1498 | } |
1499 | *d++ = v >> factor; |
1500 | s -= back2; |
1501 | } |
1502 | s += fwd2; |
1503 | } |
1504 | /* Do any strays */ |
1505 | x += f; |
1506 | if (x > 0) |
1507 | { |
1508 | int div = x * f; |
1509 | int fwd4 = (x-1) * n; |
1510 | int back4 = x*n-1; |
1511 | for (nn = n; nn > 0; nn--) |
1512 | { |
1513 | int v = 0; |
1514 | for (xx = x; xx > 0; xx--) |
1515 | { |
1516 | for (yy = f; yy > 0; yy--) |
1517 | { |
1518 | v += *s; |
1519 | s += fwd; |
1520 | } |
1521 | s -= back; |
1522 | } |
1523 | *d++ = v / div; |
1524 | s -= back4; |
1525 | } |
1526 | s += fwd4; |
1527 | } |
1528 | s += fwd3; |
1529 | } |
1530 | /* Do any stray line */ |
1531 | y += f; |
1532 | if (y > 0) |
1533 | { |
1534 | int div = y * f; |
1535 | int back5 = fwd * y - n; |
1536 | for (x = w - f; x >= 0; x -= f) |
1537 | { |
1538 | for (nn = n; nn > 0; nn--) |
1539 | { |
1540 | int v = 0; |
1541 | for (xx = f; xx > 0; xx--) |
1542 | { |
1543 | for (yy = y; yy > 0; yy--) |
1544 | { |
1545 | v += *s; |
1546 | s += fwd; |
1547 | } |
1548 | s -= back5; |
1549 | } |
1550 | *d++ = v / div; |
1551 | s -= back2; |
1552 | } |
1553 | s += fwd2; |
1554 | } |
1555 | /* Do any stray at the end of the stray line */ |
1556 | x += f; |
1557 | if (x > 0) |
1558 | { |
1559 | int back4 = x * n - 1; |
1560 | div = x * y; |
1561 | for (nn = n; nn > 0; nn--) |
1562 | { |
1563 | int v = 0; |
1564 | for (xx = x; xx > 0; xx--) |
1565 | { |
1566 | for (yy = y; yy > 0; yy--) |
1567 | { |
1568 | v += *s; |
1569 | s += fwd; |
1570 | } |
1571 | s -= back5; |
1572 | } |
1573 | *d++ = v / div; |
1574 | s -= back4; |
1575 | } |
1576 | } |
1577 | } |
1578 | #endif |
1579 | tile->w = dst_w; |
1580 | tile->h = dst_h; |
1581 | tile->stride = dst_w * n; |
1582 | if (dst_h > INT_MAX / (dst_w * n)) |
1583 | fz_throw(ctx, FZ_ERROR_MEMORY, "pixmap too large" ); |
1584 | tile->samples = fz_realloc(ctx, tile->samples, dst_h * dst_w * n); |
1585 | } |
1586 | |
1587 | /* |
1588 | Set the pixels per inch resolution of the pixmap. |
1589 | */ |
1590 | void |
1591 | fz_set_pixmap_resolution(fz_context *ctx, fz_pixmap *pix, int xres, int yres) |
1592 | { |
1593 | pix->xres = xres; |
1594 | pix->yres = yres; |
1595 | } |
1596 | |
1597 | /* |
1598 | Return the md5 digest for a pixmap |
1599 | */ |
1600 | void |
1601 | fz_md5_pixmap(fz_context *ctx, fz_pixmap *pix, unsigned char digest[16]) |
1602 | { |
1603 | fz_md5 md5; |
1604 | |
1605 | fz_md5_init(&md5); |
1606 | if (pix) |
1607 | { |
1608 | unsigned char *s = pix->samples; |
1609 | int h = pix->h; |
1610 | int ss = pix->stride; |
1611 | int len = pix->w * pix->n; |
1612 | while (h--) |
1613 | { |
1614 | fz_md5_update(&md5, s, len); |
1615 | s += ss; |
1616 | } |
1617 | } |
1618 | fz_md5_final(&md5, digest); |
1619 | } |
1620 | |
1621 | #ifdef HAVE_VALGRIND |
1622 | int fz_valgrind_pixmap(const fz_pixmap *pix) |
1623 | { |
1624 | int w, h, n, total; |
1625 | int ww, hh, nn; |
1626 | int stride; |
1627 | const unsigned char *p = pix->samples; |
1628 | |
1629 | if (pix == NULL) |
1630 | return 0; |
1631 | |
1632 | total = 0; |
1633 | ww = pix->w; |
1634 | hh = pix->h; |
1635 | nn = pix->n; |
1636 | stride = pix->stride - ww*nn; |
1637 | for (h = 0; h < hh; h++) |
1638 | { |
1639 | for (w = 0; w < ww; w++) |
1640 | for (n = 0; n < nn; n++) |
1641 | if (*p++) total ++; |
1642 | p += stride; |
1643 | } |
1644 | return total; |
1645 | } |
1646 | #endif /* HAVE_VALGRIND */ |
1647 | |
1648 | /* |
1649 | * Convert pixmap from indexed to base colorspace. |
1650 | */ |
1651 | fz_pixmap * |
1652 | fz_convert_indexed_pixmap_to_base(fz_context *ctx, const fz_pixmap *src) |
1653 | { |
1654 | fz_pixmap *dst; |
1655 | fz_colorspace *base; |
1656 | const unsigned char *s; |
1657 | unsigned char *d; |
1658 | int y, x, k, n, high; |
1659 | unsigned char *lookup; |
1660 | int s_line_inc, d_line_inc; |
1661 | |
1662 | if (src->colorspace->type != FZ_COLORSPACE_INDEXED) |
1663 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot convert non-indexed pixmap" ); |
1664 | if (src->n != 1 + src->alpha) |
1665 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot convert indexed pixmap mis-matching components" ); |
1666 | |
1667 | base = src->colorspace->u.indexed.base; |
1668 | high = src->colorspace->u.indexed.high; |
1669 | lookup = src->colorspace->u.indexed.lookup; |
1670 | n = base->n; |
1671 | |
1672 | dst = fz_new_pixmap_with_bbox(ctx, base, fz_pixmap_bbox(ctx, src), src->seps, src->alpha); |
1673 | s = src->samples; |
1674 | d = dst->samples; |
1675 | s_line_inc = src->stride - src->w * src->n; |
1676 | d_line_inc = dst->stride - dst->w * dst->n; |
1677 | |
1678 | if (src->alpha) |
1679 | { |
1680 | for (y = 0; y < src->h; y++) |
1681 | { |
1682 | for (x = 0; x < src->w; x++) |
1683 | { |
1684 | int v = *s++; |
1685 | int a = *s++; |
1686 | int aa = a + (a>>7); |
1687 | v = fz_mini(v, high); |
1688 | for (k = 0; k < n; k++) |
1689 | *d++ = (aa * lookup[v * n + k] + 128)>>8; |
1690 | *d++ = a; |
1691 | } |
1692 | s += s_line_inc; |
1693 | d += d_line_inc; |
1694 | } |
1695 | } |
1696 | else |
1697 | { |
1698 | for (y = 0; y < src->h; y++) |
1699 | { |
1700 | for (x = 0; x < src->w; x++) |
1701 | { |
1702 | int v = *s++; |
1703 | v = fz_mini(v, high); |
1704 | for (k = 0; k < n; k++) |
1705 | *d++ = lookup[v * n + k]; |
1706 | } |
1707 | s += s_line_inc; |
1708 | d += d_line_inc; |
1709 | } |
1710 | } |
1711 | |
1712 | if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE) |
1713 | dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE; |
1714 | else |
1715 | dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE; |
1716 | |
1717 | return dst; |
1718 | } |
1719 | |
1720 | /* |
1721 | * Convert pixmap from DeviceN/Separation to base colorspace. |
1722 | */ |
1723 | fz_pixmap * |
1724 | fz_convert_separation_pixmap_to_base(fz_context *ctx, const fz_pixmap *src) |
1725 | { |
1726 | fz_pixmap *dst; |
1727 | fz_colorspace *ss, *base; |
1728 | const unsigned char *s; |
1729 | unsigned char *d; |
1730 | int y, x, k, sn, bn, a; |
1731 | float src_v[FZ_MAX_COLORS]; |
1732 | float base_v[FZ_MAX_COLORS]; |
1733 | int s_line_inc, d_line_inc; |
1734 | |
1735 | ss = src->colorspace; |
1736 | |
1737 | if (ss->type != FZ_COLORSPACE_SEPARATION) |
1738 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot expand non-separation pixmap" ); |
1739 | if (src->n != ss->n + src->alpha) |
1740 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot expand separation pixmap mis-matching alpha channel" ); |
1741 | |
1742 | base = ss->u.separation.base; |
1743 | dst = fz_new_pixmap_with_bbox(ctx, base, fz_pixmap_bbox(ctx, src), src->seps, src->alpha); |
1744 | fz_clear_pixmap(ctx, dst); |
1745 | fz_try(ctx) |
1746 | { |
1747 | s = src->samples; |
1748 | d = dst->samples; |
1749 | s_line_inc = src->stride - src->w * src->n; |
1750 | d_line_inc = dst->stride - dst->w * dst->n; |
1751 | sn = ss->n; |
1752 | bn = base->n; |
1753 | |
1754 | if (src->alpha) |
1755 | { |
1756 | for (y = 0; y < src->h; y++) |
1757 | { |
1758 | for (x = 0; x < src->w; x++) |
1759 | { |
1760 | for (k = 0; k < sn; ++k) |
1761 | src_v[k] = *s++ / 255.0f; |
1762 | a = *s++; |
1763 | ss->u.separation.eval(ctx, ss->u.separation.tint, src_v, sn, base_v, bn); |
1764 | for (k = 0; k < bn; ++k) |
1765 | *d++ = base_v[k] * 255.0f; |
1766 | *d++ = a; |
1767 | } |
1768 | s += s_line_inc; |
1769 | d += d_line_inc; |
1770 | } |
1771 | } |
1772 | else |
1773 | { |
1774 | for (y = 0; y < src->h; y++) |
1775 | { |
1776 | for (x = 0; x < src->w; x++) |
1777 | { |
1778 | for (k = 0; k < sn; ++k) |
1779 | src_v[k] = *s++ / 255.0f; |
1780 | ss->u.separation.eval(ctx, ss->u.separation.tint, src_v, sn, base_v, bn); |
1781 | for (k = 0; k < bn; ++k) |
1782 | *d++ = base_v[k] * 255.0f; |
1783 | } |
1784 | s += s_line_inc; |
1785 | d += d_line_inc; |
1786 | } |
1787 | } |
1788 | |
1789 | if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE) |
1790 | dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE; |
1791 | else |
1792 | dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE; |
1793 | } |
1794 | fz_catch(ctx) |
1795 | { |
1796 | fz_drop_pixmap(ctx, dst); |
1797 | fz_rethrow(ctx); |
1798 | } |
1799 | |
1800 | return dst; |
1801 | } |
1802 | |