#include "mupdf/fitz.h"
#define SLOWCMYK
void
fz_drop_colorspace_imp(fz_context *ctx, fz_storable *cs_)
{
fz_colorspace *cs = (fz_colorspace *)cs_;
if (cs->free_data && cs->data)
cs->free_data(ctx, cs);
fz_free(ctx, cs);
}
fz_colorspace *
fz_new_colorspace(fz_context *ctx, char *name, int n)
{
fz_colorspace *cs = fz_malloc_struct(ctx, fz_colorspace);
FZ_INIT_STORABLE(cs, 1, fz_drop_colorspace_imp);
cs->size = sizeof(fz_colorspace);
fz_strlcpy(cs->name, name, sizeof cs->name);
cs->n = n;
cs->to_rgb = NULL;
cs->from_rgb = NULL;
cs->free_data = NULL;
cs->data = NULL;
return cs;
}
fz_colorspace *
fz_keep_colorspace(fz_context *ctx, fz_colorspace *cs)
{
return (fz_colorspace *)fz_keep_storable(ctx, &cs->storable);
}
void
fz_drop_colorspace(fz_context *ctx, fz_colorspace *cs)
{
fz_drop_storable(ctx, &cs->storable);
}
static void gray_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *gray, float *rgb)
{
rgb[0] = gray[0];
rgb[1] = gray[0];
rgb[2] = gray[0];
}
static void rgb_to_gray(fz_context *ctx, fz_colorspace *cs, const float *rgb, float *gray)
{
float r = rgb[0];
float g = rgb[1];
float b = rgb[2];
gray[0] = r * 0.3f + g * 0.59f + b * 0.11f;
}
static void rgb_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *rgb, float *xyz)
{
xyz[0] = rgb[0];
xyz[1] = rgb[1];
xyz[2] = rgb[2];
}
static void bgr_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *bgr, float *rgb)
{
rgb[0] = bgr[2];
rgb[1] = bgr[1];
rgb[2] = bgr[0];
}
static void rgb_to_bgr(fz_context *ctx, fz_colorspace *cs, const float *rgb, float *bgr)
{
bgr[0] = rgb[2];
bgr[1] = rgb[1];
bgr[2] = rgb[0];
}
static void cmyk_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *cmyk, float *rgb)
{
#ifdef SLOWCMYK
float c = cmyk[0], m = cmyk[1], y = cmyk[2], k = cmyk[3];
float r, g, b, x;
float cm = c * m;
float c1m = m - cm;
float cm1 = c - cm;
float c1m1 = 1 - m - cm1;
float c1m1y = c1m1 * y;
float c1m1y1 = c1m1 - c1m1y;
float c1my = c1m * y;
float c1my1 = c1m - c1my;
float cm1y = cm1 * y;
float cm1y1 = cm1 - cm1y;
float cmy = cm * y;
float cmy1 = cm - cmy;
x = c1m1y1 * k;
r = g = b = c1m1y1 - x;
r += 0.1373 * x;
g += 0.1216 * x;
b += 0.1255 * x;
x = c1m1y * k;
r += 0.1098 * x;
g += 0.1020 * x;
x = c1m1y - x;
r += x;
g += 0.9490 * x;
x = c1my1 * k;
r += 0.1412 * x;
x = c1my1 - x;
r += 0.9255 * x;
b += 0.5490 * x;
x = c1my * k;
r += 0.1333 * x;
x = c1my - x;
r += 0.9294 * x;
g += 0.1098 * x;
b += 0.1412 * x;
x = cm1y1 * k;
g += 0.0588 * x;
b += 0.1412 * x;
x = cm1y1 - x;
g += 0.6784 * x;
b += 0.9373 * x;
x = cm1y * k;
g += 0.0745 * x;
x = cm1y - x;
g += 0.6510 * x;
b += 0.3137 * x;
x = cmy1 * k;
b += 0.0078 * x;
x = cmy1 - x;
r += 0.1804 * x;
g += 0.1922 * x;
b += 0.5725 * x;
x = cmy * (1-k);
r += 0.2118 * x;
g += 0.2119 * x;
b += 0.2235 * x;
rgb[0] = fz_clamp(r, 0, 1);
rgb[1] = fz_clamp(g, 0, 1);
rgb[2] = fz_clamp(b, 0, 1);
#else
rgb[0] = 1 - fz_min(1, cmyk[0] + cmyk[3]);
rgb[1] = 1 - fz_min(1, cmyk[1] + cmyk[3]);
rgb[2] = 1 - fz_min(1, cmyk[2] + cmyk[3]);
#endif
}
static void rgb_to_cmyk(fz_context *ctx, fz_colorspace *cs, const float *rgb, float *cmyk)
{
float c, m, y, k;
c = 1 - rgb[0];
m = 1 - rgb[1];
y = 1 - rgb[2];
k = fz_min(c, fz_min(m, y));
cmyk[0] = c - k;
cmyk[1] = m - k;
cmyk[2] = y - k;
cmyk[3] = k;
}
static fz_colorspace k_default_gray = { {-1, fz_drop_colorspace_imp}, 0, "DeviceGray", 1, gray_to_rgb, rgb_to_gray };
static fz_colorspace k_default_rgb = { {-1, fz_drop_colorspace_imp}, 0, "DeviceRGB", 3, rgb_to_rgb, rgb_to_rgb };
static fz_colorspace k_default_bgr = { {-1, fz_drop_colorspace_imp}, 0, "DeviceBGR", 3, bgr_to_rgb, rgb_to_bgr };
static fz_colorspace k_default_cmyk = { {-1, fz_drop_colorspace_imp}, 0, "DeviceCMYK", 4, cmyk_to_rgb, rgb_to_cmyk };
static fz_colorspace *fz_default_gray = &k_default_gray;
static fz_colorspace *fz_default_rgb = &k_default_rgb;
static fz_colorspace *fz_default_bgr = &k_default_bgr;
static fz_colorspace *fz_default_cmyk = &k_default_cmyk;
struct fz_colorspace_context_s
{
int ctx_refs;
fz_colorspace *gray, *rgb, *bgr, *cmyk;
};
void fz_new_colorspace_context(fz_context *ctx)
{
ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context);
ctx->colorspace->ctx_refs = 1;
ctx->colorspace->gray = fz_default_gray;
ctx->colorspace->rgb = fz_default_rgb;
ctx->colorspace->bgr = fz_default_bgr;
ctx->colorspace->cmyk = fz_default_cmyk;
}
fz_colorspace_context *
fz_keep_colorspace_context(fz_context *ctx)
{
if (!ctx)
return NULL;
return fz_keep_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs);
}
void fz_drop_colorspace_context(fz_context *ctx)
{
if (!ctx)
return;
if (fz_drop_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs))
fz_free(ctx, ctx->colorspace);
}
fz_colorspace *
fz_device_gray(fz_context *ctx)
{
return ctx->colorspace->gray;
}
fz_colorspace *
fz_device_rgb(fz_context *ctx)
{
return ctx->colorspace->rgb;
}
fz_colorspace *
fz_device_bgr(fz_context *ctx)
{
return ctx->colorspace->bgr;
}
fz_colorspace *
fz_device_cmyk(fz_context *ctx)
{
return ctx->colorspace->cmyk;
}
fz_colorspace *
fz_lookup_device_colorspace(fz_context *ctx, char *name)
{
if (!strcmp(name, "DeviceGray"))
return fz_device_gray(ctx);
if (!strcmp(name, "DeviceRGB"))
return fz_device_rgb(ctx);
if (!strcmp(name, "DeviceBGR"))
return fz_device_bgr(ctx);
if (!strcmp(name, "DeviceCMYK"))
return fz_device_cmyk(ctx);
assert(!"unknown device colorspace");
return NULL;
}
void
fz_set_device_gray(fz_context *ctx, fz_colorspace *cs)
{
fz_drop_colorspace(ctx, ctx->colorspace->gray);
ctx->colorspace->gray = fz_keep_colorspace(ctx, cs);
}
void
fz_set_device_rgb(fz_context *ctx, fz_colorspace *cs)
{
fz_drop_colorspace(ctx, ctx->colorspace->rgb);
ctx->colorspace->rgb = fz_keep_colorspace(ctx, cs);
}
void
fz_set_device_bgr(fz_context *ctx, fz_colorspace *cs)
{
fz_drop_colorspace(ctx, ctx->colorspace->bgr);
ctx->colorspace->bgr = fz_keep_colorspace(ctx, cs);
}
void
fz_set_device_cmyk(fz_context *ctx, fz_colorspace *cs)
{
fz_drop_colorspace(ctx, ctx->colorspace->cmyk);
ctx->colorspace->cmyk = fz_keep_colorspace(ctx, cs);
}
int
fz_colorspace_is_indexed(fz_context *ctx, fz_colorspace *cs)
{
return (cs && !strcmp(cs->name, "Indexed"));
}
static void fast_gray_to_rgb(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
d[0] = s[0];
d[1] = s[0];
d[2] = s[0];
d[3] = s[1];
s += 2;
d += 4;
}
}
static void fast_gray_to_cmyk(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
d[0] = 0;
d[1] = 0;
d[2] = 0;
d[3] = 255 - s[0];
d[4] = s[1];
s += 2;
d += 5;
}
}
static void fast_rgb_to_gray(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
d[0] = ((s[0]+1) * 77 + (s[1]+1) * 150 + (s[2]+1) * 28) >> 8;
d[1] = s[3];
s += 4;
d += 2;
}
}
static void fast_bgr_to_gray(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
d[0] = ((s[0]+1) * 28 + (s[1]+1) * 150 + (s[2]+1) * 77) >> 8;
d[1] = s[3];
s += 4;
d += 2;
}
}
static void fast_rgb_to_cmyk(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
unsigned char c = 255 - s[0];
unsigned char m = 255 - s[1];
unsigned char y = 255 - s[2];
unsigned char k = (unsigned char)fz_mini(c, fz_mini(m, y));
d[0] = c - k;
d[1] = m - k;
d[2] = y - k;
d[3] = k;
d[4] = s[3];
s += 4;
d += 5;
}
}
static void fast_bgr_to_cmyk(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
unsigned char c = 255 - s[2];
unsigned char m = 255 - s[1];
unsigned char y = 255 - s[0];
unsigned char k = (unsigned char)fz_mini(c, fz_mini(m, y));
d[0] = c - k;
d[1] = m - k;
d[2] = y - k;
d[3] = k;
d[4] = s[3];
s += 4;
d += 5;
}
}
static void fast_cmyk_to_gray(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
unsigned char c = fz_mul255(s[0], 77);
unsigned char m = fz_mul255(s[1], 150);
unsigned char y = fz_mul255(s[2], 28);
d[0] = 255 - (unsigned char)fz_mini(c + m + y + s[3], 255);
d[1] = s[4];
s += 5;
d += 2;
}
}
#ifdef ARCH_ARM
static void
fast_cmyk_to_rgb_ARM(unsigned char *dst, unsigned char *src, int n)
__attribute__((naked));
static void
fast_cmyk_to_rgb_ARM(unsigned char *dst, unsigned char *src, int n)
{
asm volatile(
ENTER_ARM
"stmfd r13!,{r4-r11,r14} \n"
"@ r0 = dst \n"
"@ r1 = src \n"
"@ r2 = n \n"
"mov r12, #0 @ r12= CMYK = 0 \n"
"b 2f @ enter loop \n"
"1: @ White or Black \n"
"@ Cunning trick: On entry r11 = 0 if black, r11 = FF if white \n"
"eor r12,r11,#0xFF @ r12= FF if black, 0 if white \n"
"ldrb r7, [r1],#1 @ r8 = s[4] \n"
"strb r11,[r0],#1 @ d[0] = r \n"
"strb r11,[r0],#1 @ d[1] = g \n"
"strb r11,[r0],#1 @ d[2] = b \n"
"strb r7, [r0],#1 @ d[3] = s[4] \n"
"mov r12,r12,LSL #24 @ r12 = CMYK \n"
"subs r2, r2, #1 @ r2 = n-- \n"
"beq 9f \n"
"2: @ Main loop starts here \n"
"ldrb r3, [r1], #4 @ r3 = c \n"
"ldrb r6, [r1, #-1] @ r6 = k \n"
"ldrb r5, [r1, #-2] @ r5 = y \n"
"ldrb r4, [r1, #-3] @ r4 = m \n"
"eors r11,r6, #0xFF @ if (k == 255) \n"
"beq 1b @ goto black \n"
"orr r7, r3, r4, LSL #8 \n"
"orr r14,r5, r6, LSL #8 \n"
"orrs r7, r7, r14,LSL #16 @ r7 = cmyk \n"
"beq 1b @ if (cmyk == 0) white \n"
"@ At this point, we have to decode a new pixel \n"
"@ r0 = dst r1 = src r2 = n r7 = cmyk \n"
"3: @ unmatched \n"
"stmfd r13!,{r0-r1,r7} @ stash regs for space \n"
"add r3, r3, r3, LSR #7 @ r3 = c += c>>7 \n"
"add r4, r4, r4, LSR #7 @ r4 = m += m>>7 \n"
"add r5, r5, r5, LSR #7 @ r5 = y += y>>7 \n"
"add r6, r6, r6, LSR #7 @ r6 = k += k>>7 \n"
"mov r5, r5, LSR #1 @ sacrifice 1 bit of Y \n"
"mul r8, r3, r4 @ r8 = cm = c * m \n"
"rsb r9, r8, r4, LSL #8 @ r9 = c1m = (m<<8) - cm \n"
"rsb r3, r8, r3, LSL #8 @ r3 = cm1 = (c<<8) - cm \n"
"rsb r4, r4, #0x100 @ r4 = 256-m \n"
"rsb r4, r3, r4, LSL #8 @ r4 = c1m1 =((256-m)<<8)-cm1 \n"
"mul r7, r4, r5 @ r7 = c1m1y = c1m1 * y \n"
"rsb r4, r7, r4, LSL #7 @ r4 = c1m1y1 = (c1m1<<7)-c1m1y \n"
"mul r10,r9, r5 @ r10= c1my = c1m * y \n"
"rsb r9, r10,r9, LSL #7 @ r9 = c1my1 = (c1m<<7) - c1my \n"
"mul r11,r3, r5 @ r11= cm1y = cm1 * y \n"
"rsb r3, r11,r3, LSL #7 @ r3 = cm1y1 = (cm1<<7) - cm1y \n"
"mul r5, r8, r5 @ r5 = cmy = cm * y \n"
"rsb r8, r5, r8, LSL #7 @ r8 = cmy1 = (cm<<7) - cmy \n"
"@ Register recap: \n"
"@ r3 = cm1y1 \n"
"@ r4 = c1m1y1 \n"
"@ r5 = cmy \n"
"@ r6 = k \n"
"@ r7 = c1m1y \n"
"@ r8 = cmy1 \n"
"@ r9 = c1my1 \n"
"@ r10= c1my \n"
"@ r11= cm1y \n"
"@ The actual matrix multiplication \n"
"mul r14,r4, r6 @ r14= x1 = c1m1y1 * k \n"
"rsb r4, r14,r4, LSL #8 @ r4 = x0 = (c1m1y1<<8) - x1 \n"
"add r4, r4, r14,LSR #8-5 @ r4 = b = x0 + 32*(x1>>8) \n"
"sub r1, r4, r14,LSR #8 @ r1 = g = x0 + 31*(x1>>8) \n"
"add r0, r1, r14,LSR #8-2 @ r0 = r = x0 + 35*(x1>>8) \n"
" \n"
"mul r14,r7, r6 @ r14= x1 = c1m1y * k \n"
"rsb r7, r14,r7, LSL #8 @ r7 = x0 = (c1m1y<<8) - x1 \n"
"add r0, r0, r7 @ r0 = r += x0 \n"
"add r1, r1, r7 @ r1 = g += (x0>>8 * 256) \n"
"sub r1, r1, r7, LSR #8-3 @ 248 \n"
"sub r1, r1, r7, LSR #8-2 @ 244 \n"
"sub r1, r1, r7, LSR #8 @ 243 \n"
"sub r7, r14,r14,LSR #3 @ r7 = 28*(x1>>5) \n"
"add r0, r0, r7, LSR #8-5 @ r0 = r += 28 * x1 \n"
"sub r7, r7, r14,LSR #4 @ r7 = 26*(x1>>5) \n"
"add r1, r1, r7, LSR #8-5 @ r1 = g += 26 * x1 \n"
" \n"
"mul r14,r9, r6 @ r14= x1 = c1my1 * k \n"
"sub r9, r9, r14,LSR #8 @ r9 = x0>>8 = c1my1 - (x1>>8) \n"
"add r0, r0, r14,LSR #8-5 @ r0 = r += (x1>>8)*32 \n"
"add r0, r0, r14,LSR #8-2 @ r0 = r += (x1>>8)*36 \n"
"mov r14,#237 @ r14= 237 \n"
"mla r0,r14,r9,r0 @ r14= r += x0*237 \n"
"mov r14,#141 @ r14= 141 \n"
"mla r4,r14,r9,r4 @ r14= b += x0*141 \n"
" \n"
"mul r14,r10,r6 @ r14= x1 = c1my * k \n"
"sub r10,r10,r14,LSR #8 @ r10= x0>>8 = c1my - (x1>>8) \n"
"add r0, r0, r14,LSR #8-5 @ r0 = r += 32 * x1 \n"
"add r0, r0, r14,LSR #8-1 @ r0 = r += 34 * x1 \n"
"mov r14,#238 @ r14= 238 \n"
"mla r0,r14,r10,r0 @ r0 = r += 238 * x0 \n"
"mov r14,#28 @ r14= 28 \n"
"mla r1,r14,r10,r1 @ r1 = g += 28 * x0 \n"
"mov r14,#36 @ r14= 36 \n"
"mla r4,r14,r10,r4 @ r4 = b += 36 * x0 \n"
" \n"
"mul r14,r3, r6 @ r14= x1 = cm1y1 * k \n"
"sub r3, r3, r14,LSR #8 @ r3 = x1>>8 = cm1y1 - (x1>>8) \n"
"add r1, r1, r14,LSR #8-4 @ r1 = g += 16*x1 \n"
"sub r1, r1, r14,LSR #8 @ 15*x1 \n"
"add r4, r4, r14,LSR #8-5 @ r4 = b += 32*x1 \n"
"add r4, r4, r14,LSR #8-2 @ 36*x1 \n"
"mov r14,#174 @ r14= 174 \n"
"mla r1, r14,r3, r1 @ r1 = g += 174 * x0 \n"
"mov r14,#240 @ r14= 240 \n"
"mla r4, r14,r3, r4 @ r4 = b += 240 * x0 \n"
" \n"
"mul r14,r11,r6 @ r14= x1 = cm1y * k \n"
"sub r11,r11,r14,LSR #8 @ r11= x0>>8 = cm1y - (x1>>8) \n"
"add r1, r1, r14,LSR #8-4 @ r1 = g += x1 * 16 \n"
"add r1, r1, r14,LSR #8 @ x1 * 17 \n"
"add r1, r1, r14,LSR #8-1 @ x1 * 19 \n"
"mov r14,#167 @ r14 = 167 \n"
"mla r1, r14,r11,r1 @ r1 = g += 167 * x0 \n"
"mov r14,#80 @ r14 = 80 \n"
"mla r4, r14,r11,r4 @ r4 = b += 80 * x0 \n"
" \n"
"mul r14,r8, r6 @ r14= x1 = cmy1 * k \n"
"sub r8, r8, r14,LSR #8 @ r8 = x0>>8 = cmy1 - (x1>>8) \n"
"add r4, r4, r14,LSR #8-1 @ r4 = b += x1 * 2 \n"
"mov r14,#46 @ r14=46 \n"
"mla r0, r14,r8, r0 @ r0 = r += 46 * x0 \n"
"mov r14,#49 @ r14=49 \n"
"mla r1, r14,r8, r1 @ r1 = g += 49 * x0 \n"
"mov r14,#147 @ r14=147 \n"
"mla r4, r14,r8, r4 @ r4 = b += 147 * x0 \n"
" \n"
"rsb r6, r6, #256 @ r6 = k = 256-k \n"
"mul r14,r5, r6 @ r14= x0 = cmy * (256-k) \n"
"mov r11,#54 @ r11= 54 \n"
"mov r14,r14,LSR #8 @ r14= (x0>>8) \n"
"mov r8,#57 @ r8 = 57 \n"
"mla r0,r14,r11,r0 @ r0 = r += 54*x0 \n"
"mla r1,r14,r11,r1 @ r1 = g += 54*x0 \n"
"mla r4,r14,r8, r4 @ r4 = b += 57*x0 \n"
" \n"
"sub r8, r0, r0, LSR #8 @ r8 = r -= (r>>8) \n"
"sub r9, r1, r1, LSR #8 @ r9 = g -= (r>>8) \n"
"sub r10,r4, r4, LSR #8 @ r10= b -= (r>>8) \n"
"ldmfd r13!,{r0-r1,r12} \n"
"mov r8, r8, LSR #23 @ r8 = r>>23 \n"
"mov r9, r9, LSR #23 @ r9 = g>>23 \n"
"mov r10,r10,LSR #23 @ r10= b>>23 \n"
"ldrb r14,[r1],#1 @ r8 = s[4] \n"
"strb r8, [r0],#1 @ d[0] = r \n"
"strb r9, [r0],#1 @ d[1] = g \n"
"strb r10,[r0],#1 @ d[2] = b \n"
"strb r14,[r0],#1 @ d[3] = s[4] \n"
"subs r2, r2, #1 @ r2 = n-- \n"
"beq 9f \n"
"@ At this point, we've just decoded a pixel \n"
"@ r0 = dst r1 = src r2 = n r8 = r r9 = g r10= b r12= CMYK \n"
"4: \n"
"ldrb r3, [r1], #4 @ r3 = c \n"
"ldrb r6, [r1, #-1] @ r6 = k \n"
"ldrb r5, [r1, #-2] @ r5 = y \n"
"ldrb r4, [r1, #-3] @ r4 = m \n"
"eors r11,r6, #0xFF @ if (k == 255) \n"
"beq 1b @ goto black \n"
"orr r7, r3, r4, LSL #8 \n"
"orr r14,r5, r6, LSL #8 \n"
"orrs r7, r7, r14,LSL #16 @ r7 = cmyk \n"
"beq 1b @ if (cmyk == 0) white \n"
"cmp r7, r12 @ if (cmyk != CMYK) \n"
"bne 3b @ not the same, loop \n"
"@ If we get here, we just matched a pixel we have just decoded \n"
"ldrb r3, [r1],#1 @ r8 = s[4] \n"
"strb r8, [r0],#1 @ d[0] = r \n"
"strb r9, [r0],#1 @ d[1] = g \n"
"strb r10,[r0],#1 @ d[2] = b \n"
"strb r3, [r0],#1 @ d[3] = s[4] \n"
"subs r2, r2, #1 @ r2 = n-- \n"
"bne 4b \n"
"9: \n"
"ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n"
ENTER_THUMB
);
}
#endif
static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
#ifdef ARCH_ARM
fast_cmyk_to_rgb_ARM(d, s, n);
#else
unsigned int C,M,Y,K,r,g,b;
C = 0;
M = 0;
Y = 0;
K = 0;
r = 255;
g = 255;
b = 255;
while (n--)
{
#ifdef SLOWCMYK
unsigned int c = s[0];
unsigned int m = s[1];
unsigned int y = s[2];
unsigned int k = s[3];
unsigned int cm, c1m, cm1, c1m1, c1m1y, c1m1y1, c1my, c1my1, cm1y, cm1y1, cmy, cmy1;
unsigned int x0, x1;
if (c == C && m == M && y == Y && k == K)
{
}
else if (k == 0 && c == 0 && m == 0 && y == 0)
{
r = g = b = 255;
C = 0;
M = 0;
Y = 0;
K = 0;
}
else if (k == 255)
{
r = g = b = 0;
C = 0;
M = 0;
Y = 0;
K = 255;
}
else
{
c += c>>7;
m += m>>7;
y += y>>7;
k += k>>7;
y >>= 1;
cm = c * m;
c1m = (m<<8) - cm;
cm1 = (c<<8) - cm;
c1m1 = ((256 - m)<<8) - cm1;
c1m1y = c1m1 * y;
c1m1y1 = (c1m1<<7) - c1m1y;
c1my = c1m * y;
c1my1 = (c1m<<7) - c1my;
cm1y = cm1 * y;
cm1y1 = (cm1<<7) - cm1y;
cmy = cm * y;
cmy1 = (cm<<7) - cmy;
x1 = c1m1y1 * k;
x0 = (c1m1y1<<8) - x1;
x1 = x1>>8;
r = g = b = x0;
r += 35 * x1;
g += 31 * x1;
b += 32 * x1;
x1 = c1m1y * k;
x0 = (c1m1y<<8) - x1;
x1 >>= 8;
r += 28 * x1;
g += 26 * x1;
r += x0;
x0 >>= 8;
g += 243 * x0;
x1 = c1my1 * k;
x0 = (c1my1<<8) - x1;
x1 >>= 8;
x0 >>= 8;
r += 36 * x1;
r += 237 * x0;
b += 141 * x0;
x1 = c1my * k;
x0 = (c1my<<8) - x1;
x1 >>= 8;
x0 >>= 8;
r += 34 * x1;
r += 238 * x0;
g += 28 * x0;
b += 36 * x0;
x1 = cm1y1 * k;
x0 = (cm1y1<<8) - x1;
x1 >>= 8;
x0 >>= 8;
g += 15 * x1;
b += 36 * x1;
g += 174 * x0;
b += 240 * x0;
x1 = cm1y * k;
x0 = (cm1y<<8) - x1;
x1 >>= 8;
x0 >>= 8;
g += 19 * x1;
g += 167 * x0;
b += 80 * x0;
x1 = cmy1 * k;
x0 = (cmy1<<8) - x1;
x1 >>= 8;
x0 >>= 8;
b += 2 * x1;
r += 46 * x0;
g += 49 * x0;
b += 147 * x0;
x0 = cmy * (256-k);
x0 >>= 8;
r += 54 * x0;
g += 54 * x0;
b += 57 * x0;
r -= (r>>8);
g -= (g>>8);
b -= (b>>8);
r = r>>23;
g = g>>23;
b = b>>23;
C = c;
M = m;
Y = y;
K = k;
}
d[0] = r;
d[1] = g;
d[2] = b;
#else
d[0] = 255 - (unsigned char)fz_mini(s[0] + s[3], 255);
d[1] = 255 - (unsigned char)fz_mini(s[1] + s[3], 255);
d[2] = 255 - (unsigned char)fz_mini(s[2] + s[3], 255);
#endif
d[3] = s[4];
s += 5;
d += 4;
}
#endif
}
static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
#ifdef SLOWCMYK
float cmyk[4], rgb[3];
cmyk[0] = s[0] / 255.0f;
cmyk[1] = s[1] / 255.0f;
cmyk[2] = s[2] / 255.0f;
cmyk[3] = s[3] / 255.0f;
cmyk_to_rgb(ctx, NULL, cmyk, rgb);
d[0] = rgb[2] * 255;
d[1] = rgb[1] * 255;
d[2] = rgb[0] * 255;
#else
d[0] = 255 - (unsigned char)fz_mini(s[2] + s[3], 255);
d[1] = 255 - (unsigned char)fz_mini(s[1] + s[3], 255);
d[2] = 255 - (unsigned char)fz_mini(s[0] + s[3], 255);
#endif
d[3] = s[4];
s += 5;
d += 4;
}
}
static void fast_rgb_to_bgr(fz_pixmap *dst, fz_pixmap *src)
{
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
int n = src->w * src->h;
while (n--)
{
d[0] = s[2];
d[1] = s[1];
d[2] = s[0];
d[3] = s[3];
s += 4;
d += 4;
}
}
static void
fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src)
{
float srcv[FZ_MAX_COLORS];
float dstv[FZ_MAX_COLORS];
int srcn, dstn;
int k, i;
unsigned int xy;
fz_colorspace *ss = src->colorspace;
fz_colorspace *ds = dst->colorspace;
unsigned char *s = src->samples;
unsigned char *d = dst->samples;
assert(src->w == dst->w && src->h == dst->h);
assert(src->n == ss->n + 1);
assert(dst->n == ds->n + 1);
srcn = ss->n;
dstn = ds->n;
xy = (unsigned int)(src->w * src->h);
if (!strcmp(ss->name, "Lab") && srcn == 3)
{
fz_color_converter cc;
fz_lookup_color_converter(ctx, &cc, ds, ss);
for (; xy > 0; xy--)
{
srcv[0] = *s++ / 255.0f * 100;
srcv[1] = *s++ - 128;
srcv[2] = *s++ - 128;
cc.convert(ctx, &cc, dstv, srcv);
for (k = 0; k < dstn; k++)
*d++ = dstv[k] * 255;
*d++ = *s++;
}
}
else if (xy < 256)
{
fz_color_converter cc;
fz_lookup_color_converter(ctx, &cc, ds, ss);
for (; xy > 0; xy--)
{
for (k = 0; k < srcn; k++)
srcv[k] = *s++ / 255.0f;
cc.convert(ctx, &cc, dstv, srcv);
for (k = 0; k < dstn; k++)
*d++ = dstv[k] * 255;
*d++ = *s++;
}
}
else if (srcn == 1)
{
unsigned char lookup[FZ_MAX_COLORS * 256];
fz_color_converter cc;
fz_lookup_color_converter(ctx, &cc, ds, ss);
for (i = 0; i < 256; i++)
{
srcv[0] = i / 255.0f;
cc.convert(ctx, &cc, dstv, srcv);
for (k = 0; k < dstn; k++)
lookup[i * dstn + k] = dstv[k] * 255;
}
for (; xy > 0; xy--)
{
i = *s++;
for (k = 0; k < dstn; k++)
*d++ = lookup[i * dstn + k];
*d++ = *s++;
}
}
else
{
fz_hash_table *lookup;
unsigned char *color;
unsigned char dummy = s[0] ^ 255;
unsigned char *sold = &dummy;
fz_color_converter cc;
fz_lookup_color_converter(ctx, &cc, ds, ss);
lookup = fz_new_hash_table(ctx, 509, srcn, -1);
for (; xy > 0; xy--)
{
if (*s == *sold && memcmp(sold,s,srcn) == 0)
{
sold = s;
memcpy(d, d-dstn-1, dstn);
d += dstn;
s += srcn;
*d++ = *s++;
}
else
{
sold = s;
color = fz_hash_find(ctx, lookup, s);
if (color)
{
memcpy(d, color, dstn);
s += srcn;
d += dstn;
*d++ = *s++;
}
else
{
for (k = 0; k < srcn; k++)
srcv[k] = *s++ / 255.0f;
cc.convert(ctx, &cc, dstv, srcv);
for (k = 0; k < dstn; k++)
*d++ = dstv[k] * 255;
fz_hash_insert(ctx, lookup, s - srcn, d - dstn);
*d++ = *s++;
}
}
}
fz_drop_hash(ctx, lookup);
}
}
void
fz_convert_pixmap(fz_context *ctx, fz_pixmap *dp, fz_pixmap *sp)
{
fz_colorspace *ss = sp->colorspace;
fz_colorspace *ds = dp->colorspace;
assert(ss && ds);
dp->interpolate = sp->interpolate;
if (ss == fz_default_gray)
{
if (ds == fz_default_rgb) fast_gray_to_rgb(dp, sp);
else if (ds == fz_default_bgr) fast_gray_to_rgb(dp, sp);
else if (ds == fz_default_cmyk) fast_gray_to_cmyk(dp, sp);
else fz_std_conv_pixmap(ctx, dp, sp);
}
else if (ss == fz_default_rgb)
{
if (ds == fz_default_gray) fast_rgb_to_gray(dp, sp);
else if (ds == fz_default_bgr) fast_rgb_to_bgr(dp, sp);
else if (ds == fz_default_cmyk) fast_rgb_to_cmyk(dp, sp);
else fz_std_conv_pixmap(ctx, dp, sp);
}
else if (ss == fz_default_bgr)
{
if (ds == fz_default_gray) fast_bgr_to_gray(dp, sp);
else if (ds == fz_default_rgb) fast_rgb_to_bgr(dp, sp);
else if (ds == fz_default_cmyk) fast_bgr_to_cmyk(sp, dp);
else fz_std_conv_pixmap(ctx, dp, sp);
}
else if (ss == fz_default_cmyk)
{
if (ds == fz_default_gray) fast_cmyk_to_gray(dp, sp);
else if (ds == fz_default_bgr) fast_cmyk_to_bgr(ctx, dp, sp);
else if (ds == fz_default_rgb) fast_cmyk_to_rgb(ctx, dp, sp);
else fz_std_conv_pixmap(ctx, dp, sp);
}
else fz_std_conv_pixmap(ctx, dp, sp);
}
static void
std_conv_color(fz_context *ctx, fz_color_converter *cc, float *dstv, const float *srcv)
{
float rgb[3];
int i;
fz_colorspace *srcs = cc->ss;
fz_colorspace *dsts = cc->ds;
if (srcs != dsts)
{
assert(srcs->to_rgb && dsts->from_rgb);
srcs->to_rgb(ctx, srcs, srcv, rgb);
dsts->from_rgb(ctx, dsts, rgb, dstv);
for (i = 0; i < dsts->n; i++)
dstv[i] = fz_clamp(dstv[i], 0, 1);
}
else
{
for (i = 0; i < srcs->n; i++)
dstv[i] = srcv[i];
}
}
static void
g2rgb(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
dv[0] = sv[0];
dv[1] = sv[0];
dv[2] = sv[0];
}
static void
g2cmyk(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
dv[0] = 0;
dv[1] = 0;
dv[2] = 0;
dv[3] = 1 - sv[0];
}
static void
rgb2g(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
dv[0] = sv[0] * 0.3f + sv[1] * 0.59f + sv[2] * 0.11f;
}
static void
rgb2bgr(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
dv[0] = sv[2];
dv[1] = sv[1];
dv[2] = sv[0];
}
static void
rgb2cmyk(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
float c = 1 - sv[0];
float m = 1 - sv[1];
float y = 1 - sv[2];
float k = fz_min(c, fz_min(m, y));
dv[0] = c - k;
dv[1] = m - k;
dv[2] = y - k;
dv[3] = k;
}
static void
bgr2g(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
dv[0] = sv[0] * 0.11f + sv[1] * 0.59f + sv[2] * 0.3f;
}
static void
bgr2cmyk(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
float c = 1 - sv[2];
float m = 1 - sv[1];
float y = 1 - sv[0];
float k = fz_min(c, fz_min(m, y));
dv[0] = c - k;
dv[1] = m - k;
dv[2] = y - k;
dv[3] = k;
}
static void
cmyk2g(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
float c = sv[0] * 0.3f;
float m = sv[1] * 0.59f;
float y = sv[2] * 0.11f;
dv[0] = 1 - fz_min(c + m + y + sv[3], 1);
}
static void
cmyk2rgb(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
#ifdef SLOWCMYK
cmyk_to_rgb(ctx, NULL, sv, dv);
#else
dv[0] = 1 - fz_min(sv[0] + sv[3], 1);
dv[1] = 1 - fz_min(sv[1] + sv[3], 1);
dv[2] = 1 - fz_min(sv[2] + sv[3], 1);
#endif
}
static void
cmyk2bgr(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv)
{
#ifdef SLOWCMYK
float rgb[3];
cmyk_to_rgb(ctx, NULL, sv, rgb);
dv[0] = rgb[2];
dv[1] = rgb[1];
dv[2] = rgb[0];
#else
dv[0] = 1 - fz_min(sv[2] + sv[3], 1);
dv[1] = 1 - fz_min(sv[1] + sv[3], 1);
dv[2] = 1 - fz_min(sv[0] + sv[3], 1);
#endif
}
void fz_lookup_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ds, fz_colorspace *ss)
{
cc->ds = ds;
cc->ss = ss;
if (ss == fz_default_gray)
{
if ((ds == fz_default_rgb) || (ds == fz_default_bgr))
cc->convert = g2rgb;
else if (ds == fz_default_cmyk)
cc->convert = g2cmyk;
else
cc->convert = std_conv_color;
}
else if (ss == fz_default_rgb)
{
if (ds == fz_default_gray)
cc->convert = rgb2g;
else if (ds == fz_default_bgr)
cc->convert = rgb2bgr;
else if (ds == fz_default_cmyk)
cc->convert = rgb2cmyk;
else
cc->convert = std_conv_color;
}
else if (ss == fz_default_bgr)
{
if (ds == fz_default_gray)
cc->convert = bgr2g;
else if (ds == fz_default_rgb)
cc->convert = rgb2bgr;
else if (ds == fz_default_cmyk)
cc->convert = bgr2cmyk;
else
cc->convert = std_conv_color;
}
else if (ss == fz_default_cmyk)
{
if (ds == fz_default_gray)
cc->convert = cmyk2g;
else if (ds == fz_default_rgb)
cc->convert = cmyk2rgb;
else if (ds == fz_default_bgr)
cc->convert = cmyk2bgr;
else
cc->convert = std_conv_color;
}
else
cc->convert = std_conv_color;
}
void
fz_convert_color(fz_context *ctx, fz_colorspace *ds, float *dv, fz_colorspace *ss, const float *sv)
{
fz_color_converter cc;
fz_lookup_color_converter(ctx, &cc, ds, ss);
cc.convert(ctx, &cc, dv, sv);
}
struct indexed
{
fz_colorspace *base;
int high;
unsigned char *lookup;
};
static void
indexed_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *color, float *rgb)
{
struct indexed *idx = cs->data;
float alt[FZ_MAX_COLORS];
int i, k;
i = color[0] * 255;
i = fz_clampi(i, 0, idx->high);
for (k = 0; k < idx->base->n; k++)
alt[k] = idx->lookup[i * idx->base->n + k] / 255.0f;
idx->base->to_rgb(ctx, idx->base, alt, rgb);
}
static void
free_indexed(fz_context *ctx, fz_colorspace *cs)
{
struct indexed *idx = cs->data;
if (idx->base)
fz_drop_colorspace(ctx, idx->base);
fz_free(ctx, idx->lookup);
fz_free(ctx, idx);
}
fz_colorspace *
fz_new_indexed_colorspace(fz_context *ctx, fz_colorspace *base, int high, unsigned char *lookup)
{
fz_colorspace *cs;
struct indexed *idx;
idx = fz_malloc_struct(ctx, struct indexed);
idx->lookup = lookup;
idx->base = base;
idx->high = high;
fz_try(ctx)
{
cs = fz_new_colorspace(ctx, "Indexed", 1);
cs->to_rgb = indexed_to_rgb;
cs->free_data = free_indexed;
cs->data = idx;
cs->size += sizeof(*idx) + (base->n * (idx->high + 1)) + base->size;
}
fz_catch(ctx)
{
fz_free(ctx, idx);
fz_rethrow_message(ctx, "failed to create indexed colorspace");
}
return cs;
}
fz_pixmap *
fz_expand_indexed_pixmap(fz_context *ctx, fz_pixmap *src)
{
struct indexed *idx;
fz_pixmap *dst;
unsigned char *s, *d;
int y, x, k, n, high;
unsigned char *lookup;
fz_irect bbox;
assert(src->colorspace->to_rgb == indexed_to_rgb);
assert(src->n == 2);
idx = src->colorspace->data;
high = idx->high;
lookup = idx->lookup;
n = idx->base->n;
dst = fz_new_pixmap_with_bbox(ctx, idx->base, fz_pixmap_bbox(ctx, src, &bbox));
s = src->samples;
d = dst->samples;
for (y = 0; y < src->h; y++)
{
for (x = 0; x < src->w; x++)
{
int v = *s++;
int a = *s++;
v = fz_mini(v, high);
for (k = 0; k < n; k++)
*d++ = fz_mul255(lookup[v * n + k], a);
*d++ = a;
}
}
dst->interpolate = src->interpolate;
return dst;
}
typedef struct fz_cached_color_converter
{
fz_color_converter base;
fz_hash_table *hash;
}
fz_cached_color_converter;
static void fz_cached_color_convert(fz_context *ctx, fz_color_converter *cc_, float *ds, const float *ss)
{
fz_cached_color_converter *cc = cc_->opaque;
void *val = fz_hash_find(ctx, cc->hash, ss);
int n = cc->base.ds->n * sizeof(float);
fz_color_converter *base_cc = &cc->base;
if (val)
{
memcpy(ds, val, n);
return;
}
base_cc->convert(ctx, base_cc, ds, ss);
val = fz_malloc(ctx, n);
memcpy(val, ds, n);
fz_try(ctx)
{
fz_hash_insert(ctx, cc->hash, ss, val);
}
fz_catch(ctx)
{
fz_free(ctx, val);
}
}
void fz_init_cached_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ds, fz_colorspace *ss)
{
int n = ss->n;
fz_cached_color_converter *cached = fz_malloc_struct(ctx, fz_cached_color_converter);
fz_try(ctx)
{
fz_lookup_color_converter(ctx, &cached->base, ds, ss);
cached->hash = fz_new_hash_table(ctx, 256, n * sizeof(float), -1);
cc->convert = fz_cached_color_convert;
cc->ds = ds;
cc->ss = ss;
cc->opaque = cached;
}
fz_catch(ctx)
{
fz_drop_hash(ctx, cached->hash);
fz_rethrow(ctx);
}
}
void fz_fin_cached_color_converter(fz_context *ctx, fz_color_converter *cc_)
{
fz_cached_color_converter *cc;
int i, n;
if (cc_ == NULL)
return;
cc = cc_->opaque;
if (cc == NULL)
return;
cc_->opaque = NULL;
n = fz_hash_len(ctx, cc->hash);
for (i = 0; i < n; i++)
{
void *v = fz_hash_get_val(ctx, cc->hash, i);
if (v)
fz_free(ctx, v);
}
fz_drop_hash(ctx, cc->hash);
fz_free(ctx, cc);
}