1#include "mupdf/fitz.h"
2
3#include <assert.h>
4#include <limits.h>
5#include <string.h>
6#include <math.h>
7
8fz_pixmap *
9fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix)
10{
11 return fz_keep_storable(ctx, &pix->storable);
12}
13
14void
15fz_drop_pixmap(fz_context *ctx, fz_pixmap *pix)
16{
17 fz_drop_storable(ctx, &pix->storable);
18}
19
20void
21fz_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*/
56fz_pixmap *
57fz_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*/
137fz_pixmap *
138fz_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*/
168fz_pixmap *
169fz_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*/
201fz_pixmap *
202fz_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*/
227fz_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
262fz_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*/
272fz_irect
273fz_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
283fz_irect
284fz_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*/
299fz_colorspace *
300fz_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*/
310int
311fz_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*/
319int
320fz_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*/
328int
329fz_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*/
337int
338fz_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*/
348int
349fz_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*/
359int
360fz_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*/
370int
371fz_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*/
381int
382fz_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*/
390int
391fz_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*/
401unsigned char *
402fz_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
415static void
416clear_cmyka_bitmap_ARM(uint32_t *samples, int c, int value)
417__attribute__((naked));
418
419static void
420clear_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
468static void
469clear_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*/
638void
639fz_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. */
707void
708fz_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*/
765void
766fz_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
796void
797fz_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*/
849void
850fz_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
911void
912fz_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
935fz_pixmap *
936fz_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*/
968void
969fz_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. */
1026static 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
1054void
1055fz_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*/
1078void
1079fz_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*/
1103void 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*/
1131void
1132fz_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
1155size_t
1156fz_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*/
1182fz_pixmap *
1183fz_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
1214fz_pixmap *
1215fz_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
1232fz_pixmap *
1233fz_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
1260static void
1261fz_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
1267static void
1268fz_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
1441void
1442fz_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*/
1590void
1591fz_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*/
1600void
1601fz_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
1622int 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 */
1651fz_pixmap *
1652fz_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 */
1723fz_pixmap *
1724fz_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