eBookReaderSwitch/mupdf/source/fitz/pixmap.c

1830 lines
40 KiB
C

#include "mupdf/fitz.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <math.h>
fz_pixmap *
fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix)
{
return fz_keep_storable(ctx, &pix->storable);
}
void
fz_drop_pixmap(fz_context *ctx, fz_pixmap *pix)
{
fz_drop_storable(ctx, &pix->storable);
}
void
fz_drop_pixmap_imp(fz_context *ctx, fz_storable *pix_)
{
fz_pixmap *pix = (fz_pixmap *)pix_;
fz_drop_colorspace(ctx, pix->colorspace);
fz_drop_separations(ctx, pix->seps);
if (pix->flags & FZ_PIXMAP_FLAG_FREE_SAMPLES)
fz_free(ctx, pix->samples);
fz_drop_pixmap(ctx, pix->underlying);
fz_free(ctx, pix);
}
/*
Create a new pixmap, with its origin at
(0,0) using the supplied data block.
cs: The colorspace to use for the pixmap, or NULL for an alpha
plane/mask.
w: The width of the pixmap (in pixels)
h: The height of the pixmap (in pixels)
seps: Details of separations.
alpha: 0 for no alpha, 1 for alpha.
stride: The byte offset from the pixel data in a row to the pixel
data in the next row.
samples: The data block to keep the samples in.
Returns a pointer to the new pixmap. Throws exception on failure to
allocate.
*/
fz_pixmap *
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)
{
fz_pixmap *pix;
int s = fz_count_active_separations(ctx, seps);
int n;
if (w < 0 || h < 0)
fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal dimensions for pixmap %d %d", w, h);
n = alpha + s + fz_colorspace_n(ctx, colorspace);
if (stride < n*w && stride > -n*w)
fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal stride for pixmap (n=%d w=%d, stride=%d)", n, w, stride);
if (samples == NULL && stride < n*w)
fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal -ve stride for pixmap without data");
if (n > FZ_MAX_COLORS)
fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal number of colorants");
pix = fz_malloc_struct(ctx, fz_pixmap);
FZ_INIT_STORABLE(pix, 1, fz_drop_pixmap_imp);
pix->x = 0;
pix->y = 0;
pix->w = w;
pix->h = h;
pix->alpha = alpha = !!alpha;
pix->flags = FZ_PIXMAP_FLAG_INTERPOLATE;
pix->xres = 96;
pix->yres = 96;
pix->colorspace = NULL;
pix->n = n;
pix->s = s;
pix->seps = fz_keep_separations(ctx, seps);
pix->stride = stride;
if (colorspace)
{
pix->colorspace = fz_keep_colorspace(ctx, colorspace);
}
else
{
assert(alpha || s);
}
pix->samples = samples;
if (!samples)
{
fz_try(ctx)
{
if (pix->stride - 1 > INT_MAX / pix->n)
fz_throw(ctx, FZ_ERROR_GENERIC, "overly wide image");
pix->samples = Memento_label(fz_malloc(ctx, pix->h * pix->stride), "pixmap_data");
}
fz_catch(ctx)
{
fz_drop_colorspace(ctx, pix->colorspace);
fz_free(ctx, pix);
fz_rethrow(ctx);
}
pix->flags |= FZ_PIXMAP_FLAG_FREE_SAMPLES;
}
return pix;
}
/*
Create a new pixmap, with its origin at (0,0)
cs: The colorspace to use for the pixmap, or NULL for an alpha
plane/mask.
w: The width of the pixmap (in pixels)
h: The height of the pixmap (in pixels)
seps: Details of separations.
alpha: 0 for no alpha, 1 for alpha.
Returns a pointer to the new pixmap. Throws exception on failure to
allocate.
*/
fz_pixmap *
fz_new_pixmap(fz_context *ctx, fz_colorspace *colorspace, int w, int h, fz_separations *seps, int alpha)
{
int stride;
int s = fz_count_active_separations(ctx, seps);
if (!colorspace && s == 0) alpha = 1;
stride = (fz_colorspace_n(ctx, colorspace) + s + alpha) * w;
return fz_new_pixmap_with_data(ctx, colorspace, w, h, seps, alpha, stride, NULL);
}
/*
Create a pixmap of a given size,
location and pixel format.
The bounding box specifies the size of the created pixmap and
where it will be located. The colorspace determines the number
of components per pixel. Alpha is always present. Pixmaps are
reference counted, so drop references using fz_drop_pixmap.
colorspace: Colorspace format used for the created pixmap. The
pixmap will keep a reference to the colorspace.
bbox: Bounding box specifying location/size of created pixmap.
seps: Details of separations.
alpha: 0 for no alpha, 1 for alpha.
Returns a pointer to the new pixmap. Throws exception on failure to
allocate.
*/
fz_pixmap *
fz_new_pixmap_with_bbox(fz_context *ctx, fz_colorspace *colorspace, fz_irect bbox, fz_separations *seps, int alpha)
{
fz_pixmap *pixmap;
pixmap = fz_new_pixmap(ctx, colorspace, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, seps, alpha);
pixmap->x = bbox.x0;
pixmap->y = bbox.y0;
return pixmap;
}
/*
Create a pixmap of a given size,
location and pixel format, using the supplied data block.
The bounding box specifies the size of the created pixmap and
where it will be located. The colorspace determines the number
of components per pixel. Alpha is always present. Pixmaps are
reference counted, so drop references using fz_drop_pixmap.
colorspace: Colorspace format used for the created pixmap. The
pixmap will keep a reference to the colorspace.
rect: Bounding box specifying location/size of created pixmap.
seps: Details of separations.
alpha: Number of alpha planes (0 or 1).
samples: The data block to keep the samples in.
Returns a pointer to the new pixmap. Throws exception on failure to
allocate.
*/
fz_pixmap *
fz_new_pixmap_with_bbox_and_data(fz_context *ctx, fz_colorspace *colorspace, fz_irect bbox, fz_separations *seps, int alpha, unsigned char *samples)
{
int w = bbox.x1 - bbox.x0;
int stride;
int s = fz_count_active_separations(ctx, seps);
fz_pixmap *pixmap;
if (!colorspace && s == 0) alpha = 1;
stride = (fz_colorspace_n(ctx, colorspace) + s + alpha) * w;
pixmap = fz_new_pixmap_with_data(ctx, colorspace, w, bbox.y1 - bbox.y0, seps, alpha, stride, samples);
pixmap->x = bbox.x0;
pixmap->y = bbox.y0;
return pixmap;
}
/*
Create a new pixmap that represents
a subarea of the specified pixmap. A reference is taken to his
pixmap that will be dropped on destruction.
The supplied rectangle must be wholly contained within the original
pixmap.
Returns a pointer to the new pixmap. Throws exception on failure to
allocate.
*/
fz_pixmap *fz_new_pixmap_from_pixmap(fz_context *ctx, fz_pixmap *pixmap, const fz_irect *rect)
{
fz_irect local_rect;
fz_pixmap *subpix;
if (!pixmap)
return NULL;
if (rect == NULL)
{
rect = &local_rect;
local_rect.x0 = pixmap->x;
local_rect.y0 = pixmap->y;
local_rect.x1 = pixmap->x + pixmap->w;
local_rect.y1 = pixmap->y + pixmap->h;
}
else if (rect->x0 < pixmap->x || rect->y0 < pixmap->y || rect->x1 > pixmap->x + pixmap->w || rect->y1 > pixmap->y + pixmap->h)
fz_throw(ctx, FZ_ERROR_GENERIC, "Pixmap region is not a subarea");
subpix = fz_malloc_struct(ctx, fz_pixmap);
*subpix = *pixmap;
subpix->storable.refs = 1;
subpix->x = rect->x0;
subpix->y = rect->y0;
subpix->w = rect->x1 - rect->x0;
subpix->h = rect->y1 - rect->y0;
subpix->samples += (rect->x0 - pixmap->x) + (rect->y0 - pixmap->y) * pixmap->stride;
subpix->underlying = fz_keep_pixmap(ctx, pixmap);
subpix->colorspace = fz_keep_colorspace(ctx, pixmap->colorspace);
subpix->seps = fz_keep_separations(ctx, pixmap->seps);
subpix->flags &= ~FZ_PIXMAP_FLAG_FREE_SAMPLES;
return subpix;
}
fz_pixmap *fz_clone_pixmap(fz_context *ctx, fz_pixmap *old)
{
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);
memcpy(pix->samples, old->samples, pix->stride * pix->h);
return pix;
}
/*
Return the bounding box for a pixmap.
*/
fz_irect
fz_pixmap_bbox(fz_context *ctx, const fz_pixmap *pix)
{
fz_irect bbox;
bbox.x0 = pix->x;
bbox.y0 = pix->y;
bbox.x1 = pix->x + pix->w;
bbox.y1 = pix->y + pix->h;
return bbox;
}
fz_irect
fz_pixmap_bbox_no_ctx(const fz_pixmap *pix)
{
fz_irect bbox;
bbox.x0 = pix->x;
bbox.y0 = pix->y;
bbox.x1 = pix->x + pix->w;
bbox.y1 = pix->y + pix->h;
return bbox;
}
/*
Return the colorspace of a pixmap
Returns colorspace.
*/
fz_colorspace *
fz_pixmap_colorspace(fz_context *ctx, fz_pixmap *pix)
{
if (!pix)
return NULL;
return pix->colorspace;
}
/*
Return the x value of the pixmap in pixels.
*/
int
fz_pixmap_x(fz_context *ctx, fz_pixmap *pix)
{
return pix->x;
}
/*
Return the y value of the pixmap in pixels.
*/
int
fz_pixmap_y(fz_context *ctx, fz_pixmap *pix)
{
return pix->y;
}
/*
Return the width of the pixmap in pixels.
*/
int
fz_pixmap_width(fz_context *ctx, fz_pixmap *pix)
{
return pix->w;
}
/*
Return the height of the pixmap in pixels.
*/
int
fz_pixmap_height(fz_context *ctx, fz_pixmap *pix)
{
return pix->h;
}
/*
Return the number of components in a pixmap.
Returns the number of components (including spots and alpha).
*/
int
fz_pixmap_components(fz_context *ctx, fz_pixmap *pix)
{
return pix->n;
}
/*
Return the number of colorants in a pixmap.
Returns the number of colorants (components, less any spots and alpha).
*/
int
fz_pixmap_colorants(fz_context *ctx, fz_pixmap *pix)
{
return pix->n - pix->alpha - pix->s;
}
/*
Return the number of spots in a pixmap.
Returns the number of spots (components, less colorants and alpha). Does not throw exceptions.
*/
int
fz_pixmap_spots(fz_context *ctx, fz_pixmap *pix)
{
return pix->s;
}
/*
Return the number of alpha planes in a pixmap.
Returns the number of alphas. Does not throw exceptions.
*/
int
fz_pixmap_alpha(fz_context *ctx, fz_pixmap *pix)
{
return pix->alpha;
}
/*
Return the number of bytes in a row in the pixmap.
*/
int
fz_pixmap_stride(fz_context *ctx, fz_pixmap *pix)
{
return pix->stride;
}
/*
Returns a pointer to the pixel data of a pixmap.
Returns the pointer.
*/
unsigned char *
fz_pixmap_samples(fz_context *ctx, fz_pixmap *pix)
{
if (!pix)
return NULL;
return pix->samples;
}
/*
The slowest routine in most CMYK rendering profiles.
We therefore spend some effort to improve it. Rather than
writing bytes, we write uint32_t's.
*/
#ifdef ARCH_ARM
static void
clear_cmyka_bitmap_ARM(uint32_t *samples, int c, int value)
__attribute__((naked));
static void
clear_cmyka_bitmap_ARM(uint32_t *samples, int c, int value)
{
asm volatile(
ENTER_ARM
"stmfd r13!,{r4-r6,r14} \n"
"@ r0 = samples \n"
"@ r1 = c \n"
"@ r2 = value \n"
"mov r3, #255 \n"
"mov r12,#0 @ r12= 0 \n"
"subs r1, r1, #3 \n"
"ble 2f \n"
"str r12,[r13,#-20]! \n"
"str r12,[r13,#4] \n"
"str r12,[r13,#8] \n"
"str r12,[r13,#12] \n"
"str r12,[r13,#16] \n"
"strb r2, [r13,#3] \n"
"strb r3, [r13,#4] \n"
"strb r2, [r13,#8] \n"
"strb r3, [r13,#9] \n"
"strb r2, [r13,#13] \n"
"strb r3, [r13,#14] \n"
"strb r2, [r13,#18] \n"
"strb r3, [r13,#19] \n"
"ldmfd r13!,{r4,r5,r6,r12,r14} \n"
"1: \n"
"stmia r0!,{r4,r5,r6,r12,r14} \n"
"subs r1, r1, #4 \n"
"bgt 1b \n"
"2: \n"
"adds r1, r1, #3 \n"
"ble 4f \n"
"3: \n"
"strb r12,[r0], #1 \n"
"strb r12,[r0], #1 \n"
"strb r12,[r0], #1 \n"
"strb r2, [r0], #1 \n"
"strb r3, [r0], #1 \n"
"subs r1, r1, #1 \n"
"bgt 3b \n"
"4: \n"
"ldmfd r13!,{r4-r6,PC} \n"
ENTER_THUMB
);
}
#endif
static void
clear_cmyk_bitmap(unsigned char *samples, int w, int h, int spots, int stride, int value, int alpha)
{
uint32_t *s = (uint32_t *)(void *)samples;
uint8_t *t;
if (w < 0 || h < 0)
return;
if (spots)
{
int x, i;
spots += 4;
stride -= w * (spots + alpha);
for (; h > 0; h--)
{
for (x = w; x > 0; x--)
{
for (i = spots; i > 0; i--)
*samples++ = value;
if (alpha)
*samples++ = 255;
}
samples += stride;
}
return;
}
if (alpha)
{
int c = w;
stride -= w*5;
if (stride == 0)
{
#ifdef ARCH_ARM
clear_cmyka_bitmap_ARM(s, c, alpha);
return;
#else
/* We can do it all fast (except for maybe a few stragglers) */
union
{
uint8_t bytes[20];
uint32_t words[5];
} d;
c *= h;
h = 1;
d.words[0] = 0;
d.words[1] = 0;
d.words[2] = 0;
d.words[3] = 0;
d.words[4] = 0;
d.bytes[3] = value;
d.bytes[4] = 255;
d.bytes[8] = value;
d.bytes[9] = 255;
d.bytes[13] = value;
d.bytes[14] = 255;
d.bytes[18] = value;
d.bytes[19] = 255;
c -= 3;
{
const uint32_t a0 = d.words[0];
const uint32_t a1 = d.words[1];
const uint32_t a2 = d.words[2];
const uint32_t a3 = d.words[3];
const uint32_t a4 = d.words[4];
while (c > 0)
{
*s++ = a0;
*s++ = a1;
*s++ = a2;
*s++ = a3;
*s++ = a4;
c -= 4;
}
}
c += 3;
#endif
}
t = (unsigned char *)s;
w = c;
while (h--)
{
c = w;
while (c > 0)
{
*t++ = 0;
*t++ = 0;
*t++ = 0;
*t++ = value;
*t++ = 255;
c--;
}
t += stride;
}
}
else
{
stride -= w*4;
if ((stride & 3) == 0)
{
size_t W = w;
if (stride == 0)
{
W *= h;
h = 1;
}
W *= 4;
if (value == 0)
{
while (h--)
{
memset(s, 0, W);
s += (stride>>2);
}
}
else
{
/* We can do it all fast */
union
{
uint8_t bytes[4];
uint32_t word;
} d;
d.word = 0;
d.bytes[3] = value;
{
const uint32_t a0 = d.word;
while (h--)
{
size_t WW = W >> 2;
while (WW--)
{
*s++ = a0;
}
s += (stride>>2);
}
}
}
}
else
{
t = (unsigned char *)s;
while (h--)
{
int c = w;
while (c > 0)
{
*t++ = 0;
*t++ = 0;
*t++ = 0;
*t++ = value;
c--;
}
t += stride;
}
}
}
}
/*
Sets all components (including alpha) of
all pixels in a pixmap to 0.
pix: The pixmap to clear.
*/
void
fz_clear_pixmap(fz_context *ctx, fz_pixmap *pix)
{
int stride = pix->w * pix->n;
int h = pix->h;
unsigned char *s = pix->samples;
if (stride == pix->stride)
{
stride *= h;
h = 1;
}
if (pix->alpha || fz_colorspace_is_subtractive(ctx, pix->colorspace))
{
while (h--)
{
memset(s, 0, (unsigned int)stride);
s += pix->stride;
}
}
else if (pix->s == 0)
{
while (h--)
{
memset(s, 0xff, (unsigned int)stride);
s += pix->stride;
}
}
else
{
/* Horrible, slow case: additive with spots */
int w = stride/pix->n;
int spots = pix->s;
int colorants = pix->n - spots; /* We know there is no alpha */
while (h--)
{
int w2 = w;
while (w2--)
{
int i = colorants;
do
{
*s++ = 0xff;
i--;
}
while (i != 0);
i = spots;
do
{
*s++ = 0;
i--;
}
while (i != 0);
}
}
}
}
/*
Clears a pixmap with the given value.
pix: The pixmap to clear.
value: Values in the range 0 to 255 are valid. Each component
sample for each pixel in the pixmap will be set to this value,
while alpha will always be set to 255 (non-transparent).
*/
/* This function is horrible, and should be removed from the
* API and replaced with a less magic one. */
void
fz_clear_pixmap_with_value(fz_context *ctx, fz_pixmap *pix, int value)
{
unsigned char *s;
int w, h, n, stride, len;
int alpha = pix->alpha;
w = pix->w;
h = pix->h;
if (w < 0 || h < 0)
return;
/* CMYK needs special handling (and potentially any other subtractive colorspaces) */
if (fz_colorspace_n(ctx, pix->colorspace) == 4)
{
clear_cmyk_bitmap(pix->samples, w, h, pix->s, pix->stride, 255-value, pix->alpha);
return;
}
n = pix->n;
stride = pix->stride;
len = w * n;
s = pix->samples;
if (value == 255 || !alpha)
{
if (stride == len)
{
len *= h;
h = 1;
}
while (h--)
{
memset(s, value, (unsigned int)len);
s += stride;
}
}
else
{
int k, x, y;
stride -= len;
for (y = 0; y < pix->h; y++)
{
for (x = 0; x < pix->w; x++)
{
for (k = 0; k < pix->n - 1; k++)
*s++ = value;
if (alpha)
*s++ = 255;
}
s += stride;
}
}
}
/*
Fill pixmap with solid color.
*/
void
fz_fill_pixmap_with_color(fz_context *ctx, fz_pixmap *pix, fz_colorspace *colorspace, float *color, fz_color_params color_params)
{
float colorfv[FZ_MAX_COLORS];
unsigned char colorbv[FZ_MAX_COLORS];
int i, n, a, s, x, y, w, h;
n = fz_colorspace_n(ctx, pix->colorspace);
a = pix->alpha;
s = pix->s;
fz_convert_color(ctx, colorspace, color, pix->colorspace, colorfv, NULL, color_params);
for (i = 0; i < n; ++i)
colorbv[i] = colorfv[i] * 255;
w = pix->w;
h = pix->h;
for (y = 0; y < h; ++y)
{
unsigned char *p = pix->samples + y * pix->stride;
for (x = 0; x < w; ++x)
{
for (i = 0; i < n; ++i)
*p++ = colorbv[i];
for (i = 0; i < s; ++i)
*p++ = 0;
if (a)
*p++ = 255;
}
}
}
void
fz_copy_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_pixmap *src, fz_irect b, const fz_default_colorspaces *default_cs)
{
unsigned char *srcp;
unsigned char *destp;
int y, w, destspan, srcspan;
b = fz_intersect_irect(b, fz_pixmap_bbox(ctx, dest));
b = fz_intersect_irect(b, fz_pixmap_bbox(ctx, src));
w = b.x1 - b.x0;
y = b.y1 - b.y0;
if (w <= 0 || y <= 0)
return;
srcspan = src->stride;
srcp = src->samples + (unsigned int)(srcspan * (b.y0 - src->y) + src->n * (b.x0 - src->x));
destspan = dest->stride;
destp = dest->samples + (unsigned int)(destspan * (b.y0 - dest->y) + dest->n * (b.x0 - dest->x));
if (src->n == dest->n)
{
w *= src->n;
do
{
memcpy(destp, srcp, w);
srcp += srcspan;
destp += destspan;
}
while (--y);
}
else
{
fz_pixmap fake_src = *src;
fake_src.x = b.x0;
fake_src.y = b.y0;
fake_src.w = w;
fake_src.h = y;
fake_src.samples = srcp;
fz_convert_pixmap_samples(ctx, dest, &fake_src, NULL, default_cs, fz_default_color_params, 0);
}
}
/*
Clears a subrect of a pixmap with the given value.
pix: The pixmap to clear.
value: Values in the range 0 to 255 are valid. Each component
sample for each pixel in the pixmap will be set to this value,
while alpha will always be set to 255 (non-transparent).
r: the rectangle.
*/
void
fz_clear_pixmap_rect_with_value(fz_context *ctx, fz_pixmap *dest, int value, fz_irect b)
{
unsigned char *destp;
int x, y, w, k, destspan;
b = fz_intersect_irect(b, fz_pixmap_bbox(ctx, dest));
w = b.x1 - b.x0;
y = b.y1 - b.y0;
if (w <= 0 || y <= 0)
return;
destspan = dest->stride;
destp = dest->samples + (unsigned int)(destspan * (b.y0 - dest->y) + dest->n * (b.x0 - dest->x));
/* CMYK needs special handling (and potentially any other subtractive colorspaces) */
if (fz_colorspace_n(ctx, dest->colorspace) == 4)
{
value = 255 - value;
do
{
unsigned char *s = destp;
for (x = 0; x < w; x++)
{
*s++ = 0;
*s++ = 0;
*s++ = 0;
*s++ = value;
*s++ = 255;
}
destp += destspan;
}
while (--y);
return;
}
if (value == 255)
{
do
{
memset(destp, 255, (unsigned int)(w * dest->n));
destp += destspan;
}
while (--y);
}
else
{
do
{
unsigned char *s = destp;
for (x = 0; x < w; x++)
{
for (k = 0; k < dest->n - 1; k++)
*s++ = value;
*s++ = 255;
}
destp += destspan;
}
while (--y);
}
}
void
fz_premultiply_pixmap(fz_context *ctx, fz_pixmap *pix)
{
unsigned char *s = pix->samples;
unsigned char a;
int k, x, y;
int stride = pix->stride - pix->w * pix->n;
if (!pix->alpha)
return;
for (y = 0; y < pix->h; y++)
{
for (x = 0; x < pix->w; x++)
{
a = s[pix->n - 1];
for (k = 0; k < pix->n - 1; k++)
s[k] = fz_mul255(s[k], a);
s += pix->n;
}
s += stride;
}
}
fz_pixmap *
fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray)
{
fz_pixmap *alpha;
unsigned char *sp, *dp;
int w, h, sstride, dstride;
assert(gray->n == 1);
alpha = fz_new_pixmap_with_bbox(ctx, NULL, fz_pixmap_bbox(ctx, gray), 0, 1);
dp = alpha->samples;
dstride = alpha->stride;
sp = gray->samples;
sstride = gray->stride;
h = gray->h;
w = gray->w;
while (h--)
{
memcpy(dp, sp, w);
sp += sstride;
dp += dstride;
}
return alpha;
}
/*
Tint all the pixels in an RGB, BGR, or Gray pixmap.
black: Map black to this hexadecimal RGB color.
white: Map white to this hexadecimal RGB color.
*/
void
fz_tint_pixmap(fz_context *ctx, fz_pixmap *pix, int black, int white)
{
unsigned char *s = pix->samples;
int n = pix->n;
int x, y, save;
int rb = (black>>16)&255;
int gb = (black>>8)&255;
int bb = (black)&255;
int rw = (white>>16)&255;
int gw = (white>>8)&255;
int bw = (white)&255;
int rm = (rw - rb);
int gm = (gw - gb);
int bm = (bw - bb);
switch (fz_colorspace_type(ctx, pix->colorspace))
{
case FZ_COLORSPACE_GRAY:
gw = (rw + gw + bw) / 3;
gb = (rb + gb + bb) / 3;
gm = gw - gb;
for (y = 0; y < pix->h; y++)
{
for (x = 0; x < pix->w; x++)
{
*s = gb + fz_mul255(*s, gm);
s += n;
}
s += pix->stride - pix->w * n;
}
break;
case FZ_COLORSPACE_BGR:
save = rm; rm = bm; bm = save;
save = rb; rb = bb; bb = save;
/* fall through */
case FZ_COLORSPACE_RGB:
for (y = 0; y < pix->h; y++)
{
for (x = 0; x < pix->w; x++)
{
s[0] = rb + fz_mul255(s[0], rm);
s[1] = gb + fz_mul255(s[1], gm);
s[2] = bb + fz_mul255(s[2], bm);
s += n;
}
s += pix->stride - pix->w * n;
}
break;
default:
fz_throw(ctx, FZ_ERROR_GENERIC, "can only tint RGB, BGR and Gray pixmaps");
break;
}
}
/* Invert luminance in RGB/BGR pixmap, but keep the colors as is. */
static inline void invert_luminance(int type, unsigned char *s)
{
int r, g, b, y, u, v, c, d, e;
/* Convert to YUV */
if (type == FZ_COLORSPACE_RGB)
{
r = s[0];
g = s[1];
b = s[2];
}
else
{
r = s[2];
g = s[1];
b = s[0];
}
y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
/* Invert luminance */
y = 255 - y;
/* Convert to RGB */
c = y - 16;
d = u - 128;
e = v - 128;
r = (298 * c + 409 * e + 128) >> 8;
g = (298 * c - 100 * d - 208 * e + 128) >> 8;
b = (298 * c + 516 * d + 128) >> 8;
if (type == FZ_COLORSPACE_RGB)
{
s[0] = r > 255 ? 255 : r < 0 ? 0 : r;
s[1] = g > 255 ? 255 : g < 0 ? 0 : g;
s[2] = b > 255 ? 255 : b < 0 ? 0 : b;
}
else
{
s[2] = r > 255 ? 255 : r < 0 ? 0 : r;
s[1] = g > 255 ? 255 : g < 0 ? 0 : g;
s[0] = b > 255 ? 255 : b < 0 ? 0 : b;
}
}
void
fz_invert_pixmap_luminance(fz_context *ctx, fz_pixmap *pix)
{
unsigned char *s = pix->samples;
int x, y, n = pix->n;
int type = pix->colorspace ? pix->colorspace->type : FZ_COLORSPACE_NONE;
if (type == FZ_COLORSPACE_GRAY)
{
fz_invert_pixmap(ctx, pix);
}
else if (type == FZ_COLORSPACE_RGB || type == FZ_COLORSPACE_BGR)
{
for (y = 0; y < pix->h; y++)
{
for (x = 0; x < pix->w; x++)
{
invert_luminance(type, s);
s += n;
}
s += pix->stride - pix->w * n;
}
}
else
{
fz_throw(ctx, FZ_ERROR_GENERIC, "can only invert luminance of Gray and RGB pixmaps");
}
}
/*
Invert all the pixels in a pixmap. All components
of all pixels are inverted (except alpha, which is unchanged).
*/
void
fz_invert_pixmap(fz_context *ctx, fz_pixmap *pix)
{
unsigned char *s = pix->samples;
int k, x, y;
int n1 = pix->n - pix->alpha;
int n = pix->n;
for (y = 0; y < pix->h; y++)
{
for (x = 0; x < pix->w; x++)
{
for (k = 0; k < n1; k++)
s[k] = 255 - s[k];
s += n;
}
s += pix->stride - pix->w * n;
}
}
/*
Invert all the pixels in a given rectangle of a
pixmap. All components of all pixels in the rectangle are inverted
(except alpha, which is unchanged).
*/
void fz_invert_pixmap_rect(fz_context *ctx, fz_pixmap *image, fz_irect rect)
{
unsigned char *p;
int x, y, n;
int x0 = fz_clampi(rect.x0 - image->x, 0, image->w);
int x1 = fz_clampi(rect.x1 - image->x, 0, image->w);
int y0 = fz_clampi(rect.y0 - image->y, 0, image->h);
int y1 = fz_clampi(rect.y1 - image->y, 0, image->h);
for (y = y0; y < y1; y++)
{
p = image->samples + (unsigned int)((y * image->stride) + (x0 * image->n));
for (x = x0; x < x1; x++)
{
for (n = image->n; n > 1; n--, p++)
*p = 255 - *p;
p++;
}
}
}
/*
Apply gamma correction to a pixmap. All components
of all pixels are modified (except alpha, which is unchanged).
gamma: The gamma value to apply; 1.0 for no change.
*/
void
fz_gamma_pixmap(fz_context *ctx, fz_pixmap *pix, float gamma)
{
unsigned char gamma_map[256];
unsigned char *s = pix->samples;
int n1 = pix->n - pix->alpha;
int n = pix->n;
int k, x, y;
for (k = 0; k < 256; k++)
gamma_map[k] = pow(k / 255.0f, gamma) * 255;
for (y = 0; y < pix->h; y++)
{
for (x = 0; x < pix->w; x++)
{
for (k = 0; k < n1; k++)
s[k] = gamma_map[s[k]];
s += n;
}
s += pix->stride - pix->w * n;
}
}
size_t
fz_pixmap_size(fz_context *ctx, fz_pixmap * pix)
{
if (pix == NULL)
return 0;
return sizeof(*pix) + pix->n * pix->w * pix->h;
}
/*
Convert an existing pixmap to a desired
colorspace. Other properties of the pixmap, such as resolution
and position are copied to the converted pixmap.
pix: The pixmap to convert.
default_cs: If NULL pix->colorspace is used. It is possible that the data
may need to be interpreted as one of the color spaces in default_cs.
cs_des: Desired colorspace, may be NULL to denote alpha-only.
prf: Proofing color space through which we need to convert.
color_params: Parameters that may be used in conversion (e.g. ri).
keep_alpha: If 0 any alpha component is removed, otherwise
alpha is kept if present in the pixmap.
*/
fz_pixmap *
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)
{
fz_pixmap *cvt;
if (!ds && !keep_alpha)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot both throw away and keep alpha");
cvt = fz_new_pixmap(ctx, ds, pix->w, pix->h, pix->seps, keep_alpha && pix->alpha);
cvt->xres = pix->xres;
cvt->yres = pix->yres;
cvt->x = pix->x;
cvt->y = pix->y;
if (pix->flags & FZ_PIXMAP_FLAG_INTERPOLATE)
cvt->flags |= FZ_PIXMAP_FLAG_INTERPOLATE;
else
cvt->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE;
fz_try(ctx)
{
fz_convert_pixmap_samples(ctx, pix, cvt, prf, default_cs, color_params, 1);
}
fz_catch(ctx)
{
fz_drop_pixmap(ctx, cvt);
fz_rethrow(ctx);
}
return cvt;
}
fz_pixmap *
fz_new_pixmap_from_8bpp_data(fz_context *ctx, int x, int y, int w, int h, unsigned char *sp, int span)
{
fz_pixmap *pixmap = fz_new_pixmap(ctx, NULL, w, h, NULL, 1);
int stride = pixmap->stride;
unsigned char *s = pixmap->samples;
pixmap->x = x;
pixmap->y = y;
for (y = 0; y < h; y++)
{
memcpy(s, sp + y * span, w);
s += stride;
}
return pixmap;
}
fz_pixmap *
fz_new_pixmap_from_1bpp_data(fz_context *ctx, int x, int y, int w, int h, unsigned char *sp, int span)
{
fz_pixmap *pixmap = fz_new_pixmap(ctx, NULL, w, h, NULL, 1);
int stride = pixmap->stride - pixmap->w;
pixmap->x = x;
pixmap->y = y;
for (y = 0; y < h; y++)
{
unsigned char *out = pixmap->samples + y * w;
unsigned char *in = sp + y * span;
unsigned char bit = 0x80;
int ww = w;
while (ww--)
{
*out++ = (*in & bit) ? 255 : 0;
bit >>= 1;
if (bit == 0)
bit = 0x80, in++;
}
out += stride;
}
return pixmap;
}
#ifdef ARCH_ARM
static void
fz_subsample_pixmap_ARM(unsigned char *ptr, int w, int h, int f, int factor,
int n, int fwd, int back, int back2, int fwd2,
int divX, int back4, int fwd4, int fwd3,
int divY, int back5, int divXY)
__attribute__((naked));
static void
fz_subsample_pixmap_ARM(unsigned char *ptr, int w, int h, int f, int factor,
int n, int fwd, int back, int back2, int fwd2,
int divX, int back4, int fwd4, int fwd3,
int divY, int back5, int divXY)
{
asm volatile(
ENTER_ARM
"stmfd r13!,{r1,r4-r11,r14} \n"
"@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n"
"@ r0 = src = ptr \n"
"@ r1 = w \n"
"@ r2 = h \n"
"@ r3 = f \n"
"mov r9, r0 @ r9 = dst = ptr \n"
"ldr r6, [r13,#4*12] @ r6 = fwd \n"
"ldr r7, [r13,#4*13] @ r7 = back \n"
"subs r2, r2, r3 @ r2 = h -= f \n"
"blt 11f @ Skip if less than a full row \n"
"1: @ for (y = h; y > 0; y--) { \n"
"ldr r1, [r13] @ r1 = w \n"
"subs r1, r1, r3 @ r1 = w -= f \n"
"blt 6f @ Skip if less than a full col \n"
"ldr r4, [r13,#4*10] @ r4 = factor \n"
"ldr r8, [r13,#4*14] @ r8 = back2 \n"
"ldr r12,[r13,#4*15] @ r12= fwd2 \n"
"2: @ for (x = w; x > 0; x--) { \n"
"ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n"
"3: @ \n"
"mov r14,#0 @ r14= v = 0 \n"
"sub r5, r5, r3, LSL #8 @ for (xx = f; xx > 0; x--) { \n"
"4: @ \n"
"add r5, r5, r3, LSL #16 @ for (yy = f; yy > 0; y--) { \n"
"5: @ \n"
"ldrb r11,[r0], r6 @ r11= *src src += fwd \n"
"subs r5, r5, #1<<16 @ xx-- \n"
"add r14,r14,r11 @ v += r11 \n"
"bgt 5b @ } \n"
"sub r0, r0, r7 @ src -= back \n"
"adds r5, r5, #1<<8 @ yy-- \n"
"blt 4b @ } \n"
"mov r14,r14,LSR r4 @ r14 = v >>= factor \n"
"strb r14,[r9], #1 @ *d++ = r14 \n"
"sub r0, r0, r8 @ s -= back2 \n"
"subs r5, r5, #1 @ n-- \n"
"bgt 3b @ } \n"
"add r0, r0, r12 @ s += fwd2 \n"
"subs r1, r1, r3 @ x -= f \n"
"bge 2b @ } \n"
"6: @ Less than a full column left \n"
"adds r1, r1, r3 @ x += f \n"
"beq 11f @ if (x == 0) next row \n"
"@ r0 = src \n"
"@ r1 = x \n"
"@ r2 = y \n"
"@ r3 = f \n"
"@ r4 = factor \n"
"@ r6 = fwd \n"
"@ r7 = back \n"
"@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n"
"ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n"
"ldr r4, [r13,#4*16] @ r4 = divX \n"
"ldr r8, [r13,#4*17] @ r8 = back4 \n"
"ldr r12,[r13,#4*18] @ r12= fwd4 \n"
"8: @ \n"
"mov r14,#0 @ r14= v = 0 \n"
"sub r5, r5, r1, LSL #8 @ for (xx = x; xx > 0; x--) { \n"
"9: @ \n"
"add r5, r5, r3, LSL #16 @ for (yy = f; yy > 0; y--) { \n"
"10: @ \n"
"ldrb r11,[r0], r6 @ r11= *src src += fwd \n"
"subs r5, r5, #1<<16 @ xx-- \n"
"add r14,r14,r11 @ v += r11 \n"
"bgt 10b @ } \n"
"sub r0, r0, r7 @ src -= back \n"
"adds r5, r5, #1<<8 @ yy-- \n"
"blt 9b @ } \n"
"mul r14,r4, r14 @ r14= v *= divX \n"
"mov r14,r14,LSR #16 @ r14= v >>= 16 \n"
"strb r14,[r9], #1 @ *d++ = r14 \n"
"sub r0, r0, r8 @ s -= back4 \n"
"subs r5, r5, #1 @ n-- \n"
"bgt 8b @ } \n"
"add r0, r0, r12 @ s += fwd4 \n"
"11: @ \n"
"ldr r14,[r13,#4*19] @ r14 = fwd3 \n"
"subs r2, r2, r3 @ h -= f \n"
"add r0, r0, r14 @ s += fwd3 \n"
"bge 1b @ } \n"
"adds r2, r2, r3 @ h += f \n"
"beq 21f @ if no stray row, end \n"
"@ So doing one last (partial) row \n"
"@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n"
"@ r0 = src = ptr \n"
"@ r1 = w \n"
"@ r2 = h \n"
"@ r3 = f \n"
"@ r4 = factor \n"
"@ r5 = n \n"
"@ r6 = fwd \n"
"12: @ for (y = h; y > 0; y--) { \n"
"ldr r1, [r13] @ r1 = w \n"
"ldr r7, [r13,#4*21] @ r7 = back5 \n"
"ldr r8, [r13,#4*14] @ r8 = back2 \n"
"subs r1, r1, r3 @ r1 = w -= f \n"
"blt 17f @ Skip if less than a full col \n"
"ldr r4, [r13,#4*20] @ r4 = divY \n"
"ldr r12,[r13,#4*15] @ r12= fwd2 \n"
"13: @ for (x = w; x > 0; x--) { \n"
"ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n"
"14: @ \n"
"mov r14,#0 @ r14= v = 0 \n"
"sub r5, r5, r3, LSL #8 @ for (xx = f; xx > 0; x--) { \n"
"15: @ \n"
"add r5, r5, r2, LSL #16 @ for (yy = y; yy > 0; y--) { \n"
"16: @ \n"
"ldrb r11,[r0], r6 @ r11= *src src += fwd \n"
"subs r5, r5, #1<<16 @ xx-- \n"
"add r14,r14,r11 @ v += r11 \n"
"bgt 16b @ } \n"
"sub r0, r0, r7 @ src -= back5 \n"
"adds r5, r5, #1<<8 @ yy-- \n"
"blt 15b @ } \n"
"mul r14,r4, r14 @ r14 = x *= divY \n"
"mov r14,r14,LSR #16 @ r14 = v >>= 16 \n"
"strb r14,[r9], #1 @ *d++ = r14 \n"
"sub r0, r0, r8 @ s -= back2 \n"
"subs r5, r5, #1 @ n-- \n"
"bgt 14b @ } \n"
"add r0, r0, r12 @ s += fwd2 \n"
"subs r1, r1, r3 @ x -= f \n"
"bge 13b @ } \n"
"17: @ Less than a full column left \n"
"adds r1, r1, r3 @ x += f \n"
"beq 21f @ if (x == 0) end \n"
"@ r0 = src \n"
"@ r1 = x \n"
"@ r2 = y \n"
"@ r3 = f \n"
"@ r4 = factor \n"
"@ r6 = fwd \n"
"@ r7 = back5 \n"
"@ r8 = back2 \n"
"@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n"
"ldr r4, [r13,#4*22] @ r4 = divXY \n"
"ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n"
"ldr r8, [r13,#4*17] @ r8 = back4 \n"
"18: @ \n"
"mov r14,#0 @ r14= v = 0 \n"
"sub r5, r5, r1, LSL #8 @ for (xx = x; xx > 0; x--) { \n"
"19: @ \n"
"add r5, r5, r2, LSL #16 @ for (yy = y; yy > 0; y--) { \n"
"20: @ \n"
"ldrb r11,[r0],r6 @ r11= *src src += fwd \n"
"subs r5, r5, #1<<16 @ xx-- \n"
"add r14,r14,r11 @ v += r11 \n"
"bgt 20b @ } \n"
"sub r0, r0, r7 @ src -= back5 \n"
"adds r5, r5, #1<<8 @ yy-- \n"
"blt 19b @ } \n"
"mul r14,r4, r14 @ r14= v *= divX \n"
"mov r14,r14,LSR #16 @ r14= v >>= 16 \n"
"strb r14,[r9], #1 @ *d++ = r14 \n"
"sub r0, r0, r8 @ s -= back4 \n"
"subs r5, r5, #1 @ n-- \n"
"bgt 18b @ } \n"
"21: @ \n"
"ldmfd r13!,{r1,r4-r11,PC} @ pop, return to thumb \n"
ENTER_THUMB
);
}
#endif
void
fz_subsample_pixmap(fz_context *ctx, fz_pixmap *tile, int factor)
{
int dst_w, dst_h, w, h, fwd, fwd2, fwd3, back, back2, n, f;
unsigned char *s, *d;
#ifndef ARCH_ARM
int x, y, xx, yy, nn;
#endif
if (!tile)
return;
assert(tile->stride >= tile->w * tile->n);
s = d = tile->samples;
f = 1<<factor;
w = tile->w;
h = tile->h;
n = tile->n;
dst_w = (w + f-1)>>factor;
dst_h = (h + f-1)>>factor;
fwd = tile->stride;
back = f*fwd-n;
back2 = f*n-1;
fwd2 = (f-1)*n;
fwd3 = (f-1)*fwd + tile->stride - w * n;
factor *= 2;
#ifdef ARCH_ARM
{
int strayX = w%f;
int divX = (strayX ? 65536/(strayX*f) : 0);
int fwd4 = (strayX-1) * n;
int back4 = strayX*n-1;
int strayY = h%f;
int divY = (strayY ? 65536/(strayY*f) : 0);
int back5 = fwd * strayY - n;
int divXY = (strayY*strayX ? 65536/(strayX*strayY) : 0);
fz_subsample_pixmap_ARM(s, w, h, f, factor, n, fwd, back,
back2, fwd2, divX, back4, fwd4, fwd3,
divY, back5, divXY);
}
#else
for (y = h - f; y >= 0; y -= f)
{
for (x = w - f; x >= 0; x -= f)
{
for (nn = n; nn > 0; nn--)
{
int v = 0;
for (xx = f; xx > 0; xx--)
{
for (yy = f; yy > 0; yy--)
{
v += *s;
s += fwd;
}
s -= back;
}
*d++ = v >> factor;
s -= back2;
}
s += fwd2;
}
/* Do any strays */
x += f;
if (x > 0)
{
int div = x * f;
int fwd4 = (x-1) * n;
int back4 = x*n-1;
for (nn = n; nn > 0; nn--)
{
int v = 0;
for (xx = x; xx > 0; xx--)
{
for (yy = f; yy > 0; yy--)
{
v += *s;
s += fwd;
}
s -= back;
}
*d++ = v / div;
s -= back4;
}
s += fwd4;
}
s += fwd3;
}
/* Do any stray line */
y += f;
if (y > 0)
{
int div = y * f;
int back5 = fwd * y - n;
for (x = w - f; x >= 0; x -= f)
{
for (nn = n; nn > 0; nn--)
{
int v = 0;
for (xx = f; xx > 0; xx--)
{
for (yy = y; yy > 0; yy--)
{
v += *s;
s += fwd;
}
s -= back5;
}
*d++ = v / div;
s -= back2;
}
s += fwd2;
}
/* Do any stray at the end of the stray line */
x += f;
if (x > 0)
{
int back4 = x * n - 1;
div = x * y;
for (nn = n; nn > 0; nn--)
{
int v = 0;
for (xx = x; xx > 0; xx--)
{
for (yy = y; yy > 0; yy--)
{
v += *s;
s += fwd;
}
s -= back5;
}
*d++ = v / div;
s -= back4;
}
}
}
#endif
tile->w = dst_w;
tile->h = dst_h;
tile->stride = dst_w * n;
if (dst_h > INT_MAX / (dst_w * n))
fz_throw(ctx, FZ_ERROR_MEMORY, "pixmap too large");
tile->samples = fz_realloc(ctx, tile->samples, dst_h * dst_w * n);
}
/*
Set the pixels per inch resolution of the pixmap.
*/
void
fz_set_pixmap_resolution(fz_context *ctx, fz_pixmap *pix, int xres, int yres)
{
pix->xres = xres;
pix->yres = yres;
}
/*
Return the md5 digest for a pixmap
*/
void
fz_md5_pixmap(fz_context *ctx, fz_pixmap *pix, unsigned char digest[16])
{
fz_md5 md5;
fz_md5_init(&md5);
if (pix)
{
unsigned char *s = pix->samples;
int h = pix->h;
int ss = pix->stride;
int len = pix->w * pix->n;
while (h--)
{
fz_md5_update(&md5, s, len);
s += ss;
}
}
fz_md5_final(&md5, digest);
}
#ifdef HAVE_VALGRIND
int fz_valgrind_pixmap(const fz_pixmap *pix)
{
int w, h, n, total;
int ww, hh, nn;
int stride;
const unsigned char *p = pix->samples;
if (pix == NULL)
return 0;
total = 0;
ww = pix->w;
hh = pix->h;
nn = pix->n;
stride = pix->stride - ww*nn;
for (h = 0; h < hh; h++)
{
for (w = 0; w < ww; w++)
for (n = 0; n < nn; n++)
if (*p++) total ++;
p += stride;
}
return total;
}
#endif /* HAVE_VALGRIND */
/*
* Convert pixmap from indexed to base colorspace.
*/
fz_pixmap *
fz_convert_indexed_pixmap_to_base(fz_context *ctx, const fz_pixmap *src)
{
fz_pixmap *dst;
fz_colorspace *base;
const unsigned char *s;
unsigned char *d;
int y, x, k, n, high;
unsigned char *lookup;
int s_line_inc, d_line_inc;
if (src->colorspace->type != FZ_COLORSPACE_INDEXED)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot convert non-indexed pixmap");
if (src->n != 1 + src->alpha)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot convert indexed pixmap mis-matching components");
base = src->colorspace->u.indexed.base;
high = src->colorspace->u.indexed.high;
lookup = src->colorspace->u.indexed.lookup;
n = base->n;
dst = fz_new_pixmap_with_bbox(ctx, base, fz_pixmap_bbox(ctx, src), src->seps, src->alpha);
s = src->samples;
d = dst->samples;
s_line_inc = src->stride - src->w * src->n;
d_line_inc = dst->stride - dst->w * dst->n;
if (src->alpha)
{
for (y = 0; y < src->h; y++)
{
for (x = 0; x < src->w; x++)
{
int v = *s++;
int a = *s++;
int aa = a + (a>>7);
v = fz_mini(v, high);
for (k = 0; k < n; k++)
*d++ = (aa * lookup[v * n + k] + 128)>>8;
*d++ = a;
}
s += s_line_inc;
d += d_line_inc;
}
}
else
{
for (y = 0; y < src->h; y++)
{
for (x = 0; x < src->w; x++)
{
int v = *s++;
v = fz_mini(v, high);
for (k = 0; k < n; k++)
*d++ = lookup[v * n + k];
}
s += s_line_inc;
d += d_line_inc;
}
}
if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE)
dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE;
else
dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE;
return dst;
}
/*
* Convert pixmap from DeviceN/Separation to base colorspace.
*/
fz_pixmap *
fz_convert_separation_pixmap_to_base(fz_context *ctx, const fz_pixmap *src)
{
fz_pixmap *dst;
fz_colorspace *ss, *base;
const unsigned char *s;
unsigned char *d;
int y, x, k, sn, bn, a;
float src_v[FZ_MAX_COLORS];
float base_v[FZ_MAX_COLORS];
int s_line_inc, d_line_inc;
ss = src->colorspace;
if (ss->type != FZ_COLORSPACE_SEPARATION)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot expand non-separation pixmap");
if (src->n != ss->n + src->alpha)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot expand separation pixmap mis-matching alpha channel");
base = ss->u.separation.base;
dst = fz_new_pixmap_with_bbox(ctx, base, fz_pixmap_bbox(ctx, src), src->seps, src->alpha);
fz_clear_pixmap(ctx, dst);
fz_try(ctx)
{
s = src->samples;
d = dst->samples;
s_line_inc = src->stride - src->w * src->n;
d_line_inc = dst->stride - dst->w * dst->n;
sn = ss->n;
bn = base->n;
if (src->alpha)
{
for (y = 0; y < src->h; y++)
{
for (x = 0; x < src->w; x++)
{
for (k = 0; k < sn; ++k)
src_v[k] = *s++ / 255.0f;
a = *s++;
ss->u.separation.eval(ctx, ss->u.separation.tint, src_v, sn, base_v, bn);
for (k = 0; k < bn; ++k)
*d++ = base_v[k] * 255.0f;
*d++ = a;
}
s += s_line_inc;
d += d_line_inc;
}
}
else
{
for (y = 0; y < src->h; y++)
{
for (x = 0; x < src->w; x++)
{
for (k = 0; k < sn; ++k)
src_v[k] = *s++ / 255.0f;
ss->u.separation.eval(ctx, ss->u.separation.tint, src_v, sn, base_v, bn);
for (k = 0; k < bn; ++k)
*d++ = base_v[k] * 255.0f;
}
s += s_line_inc;
d += d_line_inc;
}
}
if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE)
dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE;
else
dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE;
}
fz_catch(ctx)
{
fz_drop_pixmap(ctx, dst);
fz_rethrow(ctx);
}
return dst;
}