Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 4679 → Rev 4680

/contrib/media/updf/draw/arch_arm.c
0,0 → 1,232
/*
* ARM specific render optims live here
*/
 
#include "fitz.h"
 
typedef unsigned char byte;
 
/* always surround cpu specific code with HAVE_XXX */
#ifdef ARCH_ARM
 
/* from imagescalearm.s */
extern void fz_srow4_arm(byte *src, byte *dst, int w, int denom);
extern void fz_scol4_arm(byte *src, byte *dst, int w, int denom);
 
static void
path_w4i1o4_arm(byte * restrict rgba, byte * restrict src, byte cov, int len, byte * restrict dst)
{
/* The ARM code here is a hand coded implementation of the optimized C version. */
 
if (len <= 0)
return;
 
asm volatile(
"ldr %0, [%0] @ %0 = rgba \n"
"mov r11,#0 \n"
"mov r8, #0xFF00 \n"
"mov r14,%0,lsr #24 @ r14= alpha \n"
"orr %0, %0, #0xFF000000 @ %0 = rgba |= 0xFF000000 \n"
"orr r8, r8, r8, LSL #16 @ r8 = 0xFF00FF00 \n"
"adds r14,r14,r14,LSR #7 @ r14 = alpha += alpha>>7 \n"
"beq 9f @ if (alpha == 0) bale \n"
"and r6, %0, r8 @ r6 = ga<<8 \n"
"bic %0, %0, r8 @ %0 = rb \n"
"mov r6, r6, LSR #8 @ r6 = ga \n"
"cmp r14,#256 @ if (alpha == 256) \n"
"beq 4f @ no-alpha loop \n"
"B 2f @ enter the loop \n"
"1: @ Loop used for when coverage*alpha == 0 \n"
"subs %3, %3, #1 @ len-- \n"
"ble 9f \n"
"2: \n"
"ldrb r12,[%1] @ r12= *src \n"
"ldr r9, [%4], #4 @ r9 = drb = *dst32++ \n"
"strb r11,[%1], #1 @ r11= *src++ = 0 \n"
"add %2, r12, %2 @ %2 = cov += r12 \n"
"ands %2, %2, #255 @ %2 = cov &= 255 \n"
"beq 1b @ if coverage == 0 loop back \n"
"add r10,%2, %2, LSR #7 @ r10= ca = cov+(cov>>7) \n"
"mul r10,r14,r10 @ r10= ca *= alpha \n"
"and r7, r8, r9 @ r7 = dga = drb & MASK \n"
"mov r10,r10,LSR #8 @ r10= ca >>= 8 \n"
"and r9, r8, r9, LSL #8 @ r9 = drb = (drb<<8) & MASK \n"
"sub r12,r6, r7, LSR #8 @ r12= cga = ga - (dga>>8) \n"
"sub r5, %0, r9, LSR #8 @ r5 = crb = rb - (drb>>8) \n"
"mla r7, r12,r10,r7 @ r7 = dga += cga * ca \n"
"subs %3, %3, #1 @ len-- \n"
"mla r9, r5, r10,r9 @ r9 = drb += crb * ca \n"
"and r7, r8, r7 @ r7 = dga &= MASK \n"
"and r9, r8, r9 @ r9 = drb &= MASK \n"
"orr r9, r7, r9, LSR #8 @ r9 = drb = dga | (drb>>8) \n"
"str r9, [%4, #-4] @ dst32[-1] = r9 \n"
"bgt 2b \n"
"b 9f \n"
"@ --- Solid alpha loop --------------------------------------- \n"
"3: @ Loop used when coverage == 256 \n"
"orr r9, %0, r6, LSL #8 @ r9 = rgba \n"
"str r9, [%4, #-4] @ dst32[-1] = r9 \n"
"4: @ Loop used for when coverage*alpha == 0 \n"
"subs %3, %3, #1 @ len-- \n"
"ble 9f \n"
"5: \n"
"ldrb r12,[%1] @ r12= *src \n"
"ldr r9, [%4], #4 @ r9 = drb = *dst32++ \n"
"strb r11,[%1], #1 @ r11= *src++ = 0 \n"
"add %2, r12, %2 @ %2 = cov += r12 \n"
"ands %2, %2, #255 @ %2 = cov &= 255 \n"
"beq 4b @ if coverage == 0 loop back \n"
"cmp %2, #255 @ if coverage == solid \n"
"beq 3b @ loop back \n"
"add r10,%2, %2, LSR #7 @ r10= ca = cov+(cov>>7) \n"
"and r7, r8, r9 @ r7 = dga = drb & MASK \n"
"and r9, r8, r9, LSL #8 @ r9 = dga = (drb<<8) & MASK \n"
"sub r12,r6, r7, LSR #8 @ r12= cga = ga - (dga>>8) \n"
"sub r5, %0, r9, LSR #8 @ r5 = crb = rb - (drb>>8) \n"
"mla r7, r12,r10,r7 @ r7 = dga += cga * ca \n"
"subs %3, %3, #1 @ len-- \n"
"mla r9, r5, r10,r9 @ r9 = drb += crb * ca \n"
"and r7, r8, r7 @ r7 = dga &= MASK \n"
"and r9, r8, r9 @ r9 = drb &= MASK \n"
"orr r9, r7, r9, LSR #8 @ r9 = drb = dga | (drb>>8) \n"
"str r9, [%4, #-4] @ dst32[-1] = r9 \n"
"bgt 5b \n"
"9: @ End \n"
:
"+r" (rgba),
"+r" (src),
"+r" (cov),
"+r" (len),
"+r" (dst)
:
:
"r5","r6","r7","r8","r9","r10","r11","r12","r14","memory","cc"
);
}
 
static void load_tile8_arm(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad)
{
if ((h == 0) || (w == 0))
return;
 
switch (pad)
{
case 0:
while (h--)
{
memcpy(dst, src, w);
src += sw;
dst += dw;
}
break;
 
case 1:
sw -= w;
dw -= w<<1;
asm volatile(
"MOV r11,#255 \n"
"1: \n"
"MOV r5, %[w] @ r5 = x = w \n"
"2: \n"
"LDRB r4, [%[src]], #1 @ r4 = *src++ \n"
"SUBS r5, r5, #1 \n"
"STRB r4, [%[dst]], #1 @ *dst++ = r4 \n"
"STRB r11,[%[dst]], #1 @ *dst++ = 255 \n"
"BGT 2b \n"
"ADD %[src],%[src],%[sw] @ src += sw \n"
"ADD %[dst],%[dst],%[dw] @ dst += dw \n"
"SUBS %[h],%[h],#1 \n"
"BGT 1b \n"
:
[src] "+r" (src),
[sw] "+r" (sw),
[dst] "+r" (dst),
[dw] "+r" (dw),
[h] "+r" (h),
[w] "+r" (w)
:
:
"r4","r5","r11","memory","cc"
);
break;
 
case 3:
sw -= w;
asm volatile(
"MOV r11,#255 \n"
"1: \n"
"MOV r5, %[w] @ r5 = x = w \n"
"MOV r8, %[dst] @ r8 = dp = dst \n"
"2: \n"
"LDRB r4, [%[src]], #1 @ r4 = *src++ \n"
"LDRB r6, [%[src]], #1 @ r6 = *src++ \n"
"LDRB r7, [%[src]], #1 @ r7 = *src++ \n"
"SUBS r5, r5, #3 \n"
"STRB r4, [r8], #1 @ *dp++ = r4 \n"
"STRB r6, [r8], #1 @ *dp++ = r6 \n"
"STRB r7, [r8], #1 @ *dp++ = r7 \n"
"STRB r11,[r8], #1 @ *dp++ = 255 \n"
"BGT 2b \n"
"ADD %[src],%[src],%[sw] @ src += sw \n"
"ADD %[dst],%[dst],%[dw] @ dst += dw \n"
"SUBS %[h],%[h],#1 \n"
"BGT 1b \n"
:
[src] "+r" (src),
[sw] "+r" (sw),
[dst] "+r" (dst),
[dw] "+r" (dw),
[h] "+r" (h),
[w] "+r" (w)
:
:
"r4","r5","r6","r7","r8","r11","memory","cc"
);
break;
 
default:
sw -= w;
asm volatile(
"mov r9,#255 \n"
"1: \n"
"mov r7, %[dst] @ r7 = dp = dst \n"
"mov r8, #1 @ r8 = tpad = 1 \n"
"mov r14,%[w] @ r11= x = w \n"
"2: \n"
"ldrb r10,[%[src]],#1 \n"
"subs r8, r8, #1 \n"
"moveq r8, %[pad] \n"
"streqb r9, [r7], #1 \n"
"strb r10,[r7], #1 \n"
"subs r14,r14, #1 \n"
"bgt 2b \n"
"add %[src],%[src],%[sw] \n"
"add %[dst],%[dst],%[dw] \n"
"subs %[h], %[h], #1 \n"
"bgt 1b \n"
:
[src] "+r" (src),
[sw] "+r" (sw),
[dst] "+r" (dst),
[dw] "+r" (dw),
[h] "+r" (h),
[w] "+r" (w),
[pad] "+r" (pad)
:
:
"r7","r8","r9","r10","r14","memory","cc"
);
break;
}
}
 
void
fz_accelerate_arch(void)
{
fz_path_w4i1o4 = path_w4i1o4_arm;
fz_loadtile8 = load_tile8_arm;
fz_srow4 = fz_srow4_arm;
fz_scol4 = fz_scol4_arm;
}
 
#endif
/contrib/media/updf/draw/arch_port.c
0,0 → 1,486
#include "fitz.h"
 
typedef unsigned char byte;
 
/* These C implementations use SWAR (SIMD-within-a-register) techniques. */
 
#if 0 /* TODO: move into porterduff.c functions */
 
#define MASK 0xFF00FF00;
 
static void
path_w4i1o4_32bit(byte *rgba,
byte * restrict src, byte cov, int len, byte * restrict dst)
{
/* COLOR * coverage + DST * (256-coverage) = (COLOR - DST)*coverage + DST*256 */
unsigned int *dst32 = (unsigned int *)(void *)dst;
int alpha = rgba[3];
unsigned int rb = rgba[0] | (rgba[2] << 16);
unsigned int ga = rgba[1] | 0xFF0000;
 
if (alpha == 0)
return;
 
if (alpha != 255)
{
alpha += alpha>>7; /* alpha is now in the 0...256 range */
while (len--)
{
unsigned int ca, drb, dga, crb, cga;
cov += *src; *src++ = 0;
ca = cov + (cov>>7); /* ca is in 0...256 range */
ca = (ca*alpha)>>8; /* ca is is in 0...256 range */
drb = *dst32++;
if (ca != 0)
{
dga = drb & MASK;
drb = (drb<<8) & MASK;
cga = ga - (dga>>8);
crb = rb - (drb>>8);
dga += cga * ca;
drb += crb * ca;
dga &= MASK;
drb &= MASK;
drb = dga | (drb>>8);
dst32[-1] = drb;
}
}
}
else
{
while (len--)
{
unsigned int ca, drb, dga, crb, cga;
cov += *src; *src++ = 0;
ca = cov + (cov>>7); /* ca is in 0...256 range */
drb = *dst32++;
if (ca == 0)
continue;
if (ca == 255)
{
drb = (ga<<8) | rb;
}
else
{
dga = drb & MASK;
drb = (drb<<8) & MASK;
cga = ga - (dga>>8);
crb = rb - (drb>>8);
dga += cga * ca;
drb += crb * ca;
dga &= MASK;
drb &= MASK;
drb = dga |(drb>>8);
}
dst32[-1] = drb;
}
}
}
 
static void
text_w4i1o4_32bit(byte *rgba,
byte * restrict src, int srcw,
byte * restrict dst, int dstw, int w0, int h)
{
unsigned int *dst32 = (unsigned int *)(void *)dst;
unsigned int alpha = rgba[3];
unsigned int rb = rgba[0] | (rgba[2] << 16);
unsigned int ga = rgba[1] | 0xFF0000;
 
if (alpha == 0)
return;
 
srcw -= w0;
dstw = (dstw>>2)-w0;
 
if (alpha != 255)
{
alpha += alpha>>7; /* alpha is now in the 0...256 range */
while (h--)
{
int w = w0;
while (w--)
{
unsigned int ca, drb, dga, crb, cga;
ca = *src++;
drb = *dst32++;
ca += ca>>7;
ca = (ca*alpha)>>8;
if (ca == 0)
continue;
dga = drb & MASK;
drb = (drb<<8) & MASK;
cga = ga - (dga>>8);
crb = rb - (drb>>8);
dga += cga * ca;
drb += crb * ca;
dga &= MASK;
drb &= MASK;
drb = dga | (drb>>8);
dst32[-1] = drb;
}
src += srcw;
dst32 += dstw;
}
}
else
{
while (h--)
{
int w = w0;
while (w--)
{
unsigned int ca, drb, dga, crb, cga;
ca = *src++;
drb = *dst32++;
ca += ca>>7;
if (ca == 0)
continue;
dga = drb & MASK;
drb = (drb<<8) & MASK;
cga = ga - (dga>>8);
crb = rb - (drb>>8);
dga += cga * ca;
drb += crb * ca;
dga &= MASK;
drb &= MASK;
drb = dga | (drb>>8);
dst32[-1] = drb;
}
src += srcw;
dst32 += dstw;
}
}
}
 
static void
img_4o4_32bit(byte * restrict src, byte cov, int len, byte * restrict dst,
fz_pixmap *image, int u, int v, int fa, int fb)
{
unsigned int *dst32 = (unsigned int *)(void *)dst;
unsigned int *samples = (unsigned int *)(void *)image->samples;
int w = image->w;
int h = image->h-1;
 
while (len--)
{
unsigned int a, a1, d, d1;
int sa;
cov += *src; *src = 0; src++;
/* (a,a1) = sampleargb(samples, w, h, u, v, argb); */
{
int ui, ui1, vi, vi1, ud, vd;
unsigned int b, b1, c, c1;
ui1 = 1;
ui = u >> 16;
if (ui < 0)
{
ui = 0;
ui1 = 0;
}
else if (ui >= w-1)
{
ui = w-1;
ui1 = 0;
}
vi1 = w;
vi = v >> 16;
if (vi < 0)
{
vi = 0;
vi1 = 0;
}
else if (vi >= h)
{
vi = h;
vi1 = 0;
}
ui += vi*w;
a = samples[ui];
b = samples[ui + ui1];
c = samples[ui + vi1];
d = samples[ui + ui1 + vi1];
ud = (u>>8) & 0xFF;
vd = (v>>8) & 0xFF;
ud = FZ_EXPAND(ud);
vd = FZ_EXPAND(vd);
/* (a,a1) = blend(a,b,ud) */
a1 = a & MASK;
a = (a<<8) & MASK;
b1 = (b>>8) & ~MASK;
b = b & ~MASK;
a = ((b -(a >>8)) * ud + a ) & MASK;
a1 = ((b1-(a1>>8)) * ud + a1) & MASK;
/* (c,c1) = blend(c,d,ud) */
c1 = c & MASK;
c = (c<<8) & MASK;
d1 = (d>>8) & ~MASK;
d = d & ~MASK;
c = ((d -(c >>8)) * ud + c ) & MASK;
c1 = ((d1-(c1>>8)) * ud + c1) & MASK;
/* (a,a1) = blend((a,a1),(c,c1),vd) */
a = (((c >>8)-(a >>8)) * vd + a ) & MASK;
a1 = (((c1>>8)-(a1>>8)) * vd + a1) & MASK;
}
sa = (a1>>24);
sa = FZ_COMBINE(FZ_EXPAND(sa), FZ_EXPAND(cov));
a1 |= 0xFF000000;
d = *dst32++;
d1 = d & MASK;
d = (d<<8) & MASK;
a = (((a >>8)-(d >>8)) * sa + d ) & MASK;
a1 = (((a1>>8)-(d1>>8)) * sa + d1) & MASK;
dst32[-1] = (a>>8) | a1;
u += fa;
v += fb;
}
}
 
static void
img_w4i1o4_32bit(byte *rgba, byte * restrict src, byte cov, int len,
byte * restrict dst, fz_pixmap *image, int u, int v, int fa, int fb)
{
byte *samples = image->samples;
int w = image->w;
int h = image->h-1;
int alpha = FZ_EXPAND(rgba[3]);
unsigned int rb = rgba[0] | (rgba[2] << 16);
unsigned int ga = rgba[1] | 0xFF0000;
unsigned int *dst32 = (unsigned int *)(void *)dst;
 
if (alpha == 0)
return;
if (alpha != 256)
{
while (len--)
{
unsigned int ca, drb, dga, crb, cga;
unsigned int a, b;
cov += *src; *src = 0; src++;
drb = *dst32++;
ca = FZ_COMBINE(FZ_EXPAND(cov), alpha);
if (ca != 0)
{
int ui, ui1, vi, vi1, ud, vd;
/* a = samplemask(samples, w, h, u, v); */
ui1 = 1;
ui = u >> 16;
if (ui < 0)
{
ui = 0;
ui1 = 0;
}
else if (ui >= w-1)
{
ui = w-1;
ui1 = 0;
}
vi1 = w;
vi = v >> 16;
if (vi < 0)
{
vi = 0;
vi1 = 0;
}
else if (vi >= h)
{
vi = h;
vi1 = 0;
}
ui += vi*w;
a = samples[ui];
b = samples[ui + ui1];
a |= samples[ui + vi1]<<16;
b |= samples[ui + ui1 + vi1]<<16;
ud = (u>>8) & 0xFF;
vd = (v>>8) & 0xFF;
ud = FZ_EXPAND(ud);
vd = FZ_EXPAND(vd);
/* a = blend(a,b,ud) */
a = ((b-a) * ud + (a<<8)) & MASK;
/* a = blend(a,a>>16,vd) */
a = (((a>>24)-(a>>8)) * vd + a);
a = (a>>8) & 0xFF;
ca = FZ_COMBINE(ca, FZ_EXPAND(a));
}
if (ca != 0)
{
dga = drb & MASK;
drb = (drb<<8) & MASK;
cga = ga - (dga>>8);
crb = rb - (drb>>8);
dga += cga * ca;
drb += crb * ca;
dga &= MASK;
drb &= MASK;
drb = dga | (drb>>8);
dst32[-1] = drb;
}
u += fa;
v += fb;
}
}
else
{
while (len--)
{
unsigned int ca, drb, dga, crb, cga;
unsigned int a, b;
cov += *src; *src = 0; src++;
drb = *dst32++;
if (cov != 0)
{
int ui, ui1, vi, vi1, ud, vd;
/* a = samplemask(samples, w, h, u, v); */
ui1 = 1;
ui = u >> 16;
if (ui < 0)
{
ui = 0;
ui1 = 0;
}
else if (ui >= w-1)
{
ui = w-1;
ui1 = 0;
}
vi1 = w;
vi = v >> 16;
if (vi < 0)
{
vi = 0;
vi1 = 0;
}
else if (vi >= h)
{
vi = h;
vi1 = 0;
}
ui += vi*w;
a = samples[ui];
b = samples[ui + ui1];
a |= samples[ui + vi1]<<16;
b |= samples[ui + ui1 + vi1]<<16;
ud = (u>>8) & 0xFF;
vd = (v>>8) & 0xFF;
ud = FZ_EXPAND(ud);
vd = FZ_EXPAND(vd);
/* a = blend(a,b,ud) */
a = ((b-a) * ud + (a<<8)) & MASK;
/* a = blend(a,a>>16,vd) */
a = (((a>>24)-(a>>8)) * vd + a);
a = (a>>8) & 0xFF;
ca = FZ_COMBINE(FZ_EXPAND(cov),FZ_EXPAND(a));
if (ca != 0)
{
if (ca == 256)
{
drb = (ga<<8) | rb;
}
else
{
dga = drb & MASK;
drb = (drb<<8) & MASK;
cga = ga - (dga>>8);
crb = rb - (drb>>8);
dga += cga * ca;
drb += crb * ca;
dga &= MASK;
drb &= MASK;
drb = dga | (drb>>8);
}
dst32[-1] = drb;
}
}
u += fa;
v += fb;
}
}
}
 
static void
img_1o1_32bit(byte * restrict src, byte cov, int len, byte * restrict dst,
fz_pixmap *image, int u, int v, int fa, int fb)
{
byte *samples = image->samples;
int w = image->w;
int h = image->h-1;
 
while (len--)
{
unsigned int a, b;
cov += *src; *src = 0; src++;
if (cov != 0)
{
int ui, ui1, vi, vi1, ud, vd;
/* sa = samplemask(samples, w, h, u, v); */
ui1 = 1;
ui = u >> 16;
if (ui < 0)
{
ui = 0;
ui1 = 0;
}
else if (ui >= w-1)
{
ui = w-1;
ui1 = 0;
}
vi1 = w;
vi = v >> 16;
if (vi < 0)
{
vi = 0;
vi1 = 0;
}
else if (vi >= h)
{
vi = h;
vi1 = 0;
}
ui += vi*w;
a = samples[ui];
b = samples[ui + ui1];
a |= samples[ui + vi1]<<16;
b |= samples[ui + ui1 + vi1]<<16;
ud = (u>>8) & 0xFF;
vd = (v>>8) & 0xFF;
ud = FZ_EXPAND(ud);
vd = FZ_EXPAND(vd);
/* a = blend(a,b,ud) */
a = ((b-a) * ud + (a<<8)) & MASK;
/* a = blend(a,a>>16,vd) */
a = (((a>>24)-(a>>8)) * vd + a);
a = (a>>8) & 0xFF;
a = FZ_COMBINE(FZ_EXPAND(a), FZ_EXPAND(cov));
if (a != 0)
{
if (a == 256)
dst[0] = 255;
else
dst[0] = FZ_BLEND(255, dst[0], a);
}
}
dst++;
u += fa;
v += fb;
}
}
 
#endif
 
void fz_accelerate(void)
{
if (sizeof(int) == 4 && sizeof(unsigned int) == 4 && !fz_is_big_endian())
{
// fz_path_w4i1o4 = path_w4i1o4_32bit;
// fz_text_w4i1o4 = text_w4i1o4_32bit;
// fz_img_4o4 = img_4o4_32bit;
// fz_img_w4i1o4 = img_w4i1o4_32bit;
// fz_img_1o1 = img_1o1_32bit;
}
 
#ifdef HAVE_CPUDEP
fz_accelerate_arch();
#endif
}
/contrib/media/updf/draw/draw_affine.c
0,0 → 1,588
#include "fitz.h"
 
typedef unsigned char byte;
 
static inline float roundup(float x)
{
return (x < 0) ? floorf(x) : ceilf(x);
}
 
static inline int lerp(int a, int b, int t)
{
return a + (((b - a) * t) >> 16);
}
 
static inline int bilerp(int a, int b, int c, int d, int u, int v)
{
return lerp(lerp(a, b, u), lerp(c, d, u), v);
}
 
static inline byte *sample_nearest(byte *s, int w, int h, int n, int u, int v)
{
if (u < 0) u = 0;
if (v < 0) v = 0;
if (u >= w) u = w - 1;
if (v >= h) v = h - 1;
return s + (v * w + u) * n;
}
 
/* Blend premultiplied source image in constant alpha over destination */
 
static inline void
fz_paint_affine_alpha_N_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *hp)
{
int k;
int n1 = n-1;
 
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
int uf = u & 0xffff;
int vf = v & 0xffff;
byte *a = sample_nearest(sp, sw, sh, n, ui, vi);
byte *b = sample_nearest(sp, sw, sh, n, ui+1, vi);
byte *c = sample_nearest(sp, sw, sh, n, ui, vi+1);
byte *d = sample_nearest(sp, sw, sh, n, ui+1, vi+1);
int xa = bilerp(a[n1], b[n1], c[n1], d[n1], uf, vf);
int t;
xa = fz_mul255(xa, alpha);
t = 255 - xa;
for (k = 0; k < n1; k++)
{
int x = bilerp(a[k], b[k], c[k], d[k], uf, vf);
dp[k] = fz_mul255(x, alpha) + fz_mul255(dp[k], t);
}
dp[n1] = xa + fz_mul255(dp[n1], t);
if (hp)
hp[0] = xa + fz_mul255(hp[n1], t);
}
dp += n;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
/* Special case code for gray -> rgb */
static inline void
fz_paint_affine_alpha_g2rgb_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int alpha, byte *hp)
{
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
int uf = u & 0xffff;
int vf = v & 0xffff;
byte *a = sample_nearest(sp, sw, sh, 2, ui, vi);
byte *b = sample_nearest(sp, sw, sh, 2, ui+1, vi);
byte *c = sample_nearest(sp, sw, sh, 2, ui, vi+1);
byte *d = sample_nearest(sp, sw, sh, 2, ui+1, vi+1);
int y = bilerp(a[1], b[1], c[1], d[1], uf, vf);
int x = bilerp(a[0], b[0], c[0], d[0], uf, vf);
int t;
x = fz_mul255(x, alpha);
y = fz_mul255(y, alpha);
t = 255 - y;
dp[0] = x + fz_mul255(dp[0], t);
dp[1] = x + fz_mul255(dp[1], t);
dp[2] = x + fz_mul255(dp[2], t);
dp[3] = y + fz_mul255(dp[3], t);
if (hp)
hp[0] = y + fz_mul255(hp[0], t);
}
dp += 4;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
static inline void
fz_paint_affine_alpha_N_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *hp)
{
int k;
int n1 = n-1;
 
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
byte *sample = sp + ((vi * sw + ui) * n);
int a = fz_mul255(sample[n-1], alpha);
int t = 255 - a;
for (k = 0; k < n1; k++)
dp[k] = fz_mul255(sample[k], alpha) + fz_mul255(dp[k], t);
dp[n1] = a + fz_mul255(dp[n1], t);
if (hp)
hp[0] = a + fz_mul255(hp[n1], t);
}
dp += n;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
static inline void
fz_paint_affine_alpha_g2rgb_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int alpha, byte *hp)
{
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
byte *sample = sp + ((vi * sw + ui) * 2);
int x = fz_mul255(sample[0], alpha);
int a = fz_mul255(sample[1], alpha);
int t = 255 - a;
dp[0] = x + fz_mul255(dp[0], t);
dp[1] = x + fz_mul255(dp[1], t);
dp[2] = x + fz_mul255(dp[2], t);
dp[3] = a + fz_mul255(dp[3], t);
if (hp)
hp[0] = a + fz_mul255(hp[0], t);
}
dp += 4;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
/* Blend premultiplied source image over destination */
 
static inline void
fz_paint_affine_N_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *hp)
{
int k;
int n1 = n-1;
 
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
int uf = u & 0xffff;
int vf = v & 0xffff;
byte *a = sample_nearest(sp, sw, sh, n, ui, vi);
byte *b = sample_nearest(sp, sw, sh, n, ui+1, vi);
byte *c = sample_nearest(sp, sw, sh, n, ui, vi+1);
byte *d = sample_nearest(sp, sw, sh, n, ui+1, vi+1);
int y = bilerp(a[n1], b[n1], c[n1], d[n1], uf, vf);
int t = 255 - y;
for (k = 0; k < n1; k++)
{
int x = bilerp(a[k], b[k], c[k], d[k], uf, vf);
dp[k] = x + fz_mul255(dp[k], t);
}
dp[n1] = y + fz_mul255(dp[n1], t);
if (hp)
hp[0] = y + fz_mul255(hp[0], t);
}
dp += n;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
static inline void
fz_paint_affine_solid_g2rgb_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, byte *hp)
{
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
int uf = u & 0xffff;
int vf = v & 0xffff;
byte *a = sample_nearest(sp, sw, sh, 2, ui, vi);
byte *b = sample_nearest(sp, sw, sh, 2, ui+1, vi);
byte *c = sample_nearest(sp, sw, sh, 2, ui, vi+1);
byte *d = sample_nearest(sp, sw, sh, 2, ui+1, vi+1);
int y = bilerp(a[1], b[1], c[1], d[1], uf, vf);
int t = 255 - y;
int x = bilerp(a[0], b[0], c[0], d[0], uf, vf);
dp[0] = x + fz_mul255(dp[0], t);
dp[1] = x + fz_mul255(dp[1], t);
dp[2] = x + fz_mul255(dp[2], t);
dp[3] = y + fz_mul255(dp[3], t);
if (hp)
hp[0] = y + fz_mul255(hp[0], t);
}
dp += 4;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
static inline void
fz_paint_affine_N_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *hp)
{
int k;
int n1 = n-1;
 
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
byte *sample = sp + ((vi * sw + ui) * n);
int a = sample[n1];
int t = 255 - a;
for (k = 0; k < n1; k++)
dp[k] = sample[k] + fz_mul255(dp[k], t);
dp[n1] = a + fz_mul255(dp[n1], t);
if (hp)
hp[0] = a + fz_mul255(hp[0], t);
}
dp += n;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
static inline void
fz_paint_affine_solid_g2rgb_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, byte *hp)
{
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
byte *sample = sp + ((vi * sw + ui) * 2);
int x = sample[0];
int a = sample[1];
int t = 255 - a;
dp[0] = x + fz_mul255(dp[0], t);
dp[1] = x + fz_mul255(dp[1], t);
dp[2] = x + fz_mul255(dp[2], t);
dp[3] = a + fz_mul255(dp[3], t);
if (hp)
hp[0] = a + fz_mul255(hp[0], t);
}
dp += 4;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
/* Blend non-premultiplied color in source image mask over destination */
 
static inline void
fz_paint_affine_color_N_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *color, byte *hp)
{
int n1 = n - 1;
int sa = color[n1];
int k;
 
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
int uf = u & 0xffff;
int vf = v & 0xffff;
byte *a = sample_nearest(sp, sw, sh, 1, ui, vi);
byte *b = sample_nearest(sp, sw, sh, 1, ui+1, vi);
byte *c = sample_nearest(sp, sw, sh, 1, ui, vi+1);
byte *d = sample_nearest(sp, sw, sh, 1, ui+1, vi+1);
int ma = bilerp(a[0], b[0], c[0], d[0], uf, vf);
int masa = FZ_COMBINE(FZ_EXPAND(ma), sa);
for (k = 0; k < n1; k++)
dp[k] = FZ_BLEND(color[k], dp[k], masa);
dp[n1] = FZ_BLEND(255, dp[n1], masa);
if (hp)
hp[0] = FZ_BLEND(255, hp[0], masa);
}
dp += n;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
static inline void
fz_paint_affine_color_N_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *color, byte *hp)
{
int n1 = n-1;
int sa = color[n1];
int k;
 
while (w--)
{
int ui = u >> 16;
int vi = v >> 16;
if (ui >= 0 && ui < sw && vi >= 0 && vi < sh)
{
int ma = sp[vi * sw + ui];
int masa = FZ_COMBINE(FZ_EXPAND(ma), sa);
for (k = 0; k < n1; k++)
dp[k] = FZ_BLEND(color[k], dp[k], masa);
dp[n1] = FZ_BLEND(255, dp[n1], masa);
if (hp)
hp[n1] = FZ_BLEND(255, hp[n1], masa);
}
dp += n;
if (hp)
hp++;
u += fa;
v += fb;
}
}
 
static void
fz_paint_affine_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused*/, byte *hp)
{
if (alpha == 255)
{
switch (n)
{
case 1: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 1, hp); break;
case 2: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 2, hp); break;
case 4: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 4, hp); break;
default: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, n, hp); break;
}
}
else if (alpha > 0)
{
switch (n)
{
case 1: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 1, alpha, hp); break;
case 2: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 2, alpha, hp); break;
case 4: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 4, alpha, hp); break;
default: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, n, alpha, hp); break;
}
}
}
 
static void
fz_paint_affine_g2rgb_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused*/, byte *hp)
{
if (alpha == 255)
{
fz_paint_affine_solid_g2rgb_lerp(dp, sp, sw, sh, u, v, fa, fb, w, hp);
}
else if (alpha > 0)
{
fz_paint_affine_alpha_g2rgb_lerp(dp, sp, sw, sh, u, v, fa, fb, w, alpha, hp);
}
}
 
static void
fz_paint_affine_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused */, byte *hp)
{
if (alpha == 255)
{
switch (n)
{
case 1: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 1, hp); break;
case 2: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 2, hp); break;
case 4: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 4, hp); break;
default: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, n, hp); break;
}
}
else if (alpha > 0)
{
switch (n)
{
case 1: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 1, alpha, hp); break;
case 2: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 2, alpha, hp); break;
case 4: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 4, alpha, hp); break;
default: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, n, alpha, hp); break;
}
}
}
 
static void
fz_paint_affine_g2rgb_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused*/, byte *hp)
{
if (alpha == 255)
{
fz_paint_affine_solid_g2rgb_near(dp, sp, sw, sh, u, v, fa, fb, w, hp);
}
else if (alpha > 0)
{
fz_paint_affine_alpha_g2rgb_near(dp, sp, sw, sh, u, v, fa, fb, w, alpha, hp);
}
}
 
static void
fz_paint_affine_color_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha/*unused*/, byte *color, byte *hp)
{
switch (n)
{
case 2: fz_paint_affine_color_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 2, color, hp); break;
case 4: fz_paint_affine_color_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 4, color, hp); break;
default: fz_paint_affine_color_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, n, color, hp); break;
}
}
 
static void
fz_paint_affine_color_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha/*unused*/, byte *color, byte *hp)
{
switch (n)
{
case 2: fz_paint_affine_color_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 2, color, hp); break;
case 4: fz_paint_affine_color_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 4, color, hp); break;
default: fz_paint_affine_color_N_near(dp, sp, sw, sh, u, v, fa, fb, w, n, color, hp); break;
}
}
 
/* Draw an image with an affine transform on destination */
 
static void
fz_paint_image_imp(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap *img, fz_matrix ctm, byte *color, int alpha)
{
byte *dp, *sp, *hp;
int u, v, fa, fb, fc, fd;
int x, y, w, h;
int sw, sh, n, hw;
fz_matrix inv;
fz_bbox bbox;
int dolerp;
void (*paintfn)(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color, byte *hp);
 
/* grid fit the image */
if (fz_is_rectilinear(ctm))
{
ctm.a = roundup(ctm.a);
ctm.b = roundup(ctm.b);
ctm.c = roundup(ctm.c);
ctm.d = roundup(ctm.d);
ctm.e = floorf(ctm.e);
ctm.f = floorf(ctm.f);
}
 
/* turn on interpolation for upscaled and non-rectilinear transforms */
dolerp = 0;
if (!fz_is_rectilinear(ctm))
dolerp = 1;
if (sqrtf(ctm.a * ctm.a + ctm.b * ctm.b) > img->w)
dolerp = 1;
if (sqrtf(ctm.c * ctm.c + ctm.d * ctm.d) > img->h)
dolerp = 1;
 
/* except when we shouldn't, at large magnifications */
if (!img->interpolate)
{
if (sqrtf(ctm.a * ctm.a + ctm.b * ctm.b) > img->w * 2)
dolerp = 0;
if (sqrtf(ctm.c * ctm.c + ctm.d * ctm.d) > img->h * 2)
dolerp = 0;
}
 
bbox = fz_round_rect(fz_transform_rect(ctm, fz_unit_rect));
bbox = fz_intersect_bbox(bbox, scissor);
x = bbox.x0;
y = bbox.y0;
w = bbox.x1 - bbox.x0;
h = bbox.y1 - bbox.y0;
 
/* map from screen space (x,y) to image space (u,v) */
inv = fz_scale(1.0f / img->w, -1.0f / img->h);
inv = fz_concat(inv, fz_translate(0, 1));
inv = fz_concat(inv, ctm);
inv = fz_invert_matrix(inv);
 
fa = inv.a * 65536;
fb = inv.b * 65536;
fc = inv.c * 65536;
fd = inv.d * 65536;
 
/* Calculate initial texture positions. Do a half step to start. */
u = (fa * x) + (fc * y) + inv.e * 65536 + ((fa + fc) >> 1);
v = (fb * x) + (fd * y) + inv.f * 65536 + ((fb + fd) >> 1);
 
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
n = dst->n;
sp = img->samples;
sw = img->w;
sh = img->h;
if (shape)
{
hw = shape->w;
hp = shape->samples + ((y - shape->y) * hw) + x - dst->x;
}
else
{
hw = 0;
hp = NULL;
}
 
/* TODO: if (fb == 0 && fa == 1) call fz_paint_span */
 
if (dst->n == 4 && img->n == 2)
{
assert(color == NULL);
if (dolerp)
paintfn = fz_paint_affine_g2rgb_lerp;
else
paintfn = fz_paint_affine_g2rgb_near;
}
else
{
if (dolerp)
{
if (color)
paintfn = fz_paint_affine_color_lerp;
else
paintfn = fz_paint_affine_lerp;
}
else
{
if (color)
paintfn = fz_paint_affine_color_near;
else
paintfn = fz_paint_affine_near;
}
}
 
while (h--)
{
paintfn(dp, sp, sw, sh, u, v, fa, fb, w, n, alpha, color, hp);
dp += dst->w * n;
hp += hw;
u += fc;
v += fd;
}
}
 
void
fz_paint_image_with_color(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap *img, fz_matrix ctm, byte *color)
{
assert(img->n == 1);
fz_paint_image_imp(dst, scissor, shape, img, ctm, color, 255);
}
 
void
fz_paint_image(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap *img, fz_matrix ctm, int alpha)
{
assert(dst->n == img->n || (dst->n == 4 && img->n == 2));
fz_paint_image_imp(dst, scissor, shape, img, ctm, NULL, alpha);
}
/contrib/media/updf/draw/draw_blend.c
0,0 → 1,567
#include "fitz.h"
 
/* PDF 1.4 blend modes. These are slow. */
 
typedef unsigned char byte;
 
static const char *fz_blendmode_names[] =
{
"Normal",
"Multiply",
"Screen",
"Overlay",
"Darken",
"Lighten",
"ColorDodge",
"ColorBurn",
"HardLight",
"SoftLight",
"Difference",
"Exclusion",
"Hue",
"Saturation",
"Color",
"Luminosity",
};
 
int fz_find_blendmode(char *name)
{
int i;
for (i = 0; i < nelem(fz_blendmode_names); i++)
if (!strcmp(name, fz_blendmode_names[i]))
return i;
return FZ_BLEND_NORMAL;
}
 
char *fz_blendmode_name(int blendmode)
{
if (blendmode >= 0 && blendmode < nelem(fz_blendmode_names))
return (char*)fz_blendmode_names[blendmode];
return "Normal";
}
 
/* Separable blend modes */
 
static inline int fz_screen_byte(int b, int s)
{
return b + s - fz_mul255(b, s);
}
 
static inline int fz_hard_light_byte(int b, int s)
{
int s2 = s << 1;
if (s <= 127)
return fz_mul255(b, s2);
else
return fz_screen_byte(b, s2 - 255);
}
 
static inline int fz_overlay_byte(int b, int s)
{
return fz_hard_light_byte(s, b); /* note swapped order */
}
 
static inline int fz_darken_byte(int b, int s)
{
return MIN(b, s);
}
 
static inline int fz_lighten_byte(int b, int s)
{
return MAX(b, s);
}
 
static inline int fz_color_dodge_byte(int b, int s)
{
s = 255 - s;
if (b == 0)
return 0;
else if (b >= s)
return 255;
else
return (0x1fe * b + s) / (s << 1);
}
 
static inline int fz_color_burn_byte(int b, int s)
{
b = 255 - b;
if (b == 0)
return 255;
else if (b >= s)
return 0;
else
return 0xff - (0x1fe * b + s) / (s << 1);
}
 
static inline int fz_soft_light_byte(int b, int s)
{
/* review this */
if (s < 128) {
return b - fz_mul255(fz_mul255((255 - (s<<1)), b), 255 - b);
}
else {
int dbd;
if (b < 64)
dbd = fz_mul255(fz_mul255((b << 4) - 12, b) + 4, b);
else
dbd = (int)sqrtf(255.0f * b);
return b + fz_mul255(((s<<1) - 255), (dbd - b));
}
}
 
static inline int fz_difference_byte(int b, int s)
{
return ABS(b - s);
}
 
static inline int fz_exclusion_byte(int b, int s)
{
return b + s - (fz_mul255(b, s)<<1);
}
 
/* Non-separable blend modes */
 
static void
fz_luminosity_rgb(int *rd, int *gd, int *bd, int rb, int gb, int bb, int rs, int gs, int bs)
{
int delta, scale;
int r, g, b, y;
 
/* 0.3, 0.59, 0.11 in fixed point */
delta = ((rs - rb) * 77 + (gs - gb) * 151 + (bs - bb) * 28 + 0x80) >> 8;
r = rb + delta;
g = gb + delta;
b = bb + delta;
 
if ((r | g | b) & 0x100)
{
y = (rs * 77 + gs * 151 + bs * 28 + 0x80) >> 8;
if (delta > 0)
{
int max;
max = MAX(r, MAX(g, b));
scale = ((255 - y) << 16) / (max - y);
}
else
{
int min;
min = MIN(r, MIN(g, b));
scale = (y << 16) / (y - min);
}
r = y + (((r - y) * scale + 0x8000) >> 16);
g = y + (((g - y) * scale + 0x8000) >> 16);
b = y + (((b - y) * scale + 0x8000) >> 16);
}
 
*rd = r;
*gd = g;
*bd = b;
}
 
static void
fz_saturation_rgb(int *rd, int *gd, int *bd, int rb, int gb, int bb, int rs, int gs, int bs)
{
int minb, maxb;
int mins, maxs;
int y;
int scale;
int r, g, b;
 
minb = MIN(rb, MIN(gb, bb));
maxb = MAX(rb, MAX(gb, bb));
if (minb == maxb)
{
/* backdrop has zero saturation, avoid divide by 0 */
*rd = gb;
*gd = gb;
*bd = gb;
return;
}
 
mins = MIN(rs, MIN(gs, bs));
maxs = MAX(rs, MAX(gs, bs));
 
scale = ((maxs - mins) << 16) / (maxb - minb);
y = (rb * 77 + gb * 151 + bb * 28 + 0x80) >> 8;
r = y + ((((rb - y) * scale) + 0x8000) >> 16);
g = y + ((((gb - y) * scale) + 0x8000) >> 16);
b = y + ((((bb - y) * scale) + 0x8000) >> 16);
 
if ((r | g | b) & 0x100)
{
int scalemin, scalemax;
int min, max;
 
min = MIN(r, MIN(g, b));
max = MAX(r, MAX(g, b));
 
if (min < 0)
scalemin = (y << 16) / (y - min);
else
scalemin = 0x10000;
 
if (max > 255)
scalemax = ((255 - y) << 16) / (max - y);
else
scalemax = 0x10000;
 
scale = MIN(scalemin, scalemax);
r = y + (((r - y) * scale + 0x8000) >> 16);
g = y + (((g - y) * scale + 0x8000) >> 16);
b = y + (((b - y) * scale + 0x8000) >> 16);
}
 
*rd = r;
*gd = g;
*bd = b;
}
 
static void
fz_color_rgb(int *rr, int *rg, int *rb, int br, int bg, int bb, int sr, int sg, int sb)
{
fz_luminosity_rgb(rr, rg, rb, sr, sg, sb, br, bg, bb);
}
 
static void
fz_hue_rgb(int *rr, int *rg, int *rb, int br, int bg, int bb, int sr, int sg, int sb)
{
int tr, tg, tb;
fz_luminosity_rgb(&tr, &tg, &tb, sr, sg, sb, br, bg, bb);
fz_saturation_rgb(rr, rg, rb, tr, tg, tb, br, bg, bb);
}
 
/* Blending loops */
 
void
fz_blend_separable(byte * restrict bp, byte * restrict sp, int n, int w, int blendmode)
{
int k;
int n1 = n - 1;
while (w--)
{
int sa = sp[n1];
int ba = bp[n1];
int saba = fz_mul255(sa, ba);
 
/* ugh, division to get non-premul components */
int invsa = sa ? 255 * 256 / sa : 0;
int invba = ba ? 255 * 256 / ba : 0;
 
for (k = 0; k < n1; k++)
{
int sc = (sp[k] * invsa) >> 8;
int bc = (bp[k] * invba) >> 8;
int rc;
 
switch (blendmode)
{
default:
case FZ_BLEND_NORMAL: rc = sc; break;
case FZ_BLEND_MULTIPLY: rc = fz_mul255(bc, sc); break;
case FZ_BLEND_SCREEN: rc = fz_screen_byte(bc, sc); break;
case FZ_BLEND_OVERLAY: rc = fz_overlay_byte(bc, sc); break;
case FZ_BLEND_DARKEN: rc = fz_darken_byte(bc, sc); break;
case FZ_BLEND_LIGHTEN: rc = fz_lighten_byte(bc, sc); break;
case FZ_BLEND_COLOR_DODGE: rc = fz_color_dodge_byte(bc, sc); break;
case FZ_BLEND_COLOR_BURN: rc = fz_color_burn_byte(bc, sc); break;
case FZ_BLEND_HARD_LIGHT: rc = fz_hard_light_byte(bc, sc); break;
case FZ_BLEND_SOFT_LIGHT: rc = fz_soft_light_byte(bc, sc); break;
case FZ_BLEND_DIFFERENCE: rc = fz_difference_byte(bc, sc); break;
case FZ_BLEND_EXCLUSION: rc = fz_exclusion_byte(bc, sc); break;
}
 
bp[k] = fz_mul255(255 - sa, bp[k]) + fz_mul255(255 - ba, sp[k]) + fz_mul255(saba, rc);
}
 
bp[k] = ba + sa - saba;
 
sp += n;
bp += n;
}
}
 
void
fz_blend_nonseparable(byte * restrict bp, byte * restrict sp, int w, int blendmode)
{
while (w--)
{
int rr, rg, rb;
 
int sa = sp[3];
int ba = bp[3];
int saba = fz_mul255(sa, ba);
 
/* ugh, division to get non-premul components */
int invsa = sa ? 255 * 256 / sa : 0;
int invba = ba ? 255 * 256 / ba : 0;
 
int sr = (sp[0] * invsa) >> 8;
int sg = (sp[1] * invsa) >> 8;
int sb = (sp[2] * invsa) >> 8;
 
int br = (bp[0] * invba) >> 8;
int bg = (bp[1] * invba) >> 8;
int bb = (bp[2] * invba) >> 8;
 
switch (blendmode)
{
default:
case FZ_BLEND_HUE:
fz_hue_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
case FZ_BLEND_SATURATION:
fz_saturation_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
case FZ_BLEND_COLOR:
fz_color_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
case FZ_BLEND_LUMINOSITY:
fz_luminosity_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
}
 
bp[0] = fz_mul255(255 - sa, bp[0]) + fz_mul255(255 - ba, sp[0]) + fz_mul255(saba, rr);
bp[1] = fz_mul255(255 - sa, bp[1]) + fz_mul255(255 - ba, sp[1]) + fz_mul255(saba, rg);
bp[2] = fz_mul255(255 - sa, bp[2]) + fz_mul255(255 - ba, sp[2]) + fz_mul255(saba, rb);
bp[3] = ba + sa - saba;
 
sp += 4;
bp += 4;
}
}
 
static void
fz_blend_separable_nonisolated(byte * restrict bp, byte * restrict sp, int n, int w, int blendmode, byte * restrict hp, int alpha)
{
int k;
int n1 = n - 1;
 
if (alpha == 255 && blendmode == 0)
{
/* In this case, the uncompositing and the recompositing
* cancel one another out, and it's just a simple copy. */
/* FIXME: Maybe we can avoid using the shape plane entirely
* and just copy? */
while (w--)
{
int ha = fz_mul255(*hp++, alpha); /* ha = shape_alpha */
/* If ha == 0 then leave everything unchanged */
if (ha != 0)
{
for (k = 0; k < n; k++)
{
bp[k] = sp[k];
}
}
 
sp += n;
bp += n;
}
return;
}
while (w--)
{
int ha = *hp++;
int haa = fz_mul255(ha, alpha); /* ha = shape_alpha */
/* If haa == 0 then leave everything unchanged */
if (haa != 0)
{
int sa = sp[n1];
int ba = bp[n1];
int baha = fz_mul255(ba, haa);
 
/* ugh, division to get non-premul components */
int invsa = sa ? 255 * 256 / sa : 0;
int invba = ba ? 255 * 256 / ba : 0;
 
/* Calculate result_alpha */
int ra = bp[n1] = ba - baha + haa;
 
/* Because we are a non-isolated group, we need to
* 'uncomposite' before we blend (recomposite).
* We assume that normal blending has been done inside
* the group, so: ra.rc = (1-ha).bc + ha.sc
* A bit of rearrangement, and that gives us that:
* sc = (ra.rc - bc)/ha + bc
* Now, the result of the blend was stored in src, so:
*/
int invha = ha ? 255 * 256 / ha : 0;
 
if (ra != 0) for (k = 0; k < n1; k++)
{
int sc = (sp[k] * invsa) >> 8;
int bc = (bp[k] * invba) >> 8;
int rc;
 
/* Uncomposite */
sc = (((sc-bc)*invha)>>8) + bc;
if (sc < 0) sc = 0;
if (sc > 255) sc = 255;
 
switch (blendmode)
{
default:
case FZ_BLEND_NORMAL: rc = sc; break;
case FZ_BLEND_MULTIPLY: rc = fz_mul255(bc, sc); break;
case FZ_BLEND_SCREEN: rc = fz_screen_byte(bc, sc); break;
case FZ_BLEND_OVERLAY: rc = fz_overlay_byte(bc, sc); break;
case FZ_BLEND_DARKEN: rc = fz_darken_byte(bc, sc); break;
case FZ_BLEND_LIGHTEN: rc = fz_lighten_byte(bc, sc); break;
case FZ_BLEND_COLOR_DODGE: rc = fz_color_dodge_byte(bc, sc); break;
case FZ_BLEND_COLOR_BURN: rc = fz_color_burn_byte(bc, sc); break;
case FZ_BLEND_HARD_LIGHT: rc = fz_hard_light_byte(bc, sc); break;
case FZ_BLEND_SOFT_LIGHT: rc = fz_soft_light_byte(bc, sc); break;
case FZ_BLEND_DIFFERENCE: rc = fz_difference_byte(bc, sc); break;
case FZ_BLEND_EXCLUSION: rc = fz_exclusion_byte(bc, sc); break;
}
rc = fz_mul255(255 - haa, bc) + fz_mul255(fz_mul255(255 - ba, sc), haa) + fz_mul255(baha, rc);
if (rc < 0) rc = 0;
if (rc > 255) rc = 255;
bp[k] = fz_mul255(rc, ra);
}
}
 
sp += n;
bp += n;
}
}
 
static void
fz_blend_nonseparable_nonisolated(byte * restrict bp, byte * restrict sp, int w, int blendmode, byte * restrict hp, int alpha)
{
while (w--)
{
int ha = *hp++;
int haa = fz_mul255(ha, alpha);
if (haa != 0)
{
int sa = sp[3];
int ba = bp[3];
int baha = fz_mul255(ba, haa);
 
/* Calculate result_alpha */
int ra = bp[3] = ba - baha + haa;
if (ra != 0)
{
/* Because we are a non-isolated group, we
* need to 'uncomposite' before we blend
* (recomposite). We assume that normal
* blending has been done inside the group,
* so: ra.rc = (1-ha).bc + ha.sc
* A bit of rearrangement, and that gives us
* that: sc = (ra.rc - bc)/ha + bc
* Now, the result of the blend was stored in
* src, so: */
int invha = ha ? 255 * 256 / ha : 0;
 
int rr, rg, rb;
 
/* ugh, division to get non-premul components */
int invsa = sa ? 255 * 256 / sa : 0;
int invba = ba ? 255 * 256 / ba : 0;
 
int sr = (sp[0] * invsa) >> 8;
int sg = (sp[1] * invsa) >> 8;
int sb = (sp[2] * invsa) >> 8;
 
int br = (bp[0] * invba) >> 8;
int bg = (bp[1] * invba) >> 8;
int bb = (bp[2] * invba) >> 8;
 
/* Uncomposite */
sr = (((sr-br)*invha)>>8) + br;
sg = (((sg-bg)*invha)>>8) + bg;
sb = (((sb-bb)*invha)>>8) + bb;
 
switch (blendmode)
{
default:
case FZ_BLEND_HUE:
fz_hue_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
case FZ_BLEND_SATURATION:
fz_saturation_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
case FZ_BLEND_COLOR:
fz_color_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
case FZ_BLEND_LUMINOSITY:
fz_luminosity_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb);
break;
}
 
rr = fz_mul255(255 - haa, bp[0]) + fz_mul255(fz_mul255(255 - ba, sr), haa) + fz_mul255(baha, rr);
rg = fz_mul255(255 - haa, bp[1]) + fz_mul255(fz_mul255(255 - ba, sg), haa) + fz_mul255(baha, rg);
rb = fz_mul255(255 - haa, bp[2]) + fz_mul255(fz_mul255(255 - ba, sb), haa) + fz_mul255(baha, rb);
bp[0] = fz_mul255(ra, rr);
bp[1] = fz_mul255(ra, rg);
bp[2] = fz_mul255(ra, rb);
}
}
 
sp += 4;
bp += 4;
}
}
 
void
fz_blend_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha, int blendmode, int isolated, fz_pixmap *shape)
{
unsigned char *sp, *dp;
fz_bbox bbox;
int x, y, w, h, n;
 
/* TODO: fix this hack! */
if (isolated && alpha < 255)
{
sp = src->samples;
n = src->w * src->h * src->n;
while (n--)
{
*sp = fz_mul255(*sp, alpha);
sp++;
}
}
 
bbox = fz_bound_pixmap(dst);
bbox = fz_intersect_bbox(bbox, fz_bound_pixmap(src));
 
x = bbox.x0;
y = bbox.y0;
w = bbox.x1 - bbox.x0;
h = bbox.y1 - bbox.y0;
 
n = src->n;
sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * n;
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * n;
 
assert(src->n == dst->n);
 
if (!isolated)
{
unsigned char *hp = shape->samples + (y - shape->y) * shape->w + (x - shape->x);
 
while (h--)
{
if (n == 4 && blendmode >= FZ_BLEND_HUE)
fz_blend_nonseparable_nonisolated(dp, sp, w, blendmode, hp, alpha);
else
fz_blend_separable_nonisolated(dp, sp, n, w, blendmode, hp, alpha);
sp += src->w * n;
dp += dst->w * n;
hp += shape->w;
}
}
else
{
while (h--)
{
if (n == 4 && blendmode >= FZ_BLEND_HUE)
fz_blend_nonseparable(dp, sp, w, blendmode);
else
fz_blend_separable(dp, sp, n, w, blendmode);
sp += src->w * n;
dp += dst->w * n;
}
}
}
/contrib/media/updf/draw/draw_device.c
0,0 → 1,1582
#include "fitz.h"
 
#define QUANT(x,a) (((int)((x) * (a))) / (a))
#define HSUBPIX 5.0
#define VSUBPIX 5.0
 
#define STACK_SIZE 96
 
/* Enable the following to attempt to support knockout and/or isolated
* blending groups. This code is known to give incorrect results currently
* so disabled by default. See bug 692377. */
#undef ATTEMPT_KNOCKOUT_AND_ISOLATED
 
/* Enable the following to help debug group blending. */
#undef DUMP_GROUP_BLENDS
 
/* Note #1: At various points in this code (notably when clipping with non
* rectangular masks), we create a new (empty) destination pixmap. We then
* render this pixmap, then plot it back into the original destination
* through a mask. This works well for normal blending, but falls down for
* non-zero blending modes; effectively we are forcing ourselves to use an
* isolated group.
*
* The fix for this would be to copy the contents from the underlying dest
* into the newly created dest. This would enable us to use a non
* FZ_BLEND_ISOLATED blendmode. Unfortunately, tt would break tiling, as
* we could no longer render once and blend back multiple times.
*/
 
 
typedef struct fz_draw_device_s fz_draw_device;
 
enum {
FZ_DRAWDEV_FLAGS_TYPE3 = 1,
};
 
struct fz_draw_device_s
{
fz_glyph_cache *cache;
fz_gel *gel;
 
fz_pixmap *dest;
fz_pixmap *shape;
fz_bbox scissor;
 
int flags;
int top;
int blendmode;
struct {
fz_bbox scissor;
fz_pixmap *dest;
fz_pixmap *mask;
fz_pixmap *shape;
int blendmode;
int luminosity;
float alpha;
fz_matrix ctm;
float xstep, ystep;
fz_rect area;
} stack[STACK_SIZE];
};
 
#ifdef DUMP_GROUP_BLENDS
static int group_dump_count = 0;
 
static void fz_dump_blend(fz_pixmap *pix, const char *s)
{
char name[80];
 
if (pix == NULL)
return;
 
sprintf(name, "dump%02d.png", group_dump_count);
if (s)
printf("%s%02d", s, group_dump_count);
group_dump_count++;
 
fz_write_png(pix, name, (pix->n > 1));
}
 
static void dump_spaces(int x, const char *s)
{
int i;
for (i = 0; i < x; i++)
printf(" ");
printf("%s", s);
}
 
#endif
 
static void fz_knockout_begin(void *user)
{
fz_draw_device *dev = user;
fz_bbox bbox;
fz_pixmap *dest, *shape;
int isolated = dev->blendmode & FZ_BLEND_ISOLATED;
 
if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0)
return;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
bbox = fz_bound_pixmap(dev->dest);
bbox = fz_intersect_bbox(bbox, dev->scissor);
dest = fz_new_pixmap_with_rect(dev->dest->colorspace, bbox);
 
if (isolated)
{
fz_clear_pixmap(dest);
}
else
{
fz_pixmap *prev;
int i = dev->top;
do
prev = dev->stack[--i].dest;
while (prev == NULL);
fz_copy_pixmap_rect(dest, prev, bbox);
}
 
if (dev->blendmode == 0 && isolated)
{
/* We can render direct to any existing shape plane. If there
* isn't one, we don't need to make one. */
shape = dev->shape;
}
else
{
shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(shape);
}
dev->stack[dev->top].blendmode = dev->blendmode;
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Knockout begin\n");
#endif
dev->top++;
 
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
dev->blendmode &= ~FZ_BLEND_MODEMASK;
}
 
static void fz_knockout_end(void *user)
{
fz_draw_device *dev = user;
fz_pixmap *group = dev->dest;
fz_pixmap *shape = dev->shape;
int blendmode;
int isolated;
 
if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0)
return;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
if (dev->top > 0)
{
dev->top--;
blendmode = dev->blendmode & FZ_BLEND_MODEMASK;
isolated = dev->blendmode & FZ_BLEND_ISOLATED;
dev->blendmode = dev->stack[dev->top].blendmode;
dev->shape = dev->stack[dev->top].shape;
dev->dest = dev->stack[dev->top].dest;
dev->scissor = dev->stack[dev->top].scissor;
 
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "");
fz_dump_blend(group, "Blending ");
if (shape)
fz_dump_blend(shape, "/");
fz_dump_blend(dev->dest, " onto ");
if (dev->shape)
fz_dump_blend(dev->shape, "/");
if (blendmode != 0)
printf(" (blend %d)", blendmode);
if (isolated != 0)
printf(" (isolated)");
printf(" (knockout)");
#endif
if ((blendmode == 0) && (shape == NULL))
fz_paint_pixmap(dev->dest, group, 255);
else
fz_blend_pixmap(dev->dest, group, 255, blendmode, isolated, shape);
 
fz_drop_pixmap(group);
if (shape != dev->shape)
{
if (dev->shape)
{
fz_paint_pixmap(dev->shape, shape, 255);
}
fz_drop_pixmap(shape);
}
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(dev->dest, " to get ");
if (dev->shape)
fz_dump_blend(dev->shape, "/");
printf("\n");
#endif
}
}
 
static void
fz_draw_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
float expansion = fz_matrix_expansion(ctm);
float flatness = 0.3f / expansion;
unsigned char colorbv[FZ_MAX_COLORS + 1];
float colorfv[FZ_MAX_COLORS];
fz_bbox bbox;
int i;
 
fz_reset_gel(dev->gel, dev->scissor);
fz_flatten_fill_path(dev->gel, path, ctm, flatness);
fz_sort_gel(dev->gel);
 
bbox = fz_bound_gel(dev->gel);
bbox = fz_intersect_bbox(bbox, dev->scissor);
 
if (fz_is_empty_rect(bbox))
return;
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
fz_convert_color(colorspace, color, model, colorfv);
for (i = 0; i < model->n; i++)
colorbv[i] = colorfv[i] * 255;
colorbv[i] = alpha * 255;
 
fz_scan_convert(dev->gel, even_odd, bbox, dev->dest, colorbv);
if (dev->shape)
{
fz_reset_gel(dev->gel, dev->scissor);
fz_flatten_fill_path(dev->gel, path, ctm, flatness);
fz_sort_gel(dev->gel);
 
colorbv[0] = alpha * 255;
fz_scan_convert(dev->gel, even_odd, bbox, dev->shape, colorbv);
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(dev);
}
 
static void
fz_draw_stroke_path(void *user, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
float expansion = fz_matrix_expansion(ctm);
float flatness = 0.3f / expansion;
float linewidth = stroke->linewidth;
unsigned char colorbv[FZ_MAX_COLORS + 1];
float colorfv[FZ_MAX_COLORS];
fz_bbox bbox;
int i;
 
if (linewidth * expansion < 0.1f)
linewidth = 1 / expansion;
 
fz_reset_gel(dev->gel, dev->scissor);
if (stroke->dash_len > 0)
fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
else
fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
fz_sort_gel(dev->gel);
 
bbox = fz_bound_gel(dev->gel);
bbox = fz_intersect_bbox(bbox, dev->scissor);
 
if (fz_is_empty_rect(bbox))
return;
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
fz_convert_color(colorspace, color, model, colorfv);
for (i = 0; i < model->n; i++)
colorbv[i] = colorfv[i] * 255;
colorbv[i] = alpha * 255;
 
fz_scan_convert(dev->gel, 0, bbox, dev->dest, colorbv);
if (dev->shape)
{
fz_reset_gel(dev->gel, dev->scissor);
if (stroke->dash_len > 0)
fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
else
fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
fz_sort_gel(dev->gel);
 
colorbv[0] = 255;
fz_scan_convert(dev->gel, 0, bbox, dev->shape, colorbv);
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(dev);
}
 
static void
fz_draw_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
float expansion = fz_matrix_expansion(ctm);
float flatness = 0.3f / expansion;
fz_pixmap *mask, *dest, *shape;
fz_bbox bbox;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
fz_reset_gel(dev->gel, dev->scissor);
fz_flatten_fill_path(dev->gel, path, ctm, flatness);
fz_sort_gel(dev->gel);
 
bbox = fz_bound_gel(dev->gel);
bbox = fz_intersect_bbox(bbox, dev->scissor);
if (rect)
bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));
 
if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel))
{
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = NULL;
dev->stack[dev->top].dest = NULL;
dev->stack[dev->top].shape = dev->shape;
dev->stack[dev->top].blendmode = dev->blendmode;
dev->scissor = bbox;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Clip (rectangular) begin\n");
#endif
dev->top++;
return;
}
 
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
/* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(shape);
}
else
shape = NULL;
 
fz_scan_convert(dev->gel, even_odd, bbox, mask, NULL);
 
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = mask;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
/* FIXME: See note #1 */
dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Clip (non-rectangular) begin\n");
#endif
dev->top++;
}
 
static void
fz_draw_clip_stroke_path(void *user, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
float expansion = fz_matrix_expansion(ctm);
float flatness = 0.3f / expansion;
float linewidth = stroke->linewidth;
fz_pixmap *mask, *dest, *shape;
fz_bbox bbox;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
if (linewidth * expansion < 0.1f)
linewidth = 1 / expansion;
 
fz_reset_gel(dev->gel, dev->scissor);
if (stroke->dash_len > 0)
fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
else
fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
fz_sort_gel(dev->gel);
 
bbox = fz_bound_gel(dev->gel);
bbox = fz_intersect_bbox(bbox, dev->scissor);
if (rect)
bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));
 
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
/* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(shape);
}
else
shape = NULL;
 
if (!fz_is_empty_rect(bbox))
fz_scan_convert(dev->gel, 0, bbox, mask, NULL);
 
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = mask;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
/* FIXME: See note #1 */
dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Clip (stroke) begin\n");
#endif
dev->top++;
}
 
static void
draw_glyph(unsigned char *colorbv, fz_pixmap *dst, fz_pixmap *msk,
int xorig, int yorig, fz_bbox scissor)
{
unsigned char *dp, *mp;
fz_bbox bbox;
int x, y, w, h;
 
bbox = fz_bound_pixmap(msk);
bbox.x0 += xorig;
bbox.y0 += yorig;
bbox.x1 += xorig;
bbox.y1 += yorig;
 
bbox = fz_intersect_bbox(bbox, scissor); /* scissor < dst */
x = bbox.x0;
y = bbox.y0;
w = bbox.x1 - bbox.x0;
h = bbox.y1 - bbox.y0;
 
mp = msk->samples + ((y - msk->y - yorig) * msk->w + (x - msk->x - xorig));
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
 
assert(msk->n == 1);
 
while (h--)
{
if (dst->colorspace)
fz_paint_span_with_color(dp, mp, dst->n, w, colorbv);
else
fz_paint_span(dp, mp, 1, w, 255);
dp += dst->w * dst->n;
mp += msk->w;
}
}
 
static void
fz_draw_fill_text(void *user, fz_text *text, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
unsigned char colorbv[FZ_MAX_COLORS + 1];
unsigned char shapebv;
float colorfv[FZ_MAX_COLORS];
fz_matrix tm, trm;
fz_pixmap *glyph;
int i, x, y, gid;
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
fz_convert_color(colorspace, color, model, colorfv);
for (i = 0; i < model->n; i++)
colorbv[i] = colorfv[i] * 255;
colorbv[i] = alpha * 255;
shapebv = 255;
 
tm = text->trm;
 
for (i = 0; i < text->len; i++)
{
gid = text->items[i].gid;
if (gid < 0)
continue;
 
tm.e = text->items[i].x;
tm.f = text->items[i].y;
trm = fz_concat(tm, ctm);
x = floorf(trm.e);
y = floorf(trm.f);
trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
 
glyph = fz_render_glyph(dev->cache, text->font, gid, trm, model);
if (glyph)
{
if (glyph->n == 1)
{
draw_glyph(colorbv, dev->dest, glyph, x, y, dev->scissor);
if (dev->shape)
draw_glyph(&shapebv, dev->shape, glyph, x, y, dev->scissor);
}
else
{
fz_matrix ctm = {glyph->w, 0.0, 0.0, -glyph->h, x + glyph->x, y + glyph->y + glyph->h};
fz_paint_image(dev->dest, dev->scissor, dev->shape, glyph, ctm, alpha * 255);
}
fz_drop_pixmap(glyph);
}
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(dev);
}
 
static void
fz_draw_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
unsigned char colorbv[FZ_MAX_COLORS + 1];
float colorfv[FZ_MAX_COLORS];
fz_matrix tm, trm;
fz_pixmap *glyph;
int i, x, y, gid;
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
fz_convert_color(colorspace, color, model, colorfv);
for (i = 0; i < model->n; i++)
colorbv[i] = colorfv[i] * 255;
colorbv[i] = alpha * 255;
 
tm = text->trm;
 
for (i = 0; i < text->len; i++)
{
gid = text->items[i].gid;
if (gid < 0)
continue;
 
tm.e = text->items[i].x;
tm.f = text->items[i].y;
trm = fz_concat(tm, ctm);
x = floorf(trm.e);
y = floorf(trm.f);
trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
 
glyph = fz_render_stroked_glyph(dev->cache, text->font, gid, trm, ctm, stroke);
if (glyph)
{
draw_glyph(colorbv, dev->dest, glyph, x, y, dev->scissor);
if (dev->shape)
draw_glyph(colorbv, dev->shape, glyph, x, y, dev->scissor);
fz_drop_pixmap(glyph);
}
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(dev);
}
 
static void
fz_draw_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
fz_bbox bbox;
fz_pixmap *mask, *dest, *shape;
fz_matrix tm, trm;
fz_pixmap *glyph;
int i, x, y, gid;
 
/* If accumulate == 0 then this text object is guaranteed complete */
/* If accumulate == 1 then this text object is the first (or only) in a sequence */
/* If accumulate == 2 then this text object is a continuation */
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
if (accumulate == 0)
{
/* make the mask the exact size needed */
bbox = fz_round_rect(fz_bound_text(text, ctm));
bbox = fz_intersect_bbox(bbox, dev->scissor);
}
else
{
/* be conservative about the size of the mask needed */
bbox = dev->scissor;
}
 
if (accumulate == 0 || accumulate == 1)
{
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
/* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(shape);
}
else
shape = NULL;
 
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = mask;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
/* FIXME: See note #1 */
dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Clip (text) begin\n");
#endif
dev->top++;
}
else
{
mask = dev->stack[dev->top-1].mask;
}
 
if (!fz_is_empty_rect(bbox))
{
tm = text->trm;
 
for (i = 0; i < text->len; i++)
{
gid = text->items[i].gid;
if (gid < 0)
continue;
 
tm.e = text->items[i].x;
tm.f = text->items[i].y;
trm = fz_concat(tm, ctm);
x = floorf(trm.e);
y = floorf(trm.f);
trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
 
glyph = fz_render_glyph(dev->cache, text->font, gid, trm, model);
if (glyph)
{
draw_glyph(NULL, mask, glyph, x, y, bbox);
if (dev->shape)
draw_glyph(NULL, dev->shape, glyph, x, y, bbox);
fz_drop_pixmap(glyph);
}
}
}
}
 
static void
fz_draw_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
fz_bbox bbox;
fz_pixmap *mask, *dest, *shape;
fz_matrix tm, trm;
fz_pixmap *glyph;
int i, x, y, gid;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
/* make the mask the exact size needed */
bbox = fz_round_rect(fz_bound_text(text, ctm));
bbox = fz_intersect_bbox(bbox, dev->scissor);
 
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
/* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(shape);
}
else
shape = dev->shape;
 
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = mask;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
/* FIXME: See note #1 */
dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Clip (stroke text) begin\n");
#endif
dev->top++;
 
if (!fz_is_empty_rect(bbox))
{
tm = text->trm;
 
for (i = 0; i < text->len; i++)
{
gid = text->items[i].gid;
if (gid < 0)
continue;
 
tm.e = text->items[i].x;
tm.f = text->items[i].y;
trm = fz_concat(tm, ctm);
x = floorf(trm.e);
y = floorf(trm.f);
trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
 
glyph = fz_render_stroked_glyph(dev->cache, text->font, gid, trm, ctm, stroke);
if (glyph)
{
draw_glyph(NULL, mask, glyph, x, y, bbox);
if (dev->shape)
draw_glyph(NULL, dev->shape, glyph, x, y, bbox);
fz_drop_pixmap(glyph);
}
}
}
}
 
static void
fz_draw_ignore_text(void *user, fz_text *text, fz_matrix ctm)
{
}
 
static void
fz_draw_fill_shade(void *user, fz_shade *shade, fz_matrix ctm, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
fz_pixmap *dest = dev->dest;
fz_rect bounds;
fz_bbox bbox, scissor;
float colorfv[FZ_MAX_COLORS];
unsigned char colorbv[FZ_MAX_COLORS + 1];
 
bounds = fz_bound_shade(shade, ctm);
bbox = fz_intersect_bbox(fz_round_rect(bounds), dev->scissor);
scissor = dev->scissor;
 
// TODO: proper clip by shade->bbox
 
if (fz_is_empty_rect(bbox))
return;
 
if (!model)
{
fz_warn("cannot render shading directly to an alpha mask");
return;
}
 
if (alpha < 1)
{
dest = fz_new_pixmap_with_rect(dev->dest->colorspace, bbox);
fz_clear_pixmap(dest);
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
if (shade->use_background)
{
unsigned char *s;
int x, y, n, i;
fz_convert_color(shade->colorspace, shade->background, model, colorfv);
for (i = 0; i < model->n; i++)
colorbv[i] = colorfv[i] * 255;
colorbv[i] = 255;
 
n = dest->n;
for (y = scissor.y0; y < scissor.y1; y++)
{
s = dest->samples + ((scissor.x0 - dest->x) + (y - dest->y) * dest->w) * dest->n;
for (x = scissor.x0; x < scissor.x1; x++)
{
for (i = 0; i < n; i++)
*s++ = colorbv[i];
}
}
if (dev->shape)
{
for (y = scissor.y0; y < scissor.y1; y++)
{
s = dev->shape->samples + (scissor.x0 - dev->shape->x) + (y - dev->shape->y) * dev->shape->w;
for (x = scissor.x0; x < scissor.x1; x++)
{
*s++ = 255;
}
}
}
}
 
fz_paint_shade(shade, ctm, dest, bbox);
if (dev->shape)
fz_clear_pixmap_rect_with_color(dev->shape, 255, bbox);
 
if (alpha < 1)
{
fz_paint_pixmap(dev->dest, dest, alpha * 255);
fz_drop_pixmap(dest);
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(dev);
}
 
static fz_pixmap *
fz_transform_pixmap(fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int dy, int gridfit)
{
fz_pixmap *scaled;
 
if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0)
{
/* Unrotated or X-flip or Y-flip or XY-flip */
scaled = fz_scale_pixmap_gridfit(image, ctm->e, ctm->f, ctm->a, ctm->d, gridfit);
if (scaled == NULL)
return NULL;
ctm->a = scaled->w;
ctm->d = scaled->h;
ctm->e = scaled->x;
ctm->f = scaled->y;
return scaled;
}
 
if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0)
{
/* Other orthogonal flip/rotation cases */
scaled = fz_scale_pixmap_gridfit(image, ctm->f, ctm->e, ctm->b, ctm->c, gridfit);
if (scaled == NULL)
return NULL;
ctm->b = scaled->w;
ctm->c = scaled->h;
ctm->f = scaled->x;
ctm->e = scaled->y;
return scaled;
}
 
/* Downscale, non rectilinear case */
if (dx > 0 && dy > 0)
{
scaled = fz_scale_pixmap(image, 0, 0, (float)dx, (float)dy);
return scaled;
}
 
return NULL;
}
 
static void
fz_draw_fill_image(void *user, fz_pixmap *image, fz_matrix ctm, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
fz_pixmap *converted = NULL;
fz_pixmap *scaled = NULL;
int after;
int dx, dy;
 
if (!model)
{
fz_warn("cannot render image directly to an alpha mask");
return;
}
 
if (image->w == 0 || image->h == 0)
return;
 
/* convert images with more components (cmyk->rgb) before scaling */
/* convert images with fewer components (gray->rgb after scaling */
/* convert images with expensive colorspace transforms after scaling */
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
after = 0;
if (image->colorspace == fz_device_gray)
after = 1;
 
if (image->colorspace != model && !after)
{
converted = fz_new_pixmap_with_rect(model, fz_bound_pixmap(image));
fz_convert_pixmap(image, converted);
image = converted;
}
 
dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b);
dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d);
if (dx < image->w && dy < image->h)
{
int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
scaled = fz_transform_pixmap(image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit);
if (scaled == NULL)
{
if (dx < 1)
dx = 1;
if (dy < 1)
dy = 1;
scaled = fz_scale_pixmap(image, image->x, image->y, dx, dy);
}
if (scaled != NULL)
image = scaled;
}
 
if (image->colorspace != model)
{
if ((image->colorspace == fz_device_gray && model == fz_device_rgb) ||
(image->colorspace == fz_device_gray && model == fz_device_bgr))
{
/* We have special case rendering code for gray -> rgb/bgr */
}
else
{
converted = fz_new_pixmap_with_rect(model, fz_bound_pixmap(image));
fz_convert_pixmap(image, converted);
image = converted;
}
}
 
fz_paint_image(dev->dest, dev->scissor, dev->shape, image, ctm, alpha * 255);
 
if (scaled)
fz_drop_pixmap(scaled);
if (converted)
fz_drop_pixmap(converted);
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(dev);
}
 
static void
fz_draw_fill_image_mask(void *user, fz_pixmap *image, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
unsigned char colorbv[FZ_MAX_COLORS + 1];
float colorfv[FZ_MAX_COLORS];
fz_pixmap *scaled = NULL;
int dx, dy;
int i;
 
if (image->w == 0 || image->h == 0)
return;
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b);
dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d);
if (dx < image->w && dy < image->h)
{
int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
scaled = fz_transform_pixmap(image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit);
if (scaled == NULL)
{
if (dx < 1)
dx = 1;
if (dy < 1)
dy = 1;
scaled = fz_scale_pixmap(image, image->x, image->y, dx, dy);
}
if (scaled != NULL)
image = scaled;
}
 
fz_convert_color(colorspace, color, model, colorfv);
for (i = 0; i < model->n; i++)
colorbv[i] = colorfv[i] * 255;
colorbv[i] = alpha * 255;
 
fz_paint_image_with_color(dev->dest, dev->scissor, dev->shape, image, ctm, colorbv);
 
if (scaled)
fz_drop_pixmap(scaled);
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
}
 
static void
fz_draw_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix ctm)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
fz_bbox bbox;
fz_pixmap *mask, *dest, *shape;
fz_pixmap *scaled = NULL;
int dx, dy;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Clip (image mask) begin\n");
#endif
 
if (image->w == 0 || image->h == 0)
{
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = NULL;
dev->stack[dev->top].dest = NULL;
dev->stack[dev->top].blendmode = dev->blendmode;
dev->scissor = fz_empty_bbox;
dev->top++;
return;
}
 
bbox = fz_round_rect(fz_transform_rect(ctm, fz_unit_rect));
bbox = fz_intersect_bbox(bbox, dev->scissor);
if (rect)
bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));
 
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
/* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(shape);
}
else
shape = NULL;
 
dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b);
dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d);
if (dx < image->w && dy < image->h)
{
int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
scaled = fz_transform_pixmap(image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit);
if (scaled == NULL)
{
if (dx < 1)
dx = 1;
if (dy < 1)
dy = 1;
scaled = fz_scale_pixmap(image, image->x, image->y, dx, dy);
}
if (scaled != NULL)
image = scaled;
}
 
fz_paint_image(mask, bbox, dev->shape, image, ctm, 255);
 
if (scaled)
fz_drop_pixmap(scaled);
 
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = mask;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
/* FIXME: See note #1 */
dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
dev->top++;
}
 
static void
fz_draw_pop_clip(void *user)
{
fz_draw_device *dev = user;
fz_pixmap *mask, *dest, *shape;
if (dev->top > 0)
{
dev->top--;
dev->scissor = dev->stack[dev->top].scissor;
mask = dev->stack[dev->top].mask;
dest = dev->stack[dev->top].dest;
shape = dev->stack[dev->top].shape;
dev->blendmode = dev->stack[dev->top].blendmode;
 
/* We can get here with mask == NULL if the clipping actually
* resolved to a rectangle earlier. In this case, we will
* have a dest, and the shape will be unchanged.
*/
if (mask)
{
assert(dest);
 
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "");
fz_dump_blend(dev->dest, "Clipping ");
if (dev->shape)
fz_dump_blend(dev->shape, "/");
fz_dump_blend(dest, " onto ");
if (shape)
fz_dump_blend(shape, "/");
fz_dump_blend(mask, " with ");
#endif
fz_paint_pixmap_with_mask(dest, dev->dest, mask);
if (shape != NULL)
{
assert(shape != dev->shape);
fz_paint_pixmap_with_mask(shape, dev->shape, mask);
fz_drop_pixmap(dev->shape);
dev->shape = shape;
}
fz_drop_pixmap(mask);
fz_drop_pixmap(dev->dest);
dev->dest = dest;
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(dev->dest, " to get ");
if (dev->shape)
fz_dump_blend(dev->shape, "/");
printf("\n");
#endif
}
else
{
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Clip End\n");
#endif
assert(dest == NULL);
assert(shape == dev->shape);
}
}
}
 
static void
fz_draw_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *colorfv)
{
fz_draw_device *dev = user;
fz_pixmap *dest;
fz_pixmap *shape = dev->shape;
fz_bbox bbox;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
bbox = fz_round_rect(rect);
bbox = fz_intersect_bbox(bbox, dev->scissor);
dest = fz_new_pixmap_with_rect(fz_device_gray, bbox);
if (dev->shape)
{
/* FIXME: If we ever want to support AIS true, then we
* probably want to create a shape pixmap here, using:
* shape = fz_new_pixmap_with_rect(NULL, bbox);
* then, in the end_mask code, we create the mask from this
* rather than dest.
*/
shape = NULL;
}
 
if (luminosity)
{
float bc;
if (!colorspace)
colorspace = fz_device_gray;
fz_convert_color(colorspace, colorfv, fz_device_gray, &bc);
fz_clear_pixmap_with_color(dest, bc * 255);
if (shape)
fz_clear_pixmap_with_color(shape, 255);
}
else
{
fz_clear_pixmap(dest);
if (shape)
fz_clear_pixmap(shape);
}
 
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].luminosity = luminosity;
dev->stack[dev->top].shape = dev->shape;
dev->stack[dev->top].blendmode = dev->blendmode;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Mask begin\n");
#endif
dev->top++;
 
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
}
 
static void
fz_draw_end_mask(void *user)
{
fz_draw_device *dev = user;
fz_pixmap *mask = dev->dest;
fz_pixmap *maskshape = dev->shape;
fz_pixmap *temp, *dest;
fz_bbox bbox;
int luminosity;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
if (dev->top > 0)
{
/* pop soft mask buffer */
dev->top--;
luminosity = dev->stack[dev->top].luminosity;
dev->scissor = dev->stack[dev->top].scissor;
dev->dest = dev->stack[dev->top].dest;
dev->shape = dev->stack[dev->top].shape;
 
/* convert to alpha mask */
temp = fz_alpha_from_gray(mask, luminosity);
fz_drop_pixmap(mask);
fz_drop_pixmap(maskshape);
 
/* create new dest scratch buffer */
bbox = fz_bound_pixmap(temp);
dest = fz_new_pixmap_with_rect(dev->dest->colorspace, bbox);
/* FIXME: See note #1 */
fz_clear_pixmap(dest);
 
/* push soft mask as clip mask */
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].mask = temp;
dev->stack[dev->top].dest = dev->dest;
/* FIXME: See note #1 */
dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
/* If we have a shape, then it'll need to be masked with the
* clip mask when we pop. So create a new shape now. */
if (dev->shape)
{
dev->stack[dev->top].shape = dev->shape;
dev->shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(dev->shape);
}
dev->scissor = bbox;
dev->dest = dest;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Mask -> Clip\n");
#endif
dev->top++;
}
}
 
static void
fz_draw_begin_group(void *user, fz_rect rect, int isolated, int knockout, int blendmode, float alpha)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
fz_bbox bbox;
fz_pixmap *dest, *shape;
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
bbox = fz_round_rect(rect);
bbox = fz_intersect_bbox(bbox, dev->scissor);
dest = fz_new_pixmap_with_rect(model, bbox);
 
#ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED
knockout = 0;
isolated = 1;
#endif
 
if (isolated)
{
fz_clear_pixmap(dest);
}
else
{
fz_copy_pixmap_rect(dest, dev->dest, bbox);
}
 
if (blendmode == 0 && alpha == 1.0 && isolated)
{
/* We can render direct to any existing shape plane. If there
* isn't one, we don't need to make one. */
shape = dev->shape;
}
else
{
shape = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(shape);
}
 
dev->stack[dev->top].alpha = alpha;
dev->stack[dev->top].blendmode = dev->blendmode;
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Group Begin\n");
#endif
dev->top++;
 
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
dev->blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0);
}
 
static void
fz_draw_end_group(void *user)
{
fz_draw_device *dev = user;
fz_pixmap *group = dev->dest;
fz_pixmap *shape = dev->shape;
int blendmode;
int isolated;
float alpha;
 
if (dev->top > 0)
{
dev->top--;
alpha = dev->stack[dev->top].alpha;
blendmode = dev->blendmode & FZ_BLEND_MODEMASK;
isolated = dev->blendmode & FZ_BLEND_ISOLATED;
dev->blendmode = dev->stack[dev->top].blendmode;
dev->shape = dev->stack[dev->top].shape;
dev->dest = dev->stack[dev->top].dest;
dev->scissor = dev->stack[dev->top].scissor;
 
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "");
fz_dump_blend(group, "Blending ");
if (shape)
fz_dump_blend(shape, "/");
fz_dump_blend(dev->dest, " onto ");
if (dev->shape)
fz_dump_blend(dev->shape, "/");
if (alpha != 1.0f)
printf(" (alpha %g)", alpha);
if (blendmode != 0)
printf(" (blend %d)", blendmode);
if (isolated != 0)
printf(" (isolated)");
if (blendmode & FZ_BLEND_KNOCKOUT)
printf(" (knockout)");
#endif
if ((blendmode == 0) && (shape == NULL))
fz_paint_pixmap(dev->dest, group, alpha * 255);
else
fz_blend_pixmap(dev->dest, group, alpha * 255, blendmode, isolated, shape);
 
fz_drop_pixmap(group);
if (shape != dev->shape)
{
if (dev->shape)
{
fz_paint_pixmap(dev->shape, shape, alpha * 255);
}
fz_drop_pixmap(shape);
}
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(dev->dest, " to get ");
if (dev->shape)
fz_dump_blend(dev->shape, "/");
printf("\n");
#endif
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(dev);
}
 
static void
fz_draw_begin_tile(void *user, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm)
{
fz_draw_device *dev = user;
fz_colorspace *model = dev->dest->colorspace;
fz_pixmap *dest;
fz_bbox bbox;
 
/* area, view, xstep, ystep are in pattern space */
/* ctm maps from pattern space to device space */
 
if (dev->top == STACK_SIZE)
{
fz_warn("assert: too many buffers on stack");
return;
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
 
bbox = fz_round_rect(fz_transform_rect(ctm, view));
dest = fz_new_pixmap_with_rect(model, bbox);
/* FIXME: See note #1 */
fz_clear_pixmap(dest);
 
dev->stack[dev->top].scissor = dev->scissor;
dev->stack[dev->top].dest = dev->dest;
dev->stack[dev->top].shape = dev->shape;
/* FIXME: See note #1 */
dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
dev->stack[dev->top].xstep = xstep;
dev->stack[dev->top].ystep = ystep;
dev->stack[dev->top].area = area;
dev->stack[dev->top].ctm = ctm;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Tile begin\n");
#endif
dev->top++;
 
dev->scissor = bbox;
dev->dest = dest;
}
 
static void
fz_draw_end_tile(void *user)
{
fz_draw_device *dev = user;
fz_pixmap *tile = dev->dest;
float xstep, ystep;
fz_matrix ctm, ttm;
fz_rect area;
int x0, y0, x1, y1, x, y;
 
if (dev->top > 0)
{
dev->top--;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "Tile end\n");
#endif
xstep = dev->stack[dev->top].xstep;
ystep = dev->stack[dev->top].ystep;
area = dev->stack[dev->top].area;
ctm = dev->stack[dev->top].ctm;
dev->scissor = dev->stack[dev->top].scissor;
dev->dest = dev->stack[dev->top].dest;
dev->blendmode = dev->stack[dev->top].blendmode;
 
 
x0 = floorf(area.x0 / xstep);
y0 = floorf(area.y0 / ystep);
x1 = ceilf(area.x1 / xstep);
y1 = ceilf(area.y1 / ystep);
 
ctm.e = tile->x;
ctm.f = tile->y;
 
for (y = y0; y < y1; y++)
{
for (x = x0; x < x1; x++)
{
ttm = fz_concat(fz_translate(x * xstep, y * ystep), ctm);
tile->x = ttm.e;
tile->y = ttm.f;
fz_paint_pixmap_with_rect(dev->dest, tile, 255, dev->scissor);
}
}
 
fz_drop_pixmap(tile);
}
 
if (dev->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_begin(dev);
}
 
static void
fz_draw_free_user(void *user)
{
fz_draw_device *dev = user;
/* TODO: pop and free the stacks */
if (dev->top > 0)
fz_warn("items left on stack in draw device: %d", dev->top);
fz_free_gel(dev->gel);
fz_free(dev);
}
 
fz_device *
fz_new_draw_device(fz_glyph_cache *cache, fz_pixmap *dest)
{
fz_device *dev;
fz_draw_device *ddev = fz_malloc(sizeof(fz_draw_device));
ddev->cache = cache;
ddev->gel = fz_new_gel();
ddev->dest = dest;
ddev->shape = NULL;
ddev->top = 0;
ddev->blendmode = 0;
ddev->flags = 0;
 
ddev->scissor.x0 = dest->x;
ddev->scissor.y0 = dest->y;
ddev->scissor.x1 = dest->x + dest->w;
ddev->scissor.y1 = dest->y + dest->h;
 
dev = fz_new_device(ddev);
dev->free_user = fz_draw_free_user;
 
dev->fill_path = fz_draw_fill_path;
dev->stroke_path = fz_draw_stroke_path;
dev->clip_path = fz_draw_clip_path;
dev->clip_stroke_path = fz_draw_clip_stroke_path;
 
dev->fill_text = fz_draw_fill_text;
dev->stroke_text = fz_draw_stroke_text;
dev->clip_text = fz_draw_clip_text;
dev->clip_stroke_text = fz_draw_clip_stroke_text;
dev->ignore_text = fz_draw_ignore_text;
 
dev->fill_image_mask = fz_draw_fill_image_mask;
dev->clip_image_mask = fz_draw_clip_image_mask;
dev->fill_image = fz_draw_fill_image;
dev->fill_shade = fz_draw_fill_shade;
 
dev->pop_clip = fz_draw_pop_clip;
 
dev->begin_mask = fz_draw_begin_mask;
dev->end_mask = fz_draw_end_mask;
dev->begin_group = fz_draw_begin_group;
dev->end_group = fz_draw_end_group;
 
dev->begin_tile = fz_draw_begin_tile;
dev->end_tile = fz_draw_end_tile;
 
return dev;
}
 
fz_device *
fz_new_draw_device_type3(fz_glyph_cache *cache, fz_pixmap *dest)
{
fz_device *dev = fz_new_draw_device(cache, dest);
fz_draw_device *ddev = dev->user;
ddev->flags |= FZ_DRAWDEV_FLAGS_TYPE3;
return dev;
}
/contrib/media/updf/draw/draw_edge.c
0,0 → 1,735
#include "fitz.h"
 
#define BBOX_MIN -(1<<20)
#define BBOX_MAX (1<<20)
 
/* divide and floor towards -inf */
static inline int fz_idiv(int a, int b)
{
return a < 0 ? (a - b + 1) / b : a / b;
}
 
/* If AA_BITS is defined, then we assume constant N bits of antialiasing. We
* will attempt to provide at least that number of bits of accuracy in the
* antialiasing (to a maximum of 8). If it is defined to be 0 then no
* antialiasing is done. If it is undefined to we will leave the antialiasing
* accuracy as a run time choice.
*/
 
#ifndef AA_BITS
#define AA_SCALE(x) ((x * fz_aa_scale) >> 8)
static int fz_aa_hscale = 17;
static int fz_aa_vscale = 15;
static int fz_aa_scale = 256;
static int fz_aa_level = 8;
 
#elif AA_BITS > 6
#define AA_SCALE(x) (x)
#define fz_aa_hscale 17
#define fz_aa_vscale 15
#define fz_aa_level 8
 
#elif AA_BITS > 4
#define AA_SCALE(x) ((x * 255) >> 6)
#define fz_aa_hscale 8
#define fz_aa_vscale 8
#define fz_aa_level 6
 
#elif AA_BITS > 2
#define AA_SCALE(x) (x * 17)
#define fz_aa_hscale 5
#define fz_aa_vscale 3
#define fz_aa_level 4
 
#elif AA_BITS > 0
#define AA_SCALE(x) ((x * 255) >> 2)
#define fz_aa_hscale 2
#define fz_aa_vscale 2
#define fz_aa_level 2
 
#else
#define AA_SCALE(x) (x * 255)
#define fz_aa_hscale 1
#define fz_aa_vscale 1
#define fz_aa_level 0
 
#endif
 
int
fz_get_aa_level(void)
{
return fz_aa_level;
}
 
void
fz_set_aa_level(int level)
{
#ifdef AA_BITS
fz_warn("anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_level);
#else
if (level > 6)
{
fz_aa_hscale = 17;
fz_aa_vscale = 15;
fz_aa_level = 8;
}
else if (level > 4)
{
fz_aa_hscale = 8;
fz_aa_vscale = 8;
fz_aa_level = 6;
}
else if (level > 2)
{
fz_aa_hscale = 5;
fz_aa_vscale = 3;
fz_aa_level = 4;
}
else if (level > 0)
{
fz_aa_hscale = 2;
fz_aa_vscale = 2;
fz_aa_level = 2;
}
else
{
fz_aa_hscale = 1;
fz_aa_vscale = 1;
fz_aa_level = 0;
}
fz_aa_scale = 0xFF00 / (fz_aa_hscale * fz_aa_vscale);
#endif
}
 
/*
* Global Edge List -- list of straight path segments for scan conversion
*
* Stepping along the edges is with bresenham's line algorithm.
*
* See Mike Abrash -- Graphics Programming Black Book (notably chapter 40)
*/
 
typedef struct fz_edge_s fz_edge;
 
struct fz_edge_s
{
int x, e, h, y;
int adj_up, adj_down;
int xmove;
int xdir, ydir; /* -1 or +1 */
};
 
struct fz_gel_s
{
fz_bbox clip;
fz_bbox bbox;
int cap, len;
fz_edge *edges;
int acap, alen;
fz_edge **active;
};
 
fz_gel *
fz_new_gel(void)
{
fz_gel *gel;
 
gel = fz_malloc(sizeof(fz_gel));
gel->cap = 512;
gel->len = 0;
gel->edges = fz_calloc(gel->cap, sizeof(fz_edge));
 
gel->clip.x0 = gel->clip.y0 = BBOX_MAX;
gel->clip.x1 = gel->clip.y1 = BBOX_MIN;
 
gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX;
gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN;
 
gel->acap = 64;
gel->alen = 0;
gel->active = fz_calloc(gel->acap, sizeof(fz_edge*));
 
return gel;
}
 
void
fz_reset_gel(fz_gel *gel, fz_bbox clip)
{
if (fz_is_infinite_rect(clip))
{
gel->clip.x0 = gel->clip.y0 = BBOX_MAX;
gel->clip.x1 = gel->clip.y1 = BBOX_MIN;
}
else {
gel->clip.x0 = clip.x0 * fz_aa_hscale;
gel->clip.x1 = clip.x1 * fz_aa_hscale;
gel->clip.y0 = clip.y0 * fz_aa_vscale;
gel->clip.y1 = clip.y1 * fz_aa_vscale;
}
 
gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX;
gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN;
 
gel->len = 0;
}
 
void
fz_free_gel(fz_gel *gel)
{
fz_free(gel->active);
fz_free(gel->edges);
fz_free(gel);
}
 
fz_bbox
fz_bound_gel(fz_gel *gel)
{
fz_bbox bbox;
if (gel->len == 0)
return fz_empty_bbox;
bbox.x0 = fz_idiv(gel->bbox.x0, fz_aa_hscale);
bbox.y0 = fz_idiv(gel->bbox.y0, fz_aa_vscale);
bbox.x1 = fz_idiv(gel->bbox.x1, fz_aa_hscale) + 1;
bbox.y1 = fz_idiv(gel->bbox.y1, fz_aa_vscale) + 1;
return bbox;
}
 
enum { INSIDE, OUTSIDE, LEAVE, ENTER };
 
#define clip_lerp_y(v,m,x0,y0,x1,y1,t) clip_lerp_x(v,m,y0,x0,y1,x1,t)
 
static int
clip_lerp_x(int val, int m, int x0, int y0, int x1, int y1, int *out)
{
int v0out = m ? x0 > val : x0 < val;
int v1out = m ? x1 > val : x1 < val;
 
if (v0out + v1out == 0)
return INSIDE;
 
if (v0out + v1out == 2)
return OUTSIDE;
 
if (v1out)
{
*out = y0 + (y1 - y0) * (val - x0) / (x1 - x0);
return LEAVE;
}
 
else
{
*out = y1 + (y0 - y1) * (val - x1) / (x0 - x1);
return ENTER;
}
}
 
static void
fz_insert_gel_raw(fz_gel *gel, int x0, int y0, int x1, int y1)
{
fz_edge *edge;
int dx, dy;
int winding;
int width;
int tmp;
 
if (y0 == y1)
return;
 
if (y0 > y1) {
winding = -1;
tmp = x0; x0 = x1; x1 = tmp;
tmp = y0; y0 = y1; y1 = tmp;
}
else
winding = 1;
 
if (x0 < gel->bbox.x0) gel->bbox.x0 = x0;
if (x0 > gel->bbox.x1) gel->bbox.x1 = x0;
if (x1 < gel->bbox.x0) gel->bbox.x0 = x1;
if (x1 > gel->bbox.x1) gel->bbox.x1 = x1;
 
if (y0 < gel->bbox.y0) gel->bbox.y0 = y0;
if (y1 > gel->bbox.y1) gel->bbox.y1 = y1;
 
if (gel->len + 1 == gel->cap) {
gel->cap = gel->cap + 512;
gel->edges = fz_realloc(gel->edges, gel->cap, sizeof(fz_edge));
}
 
edge = &gel->edges[gel->len++];
 
dy = y1 - y0;
dx = x1 - x0;
width = ABS(dx);
 
edge->xdir = dx > 0 ? 1 : -1;
edge->ydir = winding;
edge->x = x0;
edge->y = y0;
edge->h = dy;
edge->adj_down = dy;
 
/* initial error term going l->r and r->l */
if (dx >= 0)
edge->e = 0;
else
edge->e = -dy + 1;
 
/* y-major edge */
if (dy >= width) {
edge->xmove = 0;
edge->adj_up = width;
}
 
/* x-major edge */
else {
edge->xmove = (width / dy) * edge->xdir;
edge->adj_up = width % dy;
}
}
 
void
fz_insert_gel(fz_gel *gel, float fx0, float fy0, float fx1, float fy1)
{
int x0, y0, x1, y1;
int d, v;
 
fx0 = floorf(fx0 * fz_aa_hscale);
fx1 = floorf(fx1 * fz_aa_hscale);
fy0 = floorf(fy0 * fz_aa_vscale);
fy1 = floorf(fy1 * fz_aa_vscale);
 
x0 = CLAMP(fx0, BBOX_MIN, BBOX_MAX);
y0 = CLAMP(fy0, BBOX_MIN, BBOX_MAX);
x1 = CLAMP(fx1, BBOX_MIN, BBOX_MAX);
y1 = CLAMP(fy1, BBOX_MIN, BBOX_MAX);
 
d = clip_lerp_y(gel->clip.y0, 0, x0, y0, x1, y1, &v);
if (d == OUTSIDE) return;
if (d == LEAVE) { y1 = gel->clip.y0; x1 = v; }
if (d == ENTER) { y0 = gel->clip.y0; x0 = v; }
 
d = clip_lerp_y(gel->clip.y1, 1, x0, y0, x1, y1, &v);
if (d == OUTSIDE) return;
if (d == LEAVE) { y1 = gel->clip.y1; x1 = v; }
if (d == ENTER) { y0 = gel->clip.y1; x0 = v; }
 
d = clip_lerp_x(gel->clip.x0, 0, x0, y0, x1, y1, &v);
if (d == OUTSIDE) {
x0 = x1 = gel->clip.x0;
}
if (d == LEAVE) {
fz_insert_gel_raw(gel, gel->clip.x0, v, gel->clip.x0, y1);
x1 = gel->clip.x0;
y1 = v;
}
if (d == ENTER) {
fz_insert_gel_raw(gel, gel->clip.x0, y0, gel->clip.x0, v);
x0 = gel->clip.x0;
y0 = v;
}
 
d = clip_lerp_x(gel->clip.x1, 1, x0, y0, x1, y1, &v);
if (d == OUTSIDE) {
x0 = x1 = gel->clip.x1;
}
if (d == LEAVE) {
fz_insert_gel_raw(gel, gel->clip.x1, v, gel->clip.x1, y1);
x1 = gel->clip.x1;
y1 = v;
}
if (d == ENTER) {
fz_insert_gel_raw(gel, gel->clip.x1, y0, gel->clip.x1, v);
x0 = gel->clip.x1;
y0 = v;
}
 
fz_insert_gel_raw(gel, x0, y0, x1, y1);
}
 
void
fz_sort_gel(fz_gel *gel)
{
fz_edge *a = gel->edges;
int n = gel->len;
 
int h, i, k;
fz_edge t;
 
h = 1;
if (n < 14) {
h = 1;
}
else {
while (h < n)
h = 3 * h + 1;
h /= 3;
h /= 3;
}
 
while (h > 0)
{
for (i = 0; i < n; i++) {
t = a[i];
k = i - h;
/* TODO: sort on y major, x minor */
while (k >= 0 && a[k].y > t.y) {
a[k + h] = a[k];
k -= h;
}
a[k + h] = t;
}
 
h /= 3;
}
}
 
int
fz_is_rect_gel(fz_gel *gel)
{
/* a rectangular path is converted into two vertical edges of identical height */
if (gel->len == 2)
{
fz_edge *a = gel->edges + 0;
fz_edge *b = gel->edges + 1;
return a->y == b->y && a->h == b->h &&
a->xmove == 0 && a->adj_up == 0 &&
b->xmove == 0 && b->adj_up == 0;
}
return 0;
}
 
/*
* Active Edge List -- keep track of active edges while sweeping
*/
 
static void
sort_active(fz_edge **a, int n)
{
int h, i, k;
fz_edge *t;
 
h = 1;
if (n < 14) {
h = 1;
}
else {
while (h < n)
h = 3 * h + 1;
h /= 3;
h /= 3;
}
 
while (h > 0)
{
for (i = 0; i < n; i++) {
t = a[i];
k = i - h;
while (k >= 0 && a[k]->x > t->x) {
a[k + h] = a[k];
k -= h;
}
a[k + h] = t;
}
 
h /= 3;
}
}
 
static void
insert_active(fz_gel *gel, int y, int *e)
{
/* insert edges that start here */
while (*e < gel->len && gel->edges[*e].y == y) {
if (gel->alen + 1 == gel->acap) {
int newcap = gel->acap + 64;
fz_edge **newactive = fz_realloc(gel->active, newcap, sizeof(fz_edge*));
gel->active = newactive;
gel->acap = newcap;
}
gel->active[gel->alen++] = &gel->edges[(*e)++];
}
 
/* shell-sort the edges by increasing x */
sort_active(gel->active, gel->alen);
}
 
static void
advance_active(fz_gel *gel)
{
fz_edge *edge;
int i = 0;
 
while (i < gel->alen)
{
edge = gel->active[i];
 
edge->h --;
 
/* terminator! */
if (edge->h == 0) {
gel->active[i] = gel->active[--gel->alen];
}
 
else {
edge->x += edge->xmove;
edge->e += edge->adj_up;
if (edge->e > 0) {
edge->x += edge->xdir;
edge->e -= edge->adj_down;
}
i ++;
}
}
}
 
/*
* Anti-aliased scan conversion.
*/
 
static inline void add_span_aa(int *list, int x0, int x1, int xofs)
{
int x0pix, x0sub;
int x1pix, x1sub;
 
if (x0 == x1)
return;
 
/* x between 0 and width of bbox */
x0 -= xofs;
x1 -= xofs;
 
x0pix = x0 / fz_aa_hscale;
x0sub = x0 % fz_aa_hscale;
x1pix = x1 / fz_aa_hscale;
x1sub = x1 % fz_aa_hscale;
 
if (x0pix == x1pix)
{
list[x0pix] += x1sub - x0sub;
list[x0pix+1] += x0sub - x1sub;
}
 
else
{
list[x0pix] += fz_aa_hscale - x0sub;
list[x0pix+1] += x0sub;
list[x1pix] += x1sub - fz_aa_hscale;
list[x1pix+1] += -x1sub;
}
}
 
static inline void non_zero_winding_aa(fz_gel *gel, int *list, int xofs)
{
int winding = 0;
int x = 0;
int i;
for (i = 0; i < gel->alen; i++)
{
if (!winding && (winding + gel->active[i]->ydir))
x = gel->active[i]->x;
if (winding && !(winding + gel->active[i]->ydir))
add_span_aa(list, x, gel->active[i]->x, xofs);
winding += gel->active[i]->ydir;
}
}
 
static inline void even_odd_aa(fz_gel *gel, int *list, int xofs)
{
int even = 0;
int x = 0;
int i;
for (i = 0; i < gel->alen; i++)
{
if (!even)
x = gel->active[i]->x;
else
add_span_aa(list, x, gel->active[i]->x, xofs);
even = !even;
}
}
 
static inline void undelta_aa(unsigned char * restrict out, int * restrict in, int n)
{
int d = 0;
while (n--)
{
d += *in++;
*out++ = AA_SCALE(d);
}
}
 
static inline void blit_aa(fz_pixmap *dst, int x, int y,
unsigned char *mp, int w, unsigned char *color)
{
unsigned char *dp;
dp = dst->samples + ( (y - dst->y) * dst->w + (x - dst->x) ) * dst->n;
if (color)
fz_paint_span_with_color(dp, mp, dst->n, w, color);
else
fz_paint_span(dp, mp, 1, w, 255);
}
 
static void
fz_scan_convert_aa(fz_gel *gel, int eofill, fz_bbox clip,
fz_pixmap *dst, unsigned char *color)
{
unsigned char *alphas;
int *deltas;
int y, e;
int yd, yc;
 
int xmin = fz_idiv(gel->bbox.x0, fz_aa_hscale);
int xmax = fz_idiv(gel->bbox.x1, fz_aa_hscale) + 1;
 
int xofs = xmin * fz_aa_hscale;
 
int skipx = clip.x0 - xmin;
int clipn = clip.x1 - clip.x0;
 
if (gel->len == 0)
return;
 
assert(clip.x0 >= xmin);
assert(clip.x1 <= xmax);
 
alphas = fz_malloc(xmax - xmin + 1);
deltas = fz_malloc((xmax - xmin + 1) * sizeof(int));
memset(deltas, 0, (xmax - xmin + 1) * sizeof(int));
 
e = 0;
y = gel->edges[0].y;
yc = fz_idiv(y, fz_aa_vscale);
yd = yc;
 
while (gel->alen > 0 || e < gel->len)
{
yc = fz_idiv(y, fz_aa_vscale);
if (yc != yd)
{
if (yd >= clip.y0 && yd < clip.y1)
{
undelta_aa(alphas, deltas, skipx + clipn);
blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color);
memset(deltas, 0, (skipx + clipn) * sizeof(int));
}
}
yd = yc;
 
insert_active(gel, y, &e);
 
if (yd >= clip.y0 && yd < clip.y1)
{
if (eofill)
even_odd_aa(gel, deltas, xofs);
else
non_zero_winding_aa(gel, deltas, xofs);
}
 
advance_active(gel);
 
if (gel->alen > 0)
y ++;
else if (e < gel->len)
y = gel->edges[e].y;
}
 
if (yd >= clip.y0 && yd < clip.y1)
{
undelta_aa(alphas, deltas, skipx + clipn);
blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color);
}
 
fz_free(deltas);
fz_free(alphas);
}
 
/*
* Sharp (not anti-aliased) scan conversion
*/
 
static inline void blit_sharp(int x0, int x1, int y,
fz_bbox clip, fz_pixmap *dst, unsigned char *color)
{
unsigned char *dp;
x0 = CLAMP(x0, dst->x, dst->x + dst->w);
x1 = CLAMP(x1, dst->x, dst->x + dst->w);
if (x0 < x1)
{
dp = dst->samples + ( (y - dst->y) * dst->w + (x0 - dst->x) ) * dst->n;
if (color)
fz_paint_solid_color(dp, dst->n, x1 - x0, color);
else
fz_paint_solid_alpha(dp, x1 - x0, 255);
}
}
 
static inline void non_zero_winding_sharp(fz_gel *gel, int y,
fz_bbox clip, fz_pixmap *dst, unsigned char *color)
{
int winding = 0;
int x = 0;
int i;
for (i = 0; i < gel->alen; i++)
{
if (!winding && (winding + gel->active[i]->ydir))
x = gel->active[i]->x;
if (winding && !(winding + gel->active[i]->ydir))
blit_sharp(x, gel->active[i]->x, y, clip, dst, color);
winding += gel->active[i]->ydir;
}
}
 
static inline void even_odd_sharp(fz_gel *gel, int y,
fz_bbox clip, fz_pixmap *dst, unsigned char *color)
{
int even = 0;
int x = 0;
int i;
for (i = 0; i < gel->alen; i++)
{
if (!even)
x = gel->active[i]->x;
else
blit_sharp(x, gel->active[i]->x, y, clip, dst, color);
even = !even;
}
}
 
static void
fz_scan_convert_sharp(fz_gel *gel, int eofill, fz_bbox clip,
fz_pixmap *dst, unsigned char *color)
{
int e = 0;
int y = gel->edges[0].y;
 
while (gel->alen > 0 || e < gel->len)
{
insert_active(gel, y, &e);
 
if (y >= clip.y0 && y < clip.y1)
{
if (eofill)
even_odd_sharp(gel, y, clip, dst, color);
else
non_zero_winding_sharp(gel, y, clip, dst, color);
}
 
advance_active(gel);
 
if (gel->alen > 0)
y ++;
else if (e < gel->len)
y = gel->edges[e].y;
}
}
 
void
fz_scan_convert(fz_gel *gel, int eofill, fz_bbox clip,
fz_pixmap *dst, unsigned char *color)
{
if (fz_aa_level > 0)
fz_scan_convert_aa(gel, eofill, clip, dst, color);
else
fz_scan_convert_sharp(gel, eofill, clip, dst, color);
}
/contrib/media/updf/draw/draw_glyph.c
0,0 → 1,134
#include "fitz.h"
 
#define MAX_FONT_SIZE 1000
#define MAX_GLYPH_SIZE 256
#define MAX_CACHE_SIZE (1024*1024)
 
typedef struct fz_glyph_key_s fz_glyph_key;
 
struct fz_glyph_cache_s
{
fz_hash_table *hash;
int total;
};
 
struct fz_glyph_key_s
{
fz_font *font;
int a, b;
int c, d;
unsigned short gid;
unsigned char e, f;
};
 
fz_glyph_cache *
fz_new_glyph_cache(void)
{
fz_glyph_cache *cache;
 
cache = fz_malloc(sizeof(fz_glyph_cache));
cache->hash = fz_new_hash_table(509, sizeof(fz_glyph_key));
cache->total = 0;
 
return cache;
}
 
static void
fz_evict_glyph_cache(fz_glyph_cache *cache)
{
fz_glyph_key *key;
fz_pixmap *pixmap;
int i;
 
for (i = 0; i < fz_hash_len(cache->hash); i++)
{
key = fz_hash_get_key(cache->hash, i);
if (key->font)
fz_drop_font(key->font);
pixmap = fz_hash_get_val(cache->hash, i);
if (pixmap)
fz_drop_pixmap(pixmap);
}
 
cache->total = 0;
 
fz_empty_hash(cache->hash);
}
 
void
fz_free_glyph_cache(fz_glyph_cache *cache)
{
fz_evict_glyph_cache(cache);
fz_free_hash(cache->hash);
fz_free(cache);
}
 
fz_pixmap *
fz_render_stroked_glyph(fz_glyph_cache *cache, fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, fz_stroke_state *stroke)
{
if (font->ft_face)
return fz_render_ft_stroked_glyph(font, gid, trm, ctm, stroke);
return fz_render_glyph(cache, font, gid, trm, NULL);
}
 
fz_pixmap *
fz_render_glyph(fz_glyph_cache *cache, fz_font *font, int gid, fz_matrix ctm, fz_colorspace *model)
{
fz_glyph_key key;
fz_pixmap *val;
float size = fz_matrix_expansion(ctm);
 
if (size > MAX_FONT_SIZE)
{
/* TODO: this case should be handled by rendering glyph as a path fill */
fz_warn("font size too large (%g), not rendering glyph", size);
return NULL;
}
 
memset(&key, 0, sizeof key);
key.font = font;
key.gid = gid;
key.a = ctm.a * 65536;
key.b = ctm.b * 65536;
key.c = ctm.c * 65536;
key.d = ctm.d * 65536;
key.e = (ctm.e - floorf(ctm.e)) * 256;
key.f = (ctm.f - floorf(ctm.f)) * 256;
 
val = fz_hash_find(cache->hash, &key);
if (val)
return fz_keep_pixmap(val);
 
ctm.e = floorf(ctm.e) + key.e / 256.0f;
ctm.f = floorf(ctm.f) + key.f / 256.0f;
 
if (font->ft_face)
{
val = fz_render_ft_glyph(font, gid, ctm);
}
else if (font->t3procs)
{
val = fz_render_t3_glyph(font, gid, ctm, model);
}
else
{
fz_warn("assert: uninitialized font structure");
return NULL;
}
 
if (val)
{
if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE)
{
if (cache->total + val->w * val->h > MAX_CACHE_SIZE)
fz_evict_glyph_cache(cache);
fz_keep_font(key.font);
fz_hash_insert(cache->hash, &key, val);
cache->total += val->w * val->h;
return fz_keep_pixmap(val);
}
return val;
}
 
return NULL;
}
/contrib/media/updf/draw/draw_mesh.c
0,0 → 1,574
#include "fitz.h"
 
/*
* polygon clipping
*/
 
enum { IN, OUT, ENTER, LEAVE };
enum { MAXV = 3 + 4 };
enum { MAXN = 2 + FZ_MAX_COLORS };
 
static int clipx(float val, int ismax, float *v1, float *v2, int n)
{
float t;
int i;
int v1o = ismax ? v1[0] > val : v1[0] < val;
int v2o = ismax ? v2[0] > val : v2[0] < val;
if (v1o + v2o == 0)
return IN;
if (v1o + v2o == 2)
return OUT;
if (v2o)
{
t = (val - v1[0]) / (v2[0] - v1[0]);
v2[0] = val;
v2[1] = v1[1] + t * (v2[1] - v1[1]);
for (i = 2; i < n; i++)
v2[i] = v1[i] + t * (v2[i] - v1[i]);
return LEAVE;
}
else
{
t = (val - v2[0]) / (v1[0] - v2[0]);
v1[0] = val;
v1[1] = v2[1] + t * (v1[1] - v2[1]);
for (i = 2; i < n; i++)
v1[i] = v2[i] + t * (v1[i] - v2[i]);
return ENTER;
}
}
 
static int clipy(float val, int ismax, float *v1, float *v2, int n)
{
float t;
int i;
int v1o = ismax ? v1[1] > val : v1[1] < val;
int v2o = ismax ? v2[1] > val : v2[1] < val;
if (v1o + v2o == 0)
return IN;
if (v1o + v2o == 2)
return OUT;
if (v2o)
{
t = (val - v1[1]) / (v2[1] - v1[1]);
v2[0] = v1[0] + t * (v2[0] - v1[0]);
v2[1] = val;
for (i = 2; i < n; i++)
v2[i] = v1[i] + t * (v2[i] - v1[i]);
return LEAVE;
}
else
{
t = (val - v2[1]) / (v1[1] - v2[1]);
v1[0] = v2[0] + t * (v1[0] - v2[0]);
v1[1] = val;
for (i = 2; i < n; i++)
v1[i] = v2[i] + t * (v1[i] - v2[i]);
return ENTER;
}
}
 
static inline void copy_vert(float *dst, float *src, int n)
{
while (n--)
*dst++ = *src++;
}
 
static int clip_poly(float src[MAXV][MAXN],
float dst[MAXV][MAXN], int len, int n,
float val, int isy, int ismax)
{
float cv1[MAXN];
float cv2[MAXN];
int v1, v2, cp;
int r;
 
v1 = len - 1;
cp = 0;
 
for (v2 = 0; v2 < len; v2++)
{
copy_vert(cv1, src[v1], n);
copy_vert(cv2, src[v2], n);
 
if (isy)
r = clipy(val, ismax, cv1, cv2, n);
else
r = clipx(val, ismax, cv1, cv2, n);
 
switch (r)
{
case IN:
copy_vert(dst[cp++], cv2, n);
break;
case OUT:
break;
case LEAVE:
copy_vert(dst[cp++], cv2, n);
break;
case ENTER:
copy_vert(dst[cp++], cv1, n);
copy_vert(dst[cp++], cv2, n);
break;
}
v1 = v2;
}
 
return cp;
}
 
/*
* gouraud shaded polygon scan conversion
*/
 
static void paint_scan(fz_pixmap *pix, int y, int x1, int x2, int *v1, int *v2, int n)
{
unsigned char *p = pix->samples + ((y - pix->y) * pix->w + (x1 - pix->x)) * pix->n;
int v[FZ_MAX_COLORS];
int dv[FZ_MAX_COLORS];
int w = x2 - x1;
int k;
 
assert(w >= 0);
assert(y >= pix->y);
assert(y < pix->y + pix->h);
assert(x1 >= pix->x);
assert(x2 <= pix->x + pix->w);
 
if (w == 0)
return;
 
for (k = 0; k < n; k++)
{
v[k] = v1[k];
dv[k] = (v2[k] - v1[k]) / w;
}
 
while (w--)
{
for (k = 0; k < n; k++)
{
*p++ = v[k] >> 16;
v[k] += dv[k];
}
*p++ = 255;
}
}
 
static int find_next(int gel[MAXV][MAXN], int len, int a, int *s, int *e, int d)
{
int b;
 
while (1)
{
b = a + d;
if (b == len)
b = 0;
if (b == -1)
b = len - 1;
 
if (gel[b][1] == gel[a][1])
{
a = b;
continue;
}
 
if (gel[b][1] > gel[a][1])
{
*s = a;
*e = b;
return 0;
}
 
return 1;
}
}
 
static void load_edge(int gel[MAXV][MAXN], int s, int e, int *ael, int *del, int n)
{
int swp, k, dy;
 
if (gel[s][1] > gel[e][1])
{
swp = s; s = e; e = swp;
}
 
dy = gel[e][1] - gel[s][1];
 
ael[0] = gel[s][0];
del[0] = (gel[e][0] - gel[s][0]) / dy;
for (k = 2; k < n; k++)
{
ael[k] = gel[s][k];
del[k] = (gel[e][k] - gel[s][k]) / dy;
}
}
 
static inline void step_edge(int *ael, int *del, int n)
{
int k;
ael[0] += del[0];
for (k = 2; k < n; k++)
ael[k] += del[k];
}
 
static void
fz_paint_triangle(fz_pixmap *pix, float *av, float *bv, float *cv, int n, fz_bbox bbox)
{
float poly[MAXV][MAXN];
float temp[MAXV][MAXN];
float cx0 = bbox.x0;
float cy0 = bbox.y0;
float cx1 = bbox.x1;
float cy1 = bbox.y1;
 
int gel[MAXV][MAXN];
int ael[2][MAXN];
int del[2][MAXN];
int y, s0, s1, e0, e1;
int top, bot, len;
 
int i, k;
 
copy_vert(poly[0], av, n);
copy_vert(poly[1], bv, n);
copy_vert(poly[2], cv, n);
 
len = clip_poly(poly, temp, 3, n, cx0, 0, 0);
len = clip_poly(temp, poly, len, n, cx1, 0, 1);
len = clip_poly(poly, temp, len, n, cy0, 1, 0);
len = clip_poly(temp, poly, len, n, cy1, 1, 1);
 
if (len < 3)
return;
 
for (i = 0; i < len; i++)
{
gel[i][0] = floorf(poly[i][0] + 0.5f) * 65536; /* trunc and fix */
gel[i][1] = floorf(poly[i][1] + 0.5f); /* y is not fixpoint */
for (k = 2; k < n; k++)
gel[i][k] = poly[i][k] * 65536; /* fix with precision */
}
 
top = bot = 0;
for (i = 0; i < len; i++)
{
if (gel[i][1] < gel[top][1])
top = i;
if (gel[i][1] > gel[bot][1])
bot = i;
}
 
if (gel[bot][1] - gel[top][1] == 0)
return;
 
y = gel[top][1];
 
if (find_next(gel, len, top, &s0, &e0, 1))
return;
if (find_next(gel, len, top, &s1, &e1, -1))
return;
 
load_edge(gel, s0, e0, ael[0], del[0], n);
load_edge(gel, s1, e1, ael[1], del[1], n);
 
while (1)
{
int x0 = ael[0][0] >> 16;
int x1 = ael[1][0] >> 16;
 
if (ael[0][0] < ael[1][0])
paint_scan(pix, y, x0, x1, ael[0]+2, ael[1]+2, n-2);
else
paint_scan(pix, y, x1, x0, ael[1]+2, ael[0]+2, n-2);
 
step_edge(ael[0], del[0], n);
step_edge(ael[1], del[1], n);
y ++;
 
if (y >= gel[e0][1])
{
if (find_next(gel, len, e0, &s0, &e0, 1))
return;
load_edge(gel, s0, e0, ael[0], del[0], n);
}
 
if (y >= gel[e1][1])
{
if (find_next(gel, len, e1, &s1, &e1, -1))
return;
load_edge(gel, s1, e1, ael[1], del[1], n);
}
}
}
 
static void
fz_paint_quad(fz_pixmap *pix,
fz_point p0, fz_point p1, fz_point p2, fz_point p3,
float c0, float c1, float c2, float c3,
int n, fz_bbox bbox)
{
float v[4][3];
 
v[0][0] = p0.x;
v[0][1] = p0.y;
v[0][2] = c0;
 
v[1][0] = p1.x;
v[1][1] = p1.y;
v[1][2] = c1;
 
v[2][0] = p2.x;
v[2][1] = p2.y;
v[2][2] = c2;
 
v[3][0] = p3.x;
v[3][1] = p3.y;
v[3][2] = c3;
 
fz_paint_triangle(pix, v[0], v[2], v[3], n, bbox);
fz_paint_triangle(pix, v[0], v[3], v[1], n, bbox);
}
 
/*
* linear, radial and mesh painting
*/
 
#define HUGENUM 32000 /* how far to extend axial/radial shadings */
#define RADSEGS 32 /* how many segments to generate for radial meshes */
 
static fz_point
fz_point_on_circle(fz_point p, float r, float theta)
{
p.x = p.x + cosf(theta) * r;
p.y = p.y + sinf(theta) * r;
 
return p;
}
 
static void
fz_paint_linear(fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox)
{
fz_point p0, p1;
fz_point v0, v1, v2, v3;
fz_point e0, e1;
float theta;
 
p0.x = shade->mesh[0];
p0.y = shade->mesh[1];
p0 = fz_transform_point(ctm, p0);
 
p1.x = shade->mesh[3];
p1.y = shade->mesh[4];
p1 = fz_transform_point(ctm, p1);
 
theta = atan2f(p1.y - p0.y, p1.x - p0.x);
theta += (float)M_PI * 0.5f;
 
v0 = fz_point_on_circle(p0, HUGENUM, theta);
v1 = fz_point_on_circle(p1, HUGENUM, theta);
v2 = fz_point_on_circle(p0, -HUGENUM, theta);
v3 = fz_point_on_circle(p1, -HUGENUM, theta);
 
fz_paint_quad(dest, v0, v1, v2, v3, 0, 255, 0, 255, 3, bbox);
 
if (shade->extend[0])
{
e0.x = v0.x - (p1.x - p0.x) * HUGENUM;
e0.y = v0.y - (p1.y - p0.y) * HUGENUM;
 
e1.x = v2.x - (p1.x - p0.x) * HUGENUM;
e1.y = v2.y - (p1.y - p0.y) * HUGENUM;
 
fz_paint_quad(dest, e0, e1, v0, v2, 0, 0, 0, 0, 3, bbox);
}
 
if (shade->extend[1])
{
e0.x = v1.x + (p1.x - p0.x) * HUGENUM;
e0.y = v1.y + (p1.y - p0.y) * HUGENUM;
 
e1.x = v3.x + (p1.x - p0.x) * HUGENUM;
e1.y = v3.y + (p1.y - p0.y) * HUGENUM;
 
fz_paint_quad(dest, e0, e1, v1, v3, 255, 255, 255, 255, 3, bbox);
}
}
 
static void
fz_paint_annulus(fz_matrix ctm,
fz_point p0, float r0, float c0,
fz_point p1, float r1, float c1,
fz_pixmap *dest, fz_bbox bbox)
{
fz_point t0, t1, t2, t3, b0, b1, b2, b3;
float theta, step;
int i;
 
theta = atan2f(p1.y - p0.y, p1.x - p0.x);
step = (float)M_PI * 2 / RADSEGS;
 
for (i = 0; i < RADSEGS / 2; i++)
{
t0 = fz_point_on_circle(p0, r0, theta + i * step);
t1 = fz_point_on_circle(p0, r0, theta + i * step + step);
t2 = fz_point_on_circle(p1, r1, theta + i * step);
t3 = fz_point_on_circle(p1, r1, theta + i * step + step);
b0 = fz_point_on_circle(p0, r0, theta - i * step);
b1 = fz_point_on_circle(p0, r0, theta - i * step - step);
b2 = fz_point_on_circle(p1, r1, theta - i * step);
b3 = fz_point_on_circle(p1, r1, theta - i * step - step);
 
t0 = fz_transform_point(ctm, t0);
t1 = fz_transform_point(ctm, t1);
t2 = fz_transform_point(ctm, t2);
t3 = fz_transform_point(ctm, t3);
b0 = fz_transform_point(ctm, b0);
b1 = fz_transform_point(ctm, b1);
b2 = fz_transform_point(ctm, b2);
b3 = fz_transform_point(ctm, b3);
 
fz_paint_quad(dest, t0, t1, t2, t3, c0, c0, c1, c1, 3, bbox);
fz_paint_quad(dest, b0, b1, b2, b3, c0, c0, c1, c1, 3, bbox);
}
}
 
static void
fz_paint_radial(fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox)
{
fz_point p0, p1;
float r0, r1;
fz_point e;
float er, rs;
 
p0.x = shade->mesh[0];
p0.y = shade->mesh[1];
r0 = shade->mesh[2];
 
p1.x = shade->mesh[3];
p1.y = shade->mesh[4];
r1 = shade->mesh[5];
 
if (shade->extend[0])
{
if (r0 < r1)
rs = r0 / (r0 - r1);
else
rs = -HUGENUM;
 
e.x = p0.x + (p1.x - p0.x) * rs;
e.y = p0.y + (p1.y - p0.y) * rs;
er = r0 + (r1 - r0) * rs;
 
fz_paint_annulus(ctm, e, er, 0, p0, r0, 0, dest, bbox);
}
 
fz_paint_annulus(ctm, p0, r0, 0, p1, r1, 255, dest, bbox);
 
if (shade->extend[1])
{
if (r0 > r1)
rs = r1 / (r1 - r0);
else
rs = -HUGENUM;
 
e.x = p1.x + (p0.x - p1.x) * rs;
e.y = p1.y + (p0.y - p1.y) * rs;
er = r1 + (r0 - r1) * rs;
 
fz_paint_annulus(ctm, p1, r1, 255, e, er, 255, dest, bbox);
}
}
 
static void
fz_paint_mesh(fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox)
{
float tri[3][MAXN];
fz_point p;
float *mesh;
int ntris;
int i, k;
 
mesh = shade->mesh;
 
if (shade->use_function)
ntris = shade->mesh_len / 9;
else
ntris = shade->mesh_len / ((2 + shade->colorspace->n) * 3);
 
while (ntris--)
{
for (k = 0; k < 3; k++)
{
p.x = *mesh++;
p.y = *mesh++;
p = fz_transform_point(ctm, p);
tri[k][0] = p.x;
tri[k][1] = p.y;
if (shade->use_function)
tri[k][2] = *mesh++ * 255;
else
{
fz_convert_color(shade->colorspace, mesh, dest->colorspace, tri[k] + 2);
for (i = 0; i < dest->colorspace->n; i++)
tri[k][i + 2] *= 255;
mesh += shade->colorspace->n;
}
}
fz_paint_triangle(dest, tri[0], tri[1], tri[2], 2 + dest->colorspace->n, bbox);
}
}
 
void
fz_paint_shade(fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox)
{
unsigned char clut[256][FZ_MAX_COLORS];
fz_pixmap *temp, *conv;
float color[FZ_MAX_COLORS];
int i, k;
 
ctm = fz_concat(shade->matrix, ctm);
 
if (shade->use_function)
{
for (i = 0; i < 256; i++)
{
fz_convert_color(shade->colorspace, shade->function[i], dest->colorspace, color);
for (k = 0; k < dest->colorspace->n; k++)
clut[i][k] = color[k] * 255;
clut[i][k] = shade->function[i][shade->colorspace->n] * 255;
}
conv = fz_new_pixmap_with_rect(dest->colorspace, bbox);
temp = fz_new_pixmap_with_rect(fz_device_gray, bbox);
fz_clear_pixmap(temp);
}
else
{
temp = dest;
}
 
switch (shade->type)
{
case FZ_LINEAR: fz_paint_linear(shade, ctm, temp, bbox); break;
case FZ_RADIAL: fz_paint_radial(shade, ctm, temp, bbox); break;
case FZ_MESH: fz_paint_mesh(shade, ctm, temp, bbox); break;
}
 
if (shade->use_function)
{
unsigned char *s = temp->samples;
unsigned char *d = conv->samples;
int len = temp->w * temp->h;
while (len--)
{
int v = *s++;
int a = fz_mul255(*s++, clut[v][conv->n - 1]);
for (k = 0; k < conv->n - 1; k++)
*d++ = fz_mul255(clut[v][k], a);
*d++ = a;
}
fz_paint_pixmap(dest, conv, 255);
fz_drop_pixmap(conv);
fz_drop_pixmap(temp);
}
}
/contrib/media/updf/draw/draw_paint.c
0,0 → 1,471
#include "fitz.h"
 
/*
 
The functions in this file implement various flavours of Porter-Duff blending.
 
We take the following as definitions:
 
Cx = Color (from plane x)
ax = Alpha (from plane x)
cx = Cx.ax = Premultiplied color (from plane x)
 
The general PorterDuff blending equation is:
 
Blend Z = X op Y cz = Fx.cx + Fy. cy where Fx and Fy depend on op
 
The two operations we use in this file are: '(X in Y) over Z' and
'S over Z'. The definitions of the 'over' and 'in' operations are as
follows:
 
For S over Z, Fs = 1, Fz = 1-as
For X in Y, Fx = ay, Fy = 0
 
We have 2 choices; we can either work with premultiplied data, or non
premultiplied data. Our
 
First the premultiplied case:
 
Let S = (X in Y)
Let R = (X in Y) over Z = S over Z
 
cs = cx.Fx + cy.Fy (where Fx = ay, Fy = 0)
= cx.ay
as = ax.Fx + ay.Fy
= ax.ay
 
cr = cs.Fs + cz.Fz (where Fs = 1, Fz = 1-as)
= cs + cz.(1-as)
= cx.ay + cz.(1-ax.ay)
ar = as.Fs + az.Fz
= as + az.(1-as)
= ax.ay + az.(1-ax.ay)
 
This has various nice properties, like not needing any divisions, and
being symmetric in color and alpha, so this is what we use. Because we
went through the pain of deriving the non premultiplied forms, we list
them here too, though they are not used.
 
Non Pre-multiplied case:
 
Cs.as = Fx.Cx.ax + Fy.Cy.ay (where Fx = ay, Fy = 0)
= Cx.ay.ax
Cs = (Cx.ay.ax)/(ay.ax)
= Cx
Cr.ar = Fs.Cs.as + Fz.Cz.az (where Fs = 1, Fz = 1-as)
= Cs.as + (1-as).Cz.az
= Cx.ax.ay + Cz.az.(1-ax.ay)
Cr = (Cx.ax.ay + Cz.az.(1-ax.ay))/(ax.ay + az.(1-ax-ay))
 
Much more complex, it seems. However, if we could restrict ourselves to
the case where we were always plotting onto an opaque background (i.e.
az = 1), then:
 
Cr = Cx.(ax.ay) + Cz.(1-ax.ay)
= (Cx-Cz)*(1-ax.ay) + Cz (a single MLA operation)
ar = 1
 
Sadly, this is not true in the general case, so we abandon this effort
and stick to using the premultiplied form.
 
*/
 
typedef unsigned char byte;
 
/* These are used by the non-aa scan converter */
 
void
fz_paint_solid_alpha(byte * restrict dp, int w, int alpha)
{
int t = FZ_EXPAND(255 - alpha);
while (w--)
{
*dp = alpha + FZ_COMBINE(*dp, t);
dp ++;
}
}
 
void
fz_paint_solid_color(byte * restrict dp, int n, int w, byte *color)
{
int n1 = n - 1;
int sa = FZ_EXPAND(color[n1]);
int k;
while (w--)
{
int ma = FZ_COMBINE(FZ_EXPAND(255), sa);
for (k = 0; k < n1; k++)
dp[k] = FZ_BLEND(color[k], dp[k], ma);
dp[k] = FZ_BLEND(255, dp[k], ma);
dp += n;
}
}
 
/* Blend a non-premultiplied color in mask over destination */
 
static inline void
fz_paint_span_with_color_2(byte * restrict dp, byte * restrict mp, int w, byte *color)
{
int sa = FZ_EXPAND(color[1]);
int g = color[0];
while (w--)
{
int ma = *mp++;
ma = FZ_COMBINE(FZ_EXPAND(ma), sa);
dp[0] = FZ_BLEND(g, dp[0], ma);
dp[1] = FZ_BLEND(255, dp[1], ma);
dp += 2;
}
}
 
static inline void
fz_paint_span_with_color_4(byte * restrict dp, byte * restrict mp, int w, byte *color)
{
int sa = FZ_EXPAND(color[3]);
int r = color[0];
int g = color[1];
int b = color[2];
while (w--)
{
int ma = *mp++;
ma = FZ_COMBINE(FZ_EXPAND(ma), sa);
dp[0] = FZ_BLEND(r, dp[0], ma);
dp[1] = FZ_BLEND(g, dp[1], ma);
dp[2] = FZ_BLEND(b, dp[2], ma);
dp[3] = FZ_BLEND(255, dp[3], ma);
dp += 4;
}
}
 
static inline void
fz_paint_span_with_color_N(byte * restrict dp, byte * restrict mp, int n, int w, byte *color)
{
int n1 = n - 1;
int sa = FZ_EXPAND(color[n1]);
int k;
while (w--)
{
int ma = *mp++;
ma = FZ_COMBINE(FZ_EXPAND(ma), sa);
for (k = 0; k < n1; k++)
dp[k] = FZ_BLEND(color[k], dp[k], ma);
dp[k] = FZ_BLEND(255, dp[k], ma);
dp += n;
}
}
 
void
fz_paint_span_with_color(byte * restrict dp, byte * restrict mp, int n, int w, byte *color)
{
switch (n)
{
case 2: fz_paint_span_with_color_2(dp, mp, w, color); break;
case 4: fz_paint_span_with_color_4(dp, mp, w, color); break;
default: fz_paint_span_with_color_N(dp, mp, n, w, color); break;
}
}
 
/* Blend source in mask over destination */
 
static inline void
fz_paint_span_with_mask_2(byte * restrict dp, byte * restrict sp, byte * restrict mp, int w)
{
while (w--)
{
int masa;
int ma = *mp++;
ma = FZ_EXPAND(ma);
masa = FZ_COMBINE(sp[1], ma);
masa = 255 - masa;
masa = FZ_EXPAND(masa);
*dp = FZ_COMBINE2(*sp, ma, *dp, masa);
sp++; dp++;
*dp = FZ_COMBINE2(*sp, ma, *dp, masa);
sp++; dp++;
}
}
 
static inline void
fz_paint_span_with_mask_4(byte * restrict dp, byte * restrict sp, byte * restrict mp, int w)
{
while (w--)
{
int masa;
int ma = *mp++;
ma = FZ_EXPAND(ma);
masa = FZ_COMBINE(sp[3], ma);
masa = 255 - masa;
masa = FZ_EXPAND(masa);
*dp = FZ_COMBINE2(*sp, ma, *dp, masa);
sp++; dp++;
*dp = FZ_COMBINE2(*sp, ma, *dp, masa);
sp++; dp++;
*dp = FZ_COMBINE2(*sp, ma, *dp, masa);
sp++; dp++;
*dp = FZ_COMBINE2(*sp, ma, *dp, masa);
sp++; dp++;
}
}
 
static inline void
fz_paint_span_with_mask_N(byte * restrict dp, byte * restrict sp, byte * restrict mp, int n, int w)
{
while (w--)
{
int k = n;
int masa;
int ma = *mp++;
ma = FZ_EXPAND(ma);
masa = FZ_COMBINE(sp[n-1], ma);
masa = 255-masa;
masa = FZ_EXPAND(masa);
while (k--)
{
*dp = FZ_COMBINE2(*sp, ma, *dp, masa);
sp++; dp++;
}
}
}
 
static void
fz_paint_span_with_mask(byte * restrict dp, byte * restrict sp, byte * restrict mp, int n, int w)
{
switch (n)
{
case 2: fz_paint_span_with_mask_2(dp, sp, mp, w); break;
case 4: fz_paint_span_with_mask_4(dp, sp, mp, w); break;
default: fz_paint_span_with_mask_N(dp, sp, mp, n, w); break;
}
}
 
/* Blend source in constant alpha over destination */
 
static inline void
fz_paint_span_2_with_alpha(byte * restrict dp, byte * restrict sp, int w, int alpha)
{
alpha = FZ_EXPAND(alpha);
while (w--)
{
int masa = FZ_COMBINE(sp[1], alpha);
*dp = FZ_BLEND(*sp, *dp, masa);
dp++; sp++;
*dp = FZ_BLEND(*sp, *dp, masa);
dp++; sp++;
}
}
 
static inline void
fz_paint_span_4_with_alpha(byte * restrict dp, byte * restrict sp, int w, int alpha)
{
alpha = FZ_EXPAND(alpha);
while (w--)
{
int masa = FZ_COMBINE(sp[3], alpha);
*dp = FZ_BLEND(*sp, *dp, masa);
sp++; dp++;
*dp = FZ_BLEND(*sp, *dp, masa);
sp++; dp++;
*dp = FZ_BLEND(*sp, *dp, masa);
sp++; dp++;
*dp = FZ_BLEND(*sp, *dp, masa);
sp++; dp++;
}
}
 
static inline void
fz_paint_span_N_with_alpha(byte * restrict dp, byte * restrict sp, int n, int w, int alpha)
{
alpha = FZ_EXPAND(alpha);
while (w--)
{
int masa = FZ_COMBINE(sp[n-1], alpha);
int k = n;
while (k--)
{
*dp = FZ_BLEND(*sp++, *dp, masa);
dp++;
}
}
}
 
/* Blend source over destination */
 
static inline void
fz_paint_span_1(byte * restrict dp, byte * restrict sp, int w)
{
while (w--)
{
int t = FZ_EXPAND(255 - sp[0]);
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp ++;
}
}
 
static inline void
fz_paint_span_2(byte * restrict dp, byte * restrict sp, int w)
{
while (w--)
{
int t = FZ_EXPAND(255 - sp[1]);
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp++;
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp++;
}
}
 
static inline void
fz_paint_span_4(byte * restrict dp, byte * restrict sp, int w)
{
while (w--)
{
int t = FZ_EXPAND(255 - sp[3]);
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp++;
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp++;
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp++;
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp++;
}
}
 
static inline void
fz_paint_span_N(byte * restrict dp, byte * restrict sp, int n, int w)
{
while (w--)
{
int k = n;
int t = FZ_EXPAND(255 - sp[n-1]);
while (k--)
{
*dp = *sp++ + FZ_COMBINE(*dp, t);
dp++;
}
}
}
 
void
fz_paint_span(byte * restrict dp, byte * restrict sp, int n, int w, int alpha)
{
if (alpha == 255)
{
switch (n)
{
case 1: fz_paint_span_1(dp, sp, w); break;
case 2: fz_paint_span_2(dp, sp, w); break;
case 4: fz_paint_span_4(dp, sp, w); break;
default: fz_paint_span_N(dp, sp, n, w); break;
}
}
else if (alpha > 0)
{
switch (n)
{
case 2: fz_paint_span_2_with_alpha(dp, sp, w, alpha); break;
case 4: fz_paint_span_4_with_alpha(dp, sp, w, alpha); break;
default: fz_paint_span_N_with_alpha(dp, sp, n, w, alpha); break;
}
}
}
 
/*
* Pixmap blending functions
*/
 
void
fz_paint_pixmap_with_rect(fz_pixmap *dst, fz_pixmap *src, int alpha, fz_bbox bbox)
{
unsigned char *sp, *dp;
int x, y, w, h, n;
 
assert(dst->n == src->n);
 
bbox = fz_intersect_bbox(bbox, fz_bound_pixmap(dst));
bbox = fz_intersect_bbox(bbox, fz_bound_pixmap(src));
 
x = bbox.x0;
y = bbox.y0;
w = bbox.x1 - bbox.x0;
h = bbox.y1 - bbox.y0;
if ((w | h) == 0)
return;
 
n = src->n;
sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * src->n;
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
 
while (h--)
{
fz_paint_span(dp, sp, n, w, alpha);
sp += src->w * n;
dp += dst->w * n;
}
}
 
void
fz_paint_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha)
{
unsigned char *sp, *dp;
fz_bbox bbox;
int x, y, w, h, n;
 
assert(dst->n == src->n);
 
bbox = fz_bound_pixmap(dst);
bbox = fz_intersect_bbox(bbox, fz_bound_pixmap(src));
 
x = bbox.x0;
y = bbox.y0;
w = bbox.x1 - bbox.x0;
h = bbox.y1 - bbox.y0;
if ((w | h) == 0)
return;
 
n = src->n;
sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * src->n;
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
 
while (h--)
{
fz_paint_span(dp, sp, n, w, alpha);
sp += src->w * n;
dp += dst->w * n;
}
}
 
void
fz_paint_pixmap_with_mask(fz_pixmap *dst, fz_pixmap *src, fz_pixmap *msk)
{
unsigned char *sp, *dp, *mp;
fz_bbox bbox;
int x, y, w, h, n;
 
assert(dst->n == src->n);
assert(msk->n == 1);
 
bbox = fz_bound_pixmap(dst);
bbox = fz_intersect_bbox(bbox, fz_bound_pixmap(src));
bbox = fz_intersect_bbox(bbox, fz_bound_pixmap(msk));
 
x = bbox.x0;
y = bbox.y0;
w = bbox.x1 - bbox.x0;
h = bbox.y1 - bbox.y0;
if ((w | h) == 0)
return;
 
n = src->n;
sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * src->n;
mp = msk->samples + ((y - msk->y) * msk->w + (x - msk->x)) * msk->n;
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
 
while (h--)
{
fz_paint_span_with_mask(dp, sp, mp, n, w);
sp += src->w * n;
dp += dst->w * n;
mp += msk->w;
}
}
/contrib/media/updf/draw/draw_path.c
0,0 → 1,807
#include "fitz.h"
 
#define MAX_DEPTH 8
 
enum { BUTT = 0, ROUND = 1, SQUARE = 2, TRIANGLE = 3, MITER = 0, BEVEL = 2 };
 
static void
line(fz_gel *gel, fz_matrix *ctm, float x0, float y0, float x1, float y1)
{
float tx0 = ctm->a * x0 + ctm->c * y0 + ctm->e;
float ty0 = ctm->b * x0 + ctm->d * y0 + ctm->f;
float tx1 = ctm->a * x1 + ctm->c * y1 + ctm->e;
float ty1 = ctm->b * x1 + ctm->d * y1 + ctm->f;
fz_insert_gel(gel, tx0, ty0, tx1, ty1);
}
 
static void
bezier(fz_gel *gel, fz_matrix *ctm, float flatness,
float xa, float ya,
float xb, float yb,
float xc, float yc,
float xd, float yd, int depth)
{
float dmax;
float xab, yab;
float xbc, ybc;
float xcd, ycd;
float xabc, yabc;
float xbcd, ybcd;
float xabcd, yabcd;
 
/* termination check */
dmax = ABS(xa - xb);
dmax = MAX(dmax, ABS(ya - yb));
dmax = MAX(dmax, ABS(xd - xc));
dmax = MAX(dmax, ABS(yd - yc));
if (dmax < flatness || depth >= MAX_DEPTH)
{
line(gel, ctm, xa, ya, xd, yd);
return;
}
 
xab = xa + xb;
yab = ya + yb;
xbc = xb + xc;
ybc = yb + yc;
xcd = xc + xd;
ycd = yc + yd;
 
xabc = xab + xbc;
yabc = yab + ybc;
xbcd = xbc + xcd;
ybcd = ybc + ycd;
 
xabcd = xabc + xbcd;
yabcd = yabc + ybcd;
 
xab *= 0.5f; yab *= 0.5f;
xbc *= 0.5f; ybc *= 0.5f;
xcd *= 0.5f; ycd *= 0.5f;
 
xabc *= 0.25f; yabc *= 0.25f;
xbcd *= 0.25f; ybcd *= 0.25f;
 
xabcd *= 0.125f; yabcd *= 0.125f;
 
bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
}
 
void
fz_flatten_fill_path(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness)
{
float x1, y1, x2, y2, x3, y3;
float cx = 0;
float cy = 0;
float bx = 0;
float by = 0;
int i = 0;
 
while (i < path->len)
{
switch (path->items[i++].k)
{
case FZ_MOVETO:
/* implicit closepath before moveto */
if (i && (cx != bx || cy != by))
line(gel, &ctm, cx, cy, bx, by);
x1 = path->items[i++].v;
y1 = path->items[i++].v;
cx = bx = x1;
cy = by = y1;
break;
 
case FZ_LINETO:
x1 = path->items[i++].v;
y1 = path->items[i++].v;
line(gel, &ctm, cx, cy, x1, y1);
cx = x1;
cy = y1;
break;
 
case FZ_CURVETO:
x1 = path->items[i++].v;
y1 = path->items[i++].v;
x2 = path->items[i++].v;
y2 = path->items[i++].v;
x3 = path->items[i++].v;
y3 = path->items[i++].v;
bezier(gel, &ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3, 0);
cx = x3;
cy = y3;
break;
 
case FZ_CLOSE_PATH:
line(gel, &ctm, cx, cy, bx, by);
cx = bx;
cy = by;
break;
}
}
 
if (i && (cx != bx || cy != by))
line(gel, &ctm, cx, cy, bx, by);
}
 
struct sctx
{
fz_gel *gel;
fz_matrix *ctm;
float flatness;
 
int linejoin;
float linewidth;
float miterlimit;
fz_point beg[2];
fz_point seg[2];
int sn, bn;
int dot;
 
float *dash_list;
float dash_phase;
int dash_len;
int toggle, cap;
int offset;
float phase;
fz_point cur;
};
 
static void
fz_add_line(struct sctx *s, float x0, float y0, float x1, float y1)
{
float tx0 = s->ctm->a * x0 + s->ctm->c * y0 + s->ctm->e;
float ty0 = s->ctm->b * x0 + s->ctm->d * y0 + s->ctm->f;
float tx1 = s->ctm->a * x1 + s->ctm->c * y1 + s->ctm->e;
float ty1 = s->ctm->b * x1 + s->ctm->d * y1 + s->ctm->f;
fz_insert_gel(s->gel, tx0, ty0, tx1, ty1);
}
 
static void
fz_add_arc(struct sctx *s,
float xc, float yc,
float x0, float y0,
float x1, float y1)
{
float th0, th1, r;
float theta;
float ox, oy, nx, ny;
int n, i;
 
r = fabsf(s->linewidth);
theta = 2 * (float)M_SQRT2 * sqrtf(s->flatness / r);
th0 = atan2f(y0, x0);
th1 = atan2f(y1, x1);
 
if (r > 0)
{
if (th0 < th1)
th0 += (float)M_PI * 2;
n = ceilf((th0 - th1) / theta);
}
else
{
if (th1 < th0)
th1 += (float)M_PI * 2;
n = ceilf((th1 - th0) / theta);
}
 
ox = x0;
oy = y0;
for (i = 1; i < n; i++)
{
theta = th0 + (th1 - th0) * i / n;
nx = cosf(theta) * r;
ny = sinf(theta) * r;
fz_add_line(s, xc + ox, yc + oy, xc + nx, yc + ny);
ox = nx;
oy = ny;
}
 
fz_add_line(s, xc + ox, yc + oy, xc + x1, yc + y1);
}
 
static void
fz_add_line_stroke(struct sctx *s, fz_point a, fz_point b)
{
float dx = b.x - a.x;
float dy = b.y - a.y;
float scale = s->linewidth / sqrtf(dx * dx + dy * dy);
float dlx = dy * scale;
float dly = -dx * scale;
fz_add_line(s, a.x - dlx, a.y - dly, b.x - dlx, b.y - dly);
fz_add_line(s, b.x + dlx, b.y + dly, a.x + dlx, a.y + dly);
}
 
static void
fz_add_line_join(struct sctx *s, fz_point a, fz_point b, fz_point c)
{
float miterlimit = s->miterlimit;
float linewidth = s->linewidth;
int linejoin = s->linejoin;
float dx0, dy0;
float dx1, dy1;
float dlx0, dly0;
float dlx1, dly1;
float dmx, dmy;
float dmr2;
float scale;
float cross;
 
dx0 = b.x - a.x;
dy0 = b.y - a.y;
 
dx1 = c.x - b.x;
dy1 = c.y - b.y;
 
if (dx0 * dx0 + dy0 * dy0 < FLT_EPSILON)
linejoin = BEVEL;
if (dx1 * dx1 + dy1 * dy1 < FLT_EPSILON)
linejoin = BEVEL;
 
scale = linewidth / sqrtf(dx0 * dx0 + dy0 * dy0);
dlx0 = dy0 * scale;
dly0 = -dx0 * scale;
 
scale = linewidth / sqrtf(dx1 * dx1 + dy1 * dy1);
dlx1 = dy1 * scale;
dly1 = -dx1 * scale;
 
cross = dx1 * dy0 - dx0 * dy1;
 
dmx = (dlx0 + dlx1) * 0.5f;
dmy = (dly0 + dly1) * 0.5f;
dmr2 = dmx * dmx + dmy * dmy;
 
if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0)
linejoin = BEVEL;
 
if (linejoin == MITER)
if (dmr2 * miterlimit * miterlimit < linewidth * linewidth)
linejoin = BEVEL;
 
if (linejoin == BEVEL)
{
fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1);
fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0);
}
 
if (linejoin == MITER)
{
scale = linewidth * linewidth / dmr2;
dmx *= scale;
dmy *= scale;
 
if (cross < 0)
{
fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1);
fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dmx, b.y + dmy);
fz_add_line(s, b.x + dmx, b.y + dmy, b.x + dlx0, b.y + dly0);
}
else
{
fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0);
fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy);
fz_add_line(s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1);
}
}
 
if (linejoin == ROUND)
{
if (cross < 0)
{
fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1);
fz_add_arc(s, b.x, b.y, dlx1, dly1, dlx0, dly0);
}
else
{
fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0);
fz_add_arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1);
}
}
}
 
static void
fz_add_line_cap(struct sctx *s, fz_point a, fz_point b, int linecap)
{
float flatness = s->flatness;
float linewidth = s->linewidth;
 
float dx = b.x - a.x;
float dy = b.y - a.y;
 
float scale = linewidth / sqrtf(dx * dx + dy * dy);
float dlx = dy * scale;
float dly = -dx * scale;
 
if (linecap == BUTT)
fz_add_line(s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly);
 
if (linecap == ROUND)
{
int i;
int n = ceilf((float)M_PI / (2.0f * (float)M_SQRT2 * sqrtf(flatness / linewidth)));
float ox = b.x - dlx;
float oy = b.y - dly;
for (i = 1; i < n; i++)
{
float theta = (float)M_PI * i / n;
float cth = cosf(theta);
float sth = sinf(theta);
float nx = b.x - dlx * cth - dly * sth;
float ny = b.y - dly * cth + dlx * sth;
fz_add_line(s, ox, oy, nx, ny);
ox = nx;
oy = ny;
}
fz_add_line(s, ox, oy, b.x + dlx, b.y + dly);
}
 
if (linecap == SQUARE)
{
fz_add_line(s, b.x - dlx, b.y - dly,
b.x - dlx - dly, b.y - dly + dlx);
fz_add_line(s, b.x - dlx - dly, b.y - dly + dlx,
b.x + dlx - dly, b.y + dly + dlx);
fz_add_line(s, b.x + dlx - dly, b.y + dly + dlx,
b.x + dlx, b.y + dly);
}
 
if (linecap == TRIANGLE)
{
float mx = -dly;
float my = dlx;
fz_add_line(s, b.x - dlx, b.y - dly, b.x + mx, b.y + my);
fz_add_line(s, b.x + mx, b.y + my, b.x + dlx, b.y + dly);
}
}
 
static void
fz_add_line_dot(struct sctx *s, fz_point a)
{
float flatness = s->flatness;
float linewidth = s->linewidth;
int n = ceilf((float)M_PI / ((float)M_SQRT2 * sqrtf(flatness / linewidth)));
float ox = a.x - linewidth;
float oy = a.y;
int i;
 
for (i = 1; i < n; i++)
{
float theta = (float)M_PI * 2 * i / n;
float cth = cosf(theta);
float sth = sinf(theta);
float nx = a.x - cth * linewidth;
float ny = a.y + sth * linewidth;
fz_add_line(s, ox, oy, nx, ny);
ox = nx;
oy = ny;
}
 
fz_add_line(s, ox, oy, a.x - linewidth, a.y);
}
 
static void
fz_stroke_flush(struct sctx *s, int start_cap, int end_cap)
{
if (s->sn == 2)
{
fz_add_line_cap(s, s->beg[1], s->beg[0], start_cap);
fz_add_line_cap(s, s->seg[0], s->seg[1], end_cap);
}
else if (s->dot)
{
fz_add_line_dot(s, s->beg[0]);
}
}
 
static void
fz_stroke_moveto(struct sctx *s, fz_point cur)
{
s->seg[0] = cur;
s->beg[0] = cur;
s->sn = 1;
s->bn = 1;
s->dot = 0;
}
 
static void
fz_stroke_lineto(struct sctx *s, fz_point cur)
{
float dx = cur.x - s->seg[s->sn-1].x;
float dy = cur.y - s->seg[s->sn-1].y;
 
if (dx * dx + dy * dy < FLT_EPSILON)
{
if (s->cap == ROUND || s->dash_list)
s->dot = 1;
return;
}
 
fz_add_line_stroke(s, s->seg[s->sn-1], cur);
 
if (s->sn == 2)
{
fz_add_line_join(s, s->seg[0], s->seg[1], cur);
s->seg[0] = s->seg[1];
s->seg[1] = cur;
}
 
if (s->sn == 1)
s->seg[s->sn++] = cur;
if (s->bn == 1)
s->beg[s->bn++] = cur;
}
 
static void
fz_stroke_closepath(struct sctx *s)
{
if (s->sn == 2)
{
fz_stroke_lineto(s, s->beg[0]);
if (s->seg[1].x == s->beg[0].x && s->seg[1].y == s->beg[0].y)
fz_add_line_join(s, s->seg[0], s->beg[0], s->beg[1]);
else
fz_add_line_join(s, s->seg[1], s->beg[0], s->beg[1]);
}
else if (s->dot)
{
fz_add_line_dot(s, s->beg[0]);
}
 
s->seg[0] = s->beg[0];
s->bn = 1;
s->sn = 1;
s->dot = 0;
}
 
static void
fz_stroke_bezier(struct sctx *s,
float xa, float ya,
float xb, float yb,
float xc, float yc,
float xd, float yd, int depth)
{
float dmax;
float xab, yab;
float xbc, ybc;
float xcd, ycd;
float xabc, yabc;
float xbcd, ybcd;
float xabcd, yabcd;
 
/* termination check */
dmax = ABS(xa - xb);
dmax = MAX(dmax, ABS(ya - yb));
dmax = MAX(dmax, ABS(xd - xc));
dmax = MAX(dmax, ABS(yd - yc));
if (dmax < s->flatness || depth >= MAX_DEPTH)
{
fz_point p;
p.x = xd;
p.y = yd;
fz_stroke_lineto(s, p);
return;
}
 
xab = xa + xb;
yab = ya + yb;
xbc = xb + xc;
ybc = yb + yc;
xcd = xc + xd;
ycd = yc + yd;
 
xabc = xab + xbc;
yabc = yab + ybc;
xbcd = xbc + xcd;
ybcd = ybc + ycd;
 
xabcd = xabc + xbcd;
yabcd = yabc + ybcd;
 
xab *= 0.5f; yab *= 0.5f;
xbc *= 0.5f; ybc *= 0.5f;
xcd *= 0.5f; ycd *= 0.5f;
 
xabc *= 0.25f; yabc *= 0.25f;
xbcd *= 0.25f; ybcd *= 0.25f;
 
xabcd *= 0.125f; yabcd *= 0.125f;
 
fz_stroke_bezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
fz_stroke_bezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
}
 
void
fz_flatten_stroke_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth)
{
struct sctx s;
fz_point p0, p1, p2, p3;
int i;
 
s.gel = gel;
s.ctm = &ctm;
s.flatness = flatness;
 
s.linejoin = stroke->linejoin;
s.linewidth = linewidth * 0.5f; /* hairlines use a different value from the path value */
s.miterlimit = stroke->miterlimit;
s.sn = 0;
s.bn = 0;
s.dot = 0;
 
s.dash_list = NULL;
s.dash_phase = 0;
s.dash_len = 0;
s.toggle = 0;
s.offset = 0;
s.phase = 0;
 
s.cap = stroke->start_cap;
 
i = 0;
 
if (path->len > 0 && path->items[0].k != FZ_MOVETO)
{
fz_warn("assert: path must begin with moveto");
return;
}
 
p0.x = p0.y = 0;
 
while (i < path->len)
{
switch (path->items[i++].k)
{
case FZ_MOVETO:
p1.x = path->items[i++].v;
p1.y = path->items[i++].v;
fz_stroke_flush(&s, stroke->start_cap, stroke->end_cap);
fz_stroke_moveto(&s, p1);
p0 = p1;
break;
 
case FZ_LINETO:
p1.x = path->items[i++].v;
p1.y = path->items[i++].v;
fz_stroke_lineto(&s, p1);
p0 = p1;
break;
 
case FZ_CURVETO:
p1.x = path->items[i++].v;
p1.y = path->items[i++].v;
p2.x = path->items[i++].v;
p2.y = path->items[i++].v;
p3.x = path->items[i++].v;
p3.y = path->items[i++].v;
fz_stroke_bezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, 0);
p0 = p3;
break;
 
case FZ_CLOSE_PATH:
fz_stroke_closepath(&s);
break;
}
}
 
fz_stroke_flush(&s, stroke->start_cap, stroke->end_cap);
}
 
static void
fz_dash_moveto(struct sctx *s, fz_point a, int start_cap, int end_cap)
{
s->toggle = 1;
s->offset = 0;
s->phase = s->dash_phase;
 
while (s->phase >= s->dash_list[s->offset])
{
s->toggle = !s->toggle;
s->phase -= s->dash_list[s->offset];
s->offset ++;
if (s->offset == s->dash_len)
s->offset = 0;
}
 
s->cur = a;
 
if (s->toggle)
{
fz_stroke_flush(s, s->cap, end_cap);
s->cap = start_cap;
fz_stroke_moveto(s, a);
}
}
 
static void
fz_dash_lineto(struct sctx *s, fz_point b, int dash_cap)
{
float dx, dy;
float total, used, ratio;
fz_point a;
fz_point m;
 
a = s->cur;
dx = b.x - a.x;
dy = b.y - a.y;
total = sqrtf(dx * dx + dy * dy);
used = 0;
 
while (total - used > s->dash_list[s->offset] - s->phase)
{
used += s->dash_list[s->offset] - s->phase;
ratio = used / total;
m.x = a.x + ratio * dx;
m.y = a.y + ratio * dy;
 
if (s->toggle)
{
fz_stroke_lineto(s, m);
}
else
{
fz_stroke_flush(s, s->cap, dash_cap);
s->cap = dash_cap;
fz_stroke_moveto(s, m);
}
 
s->toggle = !s->toggle;
s->phase = 0;
s->offset ++;
if (s->offset == s->dash_len)
s->offset = 0;
}
 
s->phase += total - used;
 
s->cur = b;
 
if (s->toggle)
{
fz_stroke_lineto(s, b);
}
}
 
static void
fz_dash_bezier(struct sctx *s,
float xa, float ya,
float xb, float yb,
float xc, float yc,
float xd, float yd, int depth,
int dash_cap)
{
float dmax;
float xab, yab;
float xbc, ybc;
float xcd, ycd;
float xabc, yabc;
float xbcd, ybcd;
float xabcd, yabcd;
 
/* termination check */
dmax = ABS(xa - xb);
dmax = MAX(dmax, ABS(ya - yb));
dmax = MAX(dmax, ABS(xd - xc));
dmax = MAX(dmax, ABS(yd - yc));
if (dmax < s->flatness || depth >= MAX_DEPTH)
{
fz_point p;
p.x = xd;
p.y = yd;
fz_dash_lineto(s, p, dash_cap);
return;
}
 
xab = xa + xb;
yab = ya + yb;
xbc = xb + xc;
ybc = yb + yc;
xcd = xc + xd;
ycd = yc + yd;
 
xabc = xab + xbc;
yabc = yab + ybc;
xbcd = xbc + xcd;
ybcd = ybc + ycd;
 
xabcd = xabc + xbcd;
yabcd = yabc + ybcd;
 
xab *= 0.5f; yab *= 0.5f;
xbc *= 0.5f; ybc *= 0.5f;
xcd *= 0.5f; ycd *= 0.5f;
 
xabc *= 0.25f; yabc *= 0.25f;
xbcd *= 0.25f; ybcd *= 0.25f;
 
xabcd *= 0.125f; yabcd *= 0.125f;
 
fz_dash_bezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1, dash_cap);
fz_dash_bezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1, dash_cap);
}
 
void
fz_flatten_dash_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth)
{
struct sctx s;
fz_point p0, p1, p2, p3, beg;
float phase_len;
int i;
 
s.gel = gel;
s.ctm = &ctm;
s.flatness = flatness;
 
s.linejoin = stroke->linejoin;
s.linewidth = linewidth * 0.5f;
s.miterlimit = stroke->miterlimit;
s.sn = 0;
s.bn = 0;
s.dot = 0;
 
s.dash_list = stroke->dash_list;
s.dash_phase = stroke->dash_phase;
s.dash_len = stroke->dash_len;
s.toggle = 0;
s.offset = 0;
s.phase = 0;
 
s.cap = stroke->start_cap;
 
if (path->len > 0 && path->items[0].k != FZ_MOVETO)
{
fz_warn("assert: path must begin with moveto");
return;
}
 
phase_len = 0;
for (i = 0; i < stroke->dash_len; i++)
phase_len += stroke->dash_list[i];
if (phase_len < 0.01f || phase_len < stroke->linewidth * 0.5f)
{
fz_flatten_stroke_path(gel, path, stroke, ctm, flatness, linewidth);
return;
}
 
p0.x = p0.y = 0;
i = 0;
 
while (i < path->len)
{
switch (path->items[i++].k)
{
case FZ_MOVETO:
p1.x = path->items[i++].v;
p1.y = path->items[i++].v;
fz_dash_moveto(&s, p1, stroke->start_cap, stroke->end_cap);
beg = p0 = p1;
break;
 
case FZ_LINETO:
p1.x = path->items[i++].v;
p1.y = path->items[i++].v;
fz_dash_lineto(&s, p1, stroke->dash_cap);
p0 = p1;
break;
 
case FZ_CURVETO:
p1.x = path->items[i++].v;
p1.y = path->items[i++].v;
p2.x = path->items[i++].v;
p2.y = path->items[i++].v;
p3.x = path->items[i++].v;
p3.y = path->items[i++].v;
fz_dash_bezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, 0, stroke->dash_cap);
p0 = p3;
break;
 
case FZ_CLOSE_PATH:
fz_dash_lineto(&s, beg, stroke->dash_cap);
p0 = p1 = beg;
break;
}
}
 
fz_stroke_flush(&s, s.cap, stroke->end_cap);
}
/contrib/media/updf/draw/draw_scale.c
0,0 → 1,1250
/*
This code does smooth scaling of a pixmap.
 
This function returns a new pixmap representing the area starting at (0,0)
given by taking the source pixmap src, scaling it to width w, and height h,
and then positioning it at (frac(x),frac(y)).
*/
 
#include "fitz.h"
 
/* Do we special case handling of single pixel high/wide images? The
* 'purest' handling is given by not special casing them, but certain
* files that use such images 'stack' them to give full images. Not
* special casing them results in then being fainter and giving noticable
* rounding errors.
*/
#define SINGLE_PIXEL_SPECIALS
 
#ifdef DEBUG_SCALING
#ifdef WIN32
#include <windows.h>
static void debug_print(const char *fmt, ...)
{
va_list args;
char text[256];
va_start(args, fmt);
vsprintf(text, fmt, args);
va_end(args);
OutputDebugStringA(text);
printf(text);
}
#else
static void debug_print(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
#endif
#endif
#ifdef DEBUG_SCALING
#define DBUG(A) debug_print A
#else
#define DBUG(A) do {} while(0==1)
#endif
 
/*
Consider a row of source samples, src, of width src_w, positioned at x,
scaled to width dst_w.
 
src[i] is centred at: x + (i + 0.5)*dst_w/src_w
 
Therefore the distance between the centre of the jth output pixel and
the centre of the ith source sample is:
 
dist[j,i] = j + 0.5 - (x + (i + 0.5)*dst_w/src_w)
 
When scaling up, therefore:
 
dst[j] = SUM(filter(dist[j,i]) * src[i])
(for all ints i)
 
This can be simplified by noticing that filters are only non zero within
a given filter width (henceforth called W). So:
 
dst[j] = SUM(filter(dist[j,i]) * src[i])
(for ints i, s.t. (j*src_w/dst_w)-W < i < (j*src_w/dst_w)+W)
 
When scaling down, each filtered source sample is stretched to be wider
to avoid aliasing issues. This effectively reduces the distance between
centres.
 
dst[j] = SUM(filter(dist[j,i] * F) * F * src[i])
(where F = dst_w/src_w)
(for ints i, s.t. (j-W)/F < i < (j+W)/F)
 
*/
 
typedef struct fz_scale_filter_s fz_scale_filter;
 
struct fz_scale_filter_s
{
int width;
float (*fn)(fz_scale_filter *, float);
};
 
/* Image scale filters */
 
static float
triangle(fz_scale_filter *filter, float f)
{
if (f >= 1)
return 0;
return 1-f;
}
 
static float
box(fz_scale_filter *filter, float f)
{
if (f >= 0.5f)
return 0;
return 1;
}
 
static float
simple(fz_scale_filter *filter, float x)
{
if (x >= 1)
return 0;
return 1 + (2*x - 3)*x*x;
}
 
static float
lanczos2(fz_scale_filter *filter, float x)
{
if (x >= 2)
return 0;
return sinf(M_PI*x) * sinf(M_PI*x/2) / (M_PI*x) / (M_PI*x/2);
}
 
static float
lanczos3(fz_scale_filter *filter, float f)
{
if (f >= 3)
return 0;
return sinf(M_PI*f) * sinf(M_PI*f/3) / (M_PI*f) / (M_PI*f/3);
}
 
/*
The Mitchell family of filters is defined:
 
f(x) = 1 { (12-9B-6C)x^3 + (-18+12B+6C)x^2 + (6-2B) for x < 1
- {
6 { (-B-6C)x^3+(6B+30C)x^2+(-12B-48C)x+(8B+24C) for 1<=x<=2
 
The 'best' ones lie along the line B+2C = 1.
The literature suggests that B=1/3, C=1/3 is best.
 
f(x) = 1 { (12-3-2)x^3 - (-18+4+2)x^2 + (16/3) for x < 1
- {
6 { (-7/3)x^3 + 12x^2 - 20x + (32/3) for 1<=x<=2
 
f(x) = 1 { 21x^3 - 36x^2 + 16 for x < 1
- {
18{ -7x^3 + 36x^2 - 60x + 32 for 1<=x<=2
*/
 
static float
mitchell(fz_scale_filter *filter, float x)
{
if (x >= 2)
return 0;
if (x >= 1)
return (32 + x*(-60 + x*(36 - 7*x)))/18;
return (16 + x*x*(-36 + 21*x))/18;
}
 
fz_scale_filter fz_scale_filter_box = { 1, box };
fz_scale_filter fz_scale_filter_triangle = { 1, triangle };
fz_scale_filter fz_scale_filter_simple = { 1, simple };
fz_scale_filter fz_scale_filter_lanczos2 = { 2, lanczos2 };
fz_scale_filter fz_scale_filter_lanczos3 = { 3, lanczos3 };
fz_scale_filter fz_scale_filter_mitchell = { 2, mitchell };
 
/*
We build ourselves a set of tables to contain the precalculated weights
for a given set of scale settings.
 
The first dst_w entries in index are the index into index of the
sets of weight for each destination pixel.
 
Each of the sets of weights is a set of values consisting of:
the minimum source pixel index used for this destination pixel
the number of weights used for this destination pixel
the weights themselves
 
So to calculate dst[i] we do the following:
 
weights = &index[index[i]];
min = *weights++;
len = *weights++;
dst[i] = 0;
while (--len > 0)
dst[i] += src[min++] * *weights++
 
in addition, we guarantee that at the end of this process weights will now
point to the weights value for dst pixel i+1.
 
In the simplest version of this algorithm, we would scale the whole image
horizontally first into a temporary buffer, then scale that temporary
buffer again vertically to give us our result. Using such a simple
algorithm would mean that could use the same style of weights for both
horizontal and vertical scaling.
 
Unfortunately, this would also require a large temporary buffer,
particularly in the case where we are scaling up.
 
We therefore modify the algorithm as follows; we scale scanlines from the
source image horizontally into a temporary buffer, until we have all the
contributors for a given output scanline. We then produce that output
scanline from the temporary buffer. In this way we restrict the height
of the temporary buffer to a small fraction of the final size.
 
Unfortunately, this means that the pseudo code for recombining a
scanline of fully scaled pixels is as follows:
 
weights = &index[index[y]];
min = *weights++;
len = *weights++;
for (x=0 to dst_w)
min2 = min
len2 = len
weights2 = weights
dst[x] = 0;
while (--len2 > 0)
dst[x] += temp[x][(min2++) % tmp_buf_height] * *weights2++
 
i.e. it requires a % operation for every source pixel - this is typically
expensive.
 
To avoid this, we alter the order in which vertical weights are stored,
so that they are ordered in the same order as the temporary buffer lines
would appear. This simplifies the algorithm to:
 
weights = &index[index[y]];
min = *weights++;
len = *weights++;
for (x=0 to dst_w)
min2 = 0
len2 = len
weights2 = weights
dst[x] = 0;
while (--len2 > 0)
dst[x] += temp[i][min2++] * *weights2++
 
This means that len may be larger than it needs to be (due to the
possible inclusion of a zero weight row or two), but in practise this
is only an increase of 1 or 2 at worst.
 
We implement this by generating the weights as normal (but ensuring we
leave enough space) and then reordering afterwards.
 
*/
 
typedef struct fz_weights_s fz_weights;
 
struct fz_weights_s
{
int count;
int max_len;
int n;
int flip;
int new_line;
int index[1];
};
 
static fz_weights *
new_weights(fz_scale_filter *filter, int src_w, float dst_w, int dst_w_i, int n, int flip)
{
int max_len;
fz_weights *weights;
 
if (src_w > dst_w)
{
/* Scaling down, so there will be a maximum of
* 2*filterwidth*src_w/dst_w src pixels
* contributing to each dst pixel. */
max_len = (int)ceilf((2 * filter->width * src_w)/dst_w);
if (max_len > src_w)
max_len = src_w;
}
else
{
/* Scaling up, so there will be a maximum of
* 2*filterwidth src pixels contributing to each dst pixel.
*/
max_len = 2 * filter->width;
}
/* We need the size of the struct,
* plus dst_w*sizeof(int) for the index
* plus (2+max_len)*sizeof(int) for the weights
* plus room for an extra set of weights for reordering.
*/
weights = fz_malloc(sizeof(*weights)+(max_len+3)*(dst_w_i+1)*sizeof(int));
if (weights == NULL)
return NULL;
weights->count = -1;
weights->max_len = max_len;
weights->index[0] = dst_w_i;
weights->n = n;
weights->flip = flip;
return weights;
}
 
static void
init_weights(fz_weights *weights, int j)
{
int index;
 
assert(weights->count == j-1);
weights->count++;
weights->new_line = 1;
if (j == 0)
index = weights->index[0];
else
{
index = weights->index[j-1];
index += 2 + weights->index[index+1];
}
weights->index[j] = index; /* row pointer */
weights->index[index] = 0; /* min */
weights->index[index+1] = 0; /* len */
}
 
static void
add_weight(fz_weights *weights, int j, int i, fz_scale_filter *filter,
float x, float F, float G, int src_w, float dst_w)
{
float dist = j - x + 0.5f - ((i + 0.5f)*dst_w/src_w);
float f;
int min, len, index, weight;
 
dist *= G;
if (dist < 0)
dist = -dist;
f = filter->fn(filter, dist)*F;
weight = (int)(256*f+0.5f);
if (weight == 0)
return;
 
/* wrap i back into range */
#ifdef MIRROR_WRAP
do
{
if (i < 0)
i = -1-i;
else if (i >= src_w)
i = 2*src_w-1-i;
else
break;
}
while (1);
#elif defined(WRAP)
if (i < 0)
i = 0;
else if (i >= src_w)
i = src_w-1;
#else
if (i < 0)
{
i = 0;
weight = 0;
}
else if (i >= src_w)
{
i = src_w-1;
weight = 0;
}
if (weight == 0)
return;
#endif
 
DBUG(("add_weight[%d][%d] = %d(%g) dist=%g\n",j,i,weight,f,dist));
 
if (weights->new_line)
{
/* New line */
weights->new_line = 0;
index = weights->index[j]; /* row pointer */
weights->index[index] = i; /* min */
weights->index[index+1] = 0; /* len */
}
index = weights->index[j];
min = weights->index[index++];
len = weights->index[index++];
while (i < min)
{
/* This only happens in rare cases, but we need to insert
* one earlier. In exceedingly rare cases we may need to
* insert more than one earlier. */
int k;
 
for (k = len; k > 0; k--)
{
weights->index[index+k] = weights->index[index+k-1];
}
weights->index[index] = 0;
min--;
len++;
weights->index[index-2] = min;
weights->index[index-1] = len;
}
if (i-min >= len)
{
/* The usual case */
while (i-min >= ++len)
{
weights->index[index+len-1] = 0;
}
assert(len-1 == i-min);
weights->index[index+i-min] = weight;
weights->index[index-1] = len;
assert(len <= weights->max_len);
}
else
{
/* Infrequent case */
weights->index[index+i-min] += weight;
}
}
 
static void
reorder_weights(fz_weights *weights, int j, int src_w)
{
int idx = weights->index[j];
int min = weights->index[idx++];
int len = weights->index[idx++];
int max = weights->max_len;
int tmp = idx+max;
int i, off;
 
/* Copy into the temporary area */
memcpy(&weights->index[tmp], &weights->index[idx], sizeof(int)*len);
 
/* Pad out if required */
assert(len <= max);
assert(min+len <= src_w);
off = 0;
if (len < max)
{
memset(&weights->index[tmp+len], 0, sizeof(int)*(max-len));
len = max;
if (min + len > src_w)
{
off = min + len - src_w;
min = src_w - len;
weights->index[idx-2] = min;
}
weights->index[idx-1] = len;
}
 
/* Copy back into the proper places */
for (i = 0; i < len; i++)
{
weights->index[idx+((min+i+off) % max)] = weights->index[tmp+i];
}
}
 
/* Due to rounding and edge effects, the sums for the weights sometimes don't
* add up to 256. This causes visible rendering effects. Therefore, we take
* pains to ensure that they 1) never exceed 256, and 2) add up to exactly
* 256 for all pixels that are completely covered. See bug #691629. */
static void
check_weights(fz_weights *weights, int j, int w, float x, float wf)
{
int idx, len;
int sum = 0;
int max = -256;
int maxidx = 0;
int i;
 
idx = weights->index[j];
idx++; /* min */
len = weights->index[idx++];
 
for(i=0; i < len; i++)
{
int v = weights->index[idx++];
sum += v;
if (v > max)
{
max = v;
maxidx = idx;
}
}
/* If we aren't the first or last pixel, OR if the sum is too big
* then adjust it. */
if (((j != 0) && (j != w-1)) || (sum > 256))
weights->index[maxidx-1] += 256-sum;
/* Otherwise, if we are the first pixel, and it's fully covered, then
* adjust it. */
else if ((j == 0) && (x < 0.0001F) && (sum != 256))
weights->index[maxidx-1] += 256-sum;
/* Finally, if we are the last pixel, and it's fully covered, then
* adjust it. */
else if ((j == w-1) && ((float)w-wf < 0.0001F) && (sum != 256))
weights->index[maxidx-1] += 256-sum;
DBUG(("total weight %d = %d\n", j, sum));
}
 
static fz_weights *
make_weights(int src_w, float x, float dst_w, fz_scale_filter *filter, int vertical, int dst_w_int, int n, int flip)
{
fz_weights *weights;
float F, G;
float window;
int j;
 
if (dst_w < src_w)
{
/* Scaling down */
F = dst_w / src_w;
G = 1;
}
else
{
/* Scaling up */
F = 1;
G = src_w / dst_w;
}
window = filter->width / F;
DBUG(("make_weights src_w=%d x=%g dst_w=%g dst_w_int=%d F=%g window=%g\n", src_w, x, dst_w, dst_w_int, F, window));
weights = new_weights(filter, src_w, dst_w, dst_w_int, n, flip);
if (weights == NULL)
return NULL;
for (j = 0; j < dst_w_int; j++)
{
/* find the position of the centre of dst[j] in src space */
float centre = (j - x + 0.5f)*src_w/dst_w - 0.5f;
int l, r;
l = ceilf(centre - window);
r = floorf(centre + window);
DBUG(("%d: centre=%g l=%d r=%d\n", j, centre, l, r));
init_weights(weights, j);
for (; l <= r; l++)
{
add_weight(weights, j, l, filter, x, F, G, src_w, dst_w);
}
check_weights(weights, j, dst_w_int, x, dst_w);
if (vertical)
{
reorder_weights(weights, j, src_w);
}
}
weights->count++; /* weights->count = dst_w_int now */
return weights;
}
 
static void
scale_row_to_temp(int *dst, unsigned char *src, fz_weights *weights)
{
int *contrib = &weights->index[weights->index[0]];
int len, i, j, n;
unsigned char *min;
 
n = weights->n;
if (weights->flip)
{
dst += (weights->count-1)*n;
for (i=weights->count; i > 0; i--)
{
min = &src[n * *contrib++];
len = *contrib++;
for (j = 0; j < n; j++)
dst[j] = 0;
while (len-- > 0)
{
for (j = n; j > 0; j--)
*dst++ += *min++ * *contrib;
dst -= n;
contrib++;
}
dst -= n;
}
}
else
{
for (i=weights->count; i > 0; i--)
{
min = &src[n * *contrib++];
len = *contrib++;
for (j = 0; j < n; j++)
dst[j] = 0;
while (len-- > 0)
{
for (j = n; j > 0; j--)
*dst++ += *min++ * *contrib;
dst -= n;
contrib++;
}
dst += n;
}
}
}
 
static void
scale_row_to_temp1(int *dst, unsigned char *src, fz_weights *weights)
{
int *contrib = &weights->index[weights->index[0]];
int len, i;
unsigned char *min;
 
assert(weights->n == 1);
if (weights->flip)
{
dst += weights->count;
for (i=weights->count; i > 0; i--)
{
int val = 0;
min = &src[*contrib++];
len = *contrib++;
while (len-- > 0)
{
val += *min++ * *contrib++;
}
*--dst = val;
}
}
else
{
for (i=weights->count; i > 0; i--)
{
int val = 0;
min = &src[*contrib++];
len = *contrib++;
while (len-- > 0)
{
val += *min++ * *contrib++;
}
*dst++ = val;
}
}
}
 
static void
scale_row_to_temp2(int *dst, unsigned char *src, fz_weights *weights)
{
int *contrib = &weights->index[weights->index[0]];
int len, i;
unsigned char *min;
 
assert(weights->n == 2);
if (weights->flip)
{
dst += 2*weights->count;
for (i=weights->count; i > 0; i--)
{
int c1 = 0;
int c2 = 0;
min = &src[2 * *contrib++];
len = *contrib++;
while (len-- > 0)
{
c1 += *min++ * *contrib;
c2 += *min++ * *contrib++;
}
*--dst = c2;
*--dst = c1;
}
}
else
{
for (i=weights->count; i > 0; i--)
{
int c1 = 0;
int c2 = 0;
min = &src[2 * *contrib++];
len = *contrib++;
while (len-- > 0)
{
c1 += *min++ * *contrib;
c2 += *min++ * *contrib++;
}
*dst++ = c1;
*dst++ = c2;
}
}
}
 
static void
scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights)
{
int *contrib = &weights->index[weights->index[0]];
#ifndef ARCH_ARM
int len, i;
unsigned char *min;
#endif
 
assert(weights->n == 4);
if (weights->flip)
{
dst += 4*weights->count;
#ifdef ARCH_ARM
asm volatile(
"1:"
"ldr r4, [%2], #4 @ r4 = *contrib++ \n"
"ldr r9, [%2], #4 @ r9 = len = *contrib++ \n"
"mov r5, #0 @ r5 = r = 0 \n"
"mov r6, #0 @ r6 = g = 0 \n"
"mov r7, #0 @ r7 = b = 0 \n"
"mov r8, #0 @ r8 = a = 0 \n"
"add r4, %1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
"cmp r9, #0 @ while (len-- > 0) \n"
"beq 3f @ { \n"
"2: \n"
"ldr r10,[%2], #4 @ r10 = *contrib++ \n"
"ldrb r11,[r4], #1 @ r11 = *min++ \n"
"ldrb r12,[r4], #1 @ r12 = *min++ \n"
"ldrb r14,[r4], #1 @ r14 = *min++ \n"
"mla r5, r10,r11,r5 @ r += r11 * r10 \n"
"ldrb r11,[r4], #1 @ r11 = *min++ \n"
"mla r6, r10,r12,r6 @ g += r12 * r10 \n"
"mla r7, r10,r14,r7 @ b += r14 * r10 \n"
"mla r8, r10,r11,r8 @ a += r11 * r10 \n"
"subs r9, r9, #1 @ r9 = len-- \n"
"bgt 2b @ } \n"
"stmdb %0!,{r5,r6,r7,r8} @ *--dst=a;*--dst=b; \n"
"3: @ *--dst=g;*--dst=r; \n"
"subs %3, %3, #1 @ i-- \n"
"bgt 1b @ \n"
:
:
"r" (dst),
"r" (src),
"r" (contrib),
"r" (weights->count)
:
"r4","r5","r6","r7","r8","r9","r10","r11","r12","r14",
"memory","cc"
);
#else
for (i=weights->count; i > 0; i--)
{
int r = 0;
int g = 0;
int b = 0;
int a = 0;
min = &src[4 * *contrib++];
len = *contrib++;
while (len-- > 0)
{
r += *min++ * *contrib;
g += *min++ * *contrib;
b += *min++ * *contrib;
a += *min++ * *contrib++;
}
*--dst = a;
*--dst = b;
*--dst = g;
*--dst = r;
}
#endif
}
else
{
#ifdef ARCH_ARM
asm volatile(
"1:"
"ldr r4, [%2], #4 @ r4 = *contrib++ \n"
"ldr r9, [%2], #4 @ r9 = len = *contrib++ \n"
"mov r5, #0 @ r5 = r = 0 \n"
"mov r6, #0 @ r6 = g = 0 \n"
"mov r7, #0 @ r7 = b = 0 \n"
"mov r8, #0 @ r8 = a = 0 \n"
"add r4, %1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
"cmp r9, #0 @ while (len-- > 0) \n"
"beq 3f @ { \n"
"2: \n"
"ldr r10,[%2], #4 @ r10 = *contrib++ \n"
"ldrb r11,[r4], #1 @ r11 = *min++ \n"
"ldrb r12,[r4], #1 @ r12 = *min++ \n"
"ldrb r14,[r4], #1 @ r14 = *min++ \n"
"mla r5, r10,r11,r5 @ r += r11 * r10 \n"
"ldrb r11,[r4], #1 @ r11 = *min++ \n"
"mla r6, r10,r12,r6 @ g += r12 * r10 \n"
"mla r7, r10,r14,r7 @ b += r14 * r10 \n"
"mla r8, r10,r11,r8 @ a += r11 * r10 \n"
"subs r9, r9, #1 @ r9 = len-- \n"
"bgt 2b @ } \n"
"stmia %0!,{r5,r6,r7,r8} @ *dst++=r;*dst++=g; \n"
"3: @ *dst++=b;*dst++=a; \n"
"subs %3, %3, #1 @ i-- \n"
"bgt 1b @ \n"
:
:
"r" (dst),
"r" (src),
"r" (contrib),
"r" (weights->count)
:
"r4","r5","r6","r7","r8","r9","r10","r11","r12","r14",
"memory","cc"
);
#else
for (i=weights->count; i > 0; i--)
{
int r = 0;
int g = 0;
int b = 0;
int a = 0;
min = &src[4 * *contrib++];
len = *contrib++;
while (len-- > 0)
{
r += *min++ * *contrib;
g += *min++ * *contrib;
b += *min++ * *contrib;
a += *min++ * *contrib++;
}
*dst++ = r;
*dst++ = g;
*dst++ = b;
*dst++ = a;
}
#endif
}
}
 
static void
scale_row_from_temp(unsigned char *dst, int *src, fz_weights *weights, int width, int row)
{
int *contrib = &weights->index[weights->index[row]];
int len, x;
 
contrib++; /* Skip min */
len = *contrib++;
for (x=width; x > 0; x--)
{
int *min = src;
int val = 0;
int len2 = len;
int *contrib2 = contrib;
 
while (len2-- > 0)
{
val += *min * *contrib2++;
min += width;
}
val = (val+(1<<15))>>16;
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
*dst++ = val;
src++;
}
}
 
#ifdef SINGLE_PIXEL_SPECIALS
static void
duplicate_single_pixel(unsigned char *dst, unsigned char *src, int n, int w, int h)
{
int i;
 
for (i = n; i > 0; i--)
*dst++ = *src++;
for (i = (w*h-1)*n; i > 0; i--)
{
*dst = dst[-n];
dst++;
}
}
 
static void
scale_single_row(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int h)
{
int *contrib = &weights->index[weights->index[0]];
int min, len, i, j, val, n;
int tmp[FZ_MAX_COLORS];
 
n = weights->n;
/* Scale a single row */
if (weights->flip)
{
dst += (weights->count-1)*n;
for (i=weights->count; i > 0; i--)
{
min = *contrib++;
len = *contrib++;
min *= n;
for (j = 0; j < n; j++)
tmp[j] = 0;
while (len-- > 0)
{
for (j = 0; j < n; j++)
tmp[j] += src[min++] * *contrib;
contrib++;
}
for (j = 0; j < n; j++)
{
val = (tmp[j]+(1<<7))>>8;
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
*dst++ = val;
}
dst -= 2*n;
}
dst += n * (weights->count+1);
}
else
{
for (i=weights->count; i > 0; i--)
{
min = *contrib++;
len = *contrib++;
min *= n;
for (j = 0; j < n; j++)
tmp[j] = 0;
while (len-- > 0)
{
for (j = 0; j < n; j++)
tmp[j] += src[min++] * *contrib;
contrib++;
}
for (j = 0; j < n; j++)
{
val = (tmp[j]+(1<<7))>>8;
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
*dst++ = val;
}
}
}
/* And then duplicate it h times */
n *= weights->count;
while (--h > 0)
{
memcpy(dst, dst-n, n);
dst += n;
}
}
 
static void
scale_single_col(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int n, int w, int flip_y)
{
int *contrib = &weights->index[weights->index[0]];
int min, len, i, j, val;
int tmp[FZ_MAX_COLORS];
 
if (flip_y)
{
src_w = (src_w-1)*n;
w = (w-1)*n;
for (i=weights->count; i > 0; i--)
{
/* Scale the next pixel in the column */
min = *contrib++;
len = *contrib++;
min = src_w-min*n;
for (j = 0; j < n; j++)
tmp[j] = 0;
while (len-- > 0)
{
for (j = 0; j < n; j++)
tmp[j] += src[src_w-min+j] * *contrib;
contrib++;
}
for (j = 0; j < n; j++)
{
val = (tmp[j]+(1<<7))>>8;
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
*dst++ = val;
}
/* And then duplicate it across the row */
for (j = w; j > 0; j--)
{
*dst = dst[-n];
dst++;
}
}
}
else
{
w = (w-1)*n;
for (i=weights->count; i > 0; i--)
{
/* Scale the next pixel in the column */
min = *contrib++;
len = *contrib++;
min *= n;
for (j = 0; j < n; j++)
tmp[j] = 0;
while (len-- > 0)
{
for (j = 0; j < n; j++)
tmp[j] += src[min++] * *contrib;
contrib++;
}
for (j = 0; j < n; j++)
{
val = (tmp[j]+(1<<7))>>8;
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
*dst++ = val;
}
/* And then duplicate it across the row */
for (j = w; j > 0; j--)
{
*dst = dst[-n];
dst++;
}
}
}
}
#endif /* SINGLE_PIXEL_SPECIALS */
 
fz_pixmap *
fz_scale_pixmap_gridfit(fz_pixmap *src, float x, float y, float w, float h, int gridfit)
{
if (gridfit) {
float n;
if (w > 0) {
/* Adjust the left hand edge, leftwards to a pixel boundary */
n = (float)(int)x; /* n is now on a pixel boundary */
if (n > x) /* Ensure it's the pixel boundary BELOW x */
n -= 1.0f;
w += x-n; /* width gets wider as x >= n */
x = n;
/* Adjust the right hand edge rightwards to a pixel boundary */
n = (float)(int)w; /* n is now the integer width <= w */
if (n != w) /* If w isn't an integer already, bump it */
w = 1.0f + n;/* up to the next integer. */
} else {
/* Adjust the right hand edge, rightwards to a pixel boundary */
n = (float)(int)x; /* n is now on a pixel boundary */
if (n > x) /* Ensure it's the pixel boundary <= x */
n -= 1.0f;
if (n != x) /* If x isn't on a pixel boundary already, */
n += 1.0f; /* make n be the pixel boundary above x. */
w -= n-x; /* Expand width (more negative!) as n >= x */
x = n;
/* Adjust the left hand edge leftwards to a pixel boundary */
n = (float)(int)w;
if (n != w)
w = n - 1.0f;
}
if (h > 0) {
/* Adjust the bottom edge, downwards to a pixel boundary */
n = (float)(int)y; /* n is now on a pixel boundary */
if (n > y) /* Ensure it's the pixel boundary BELOW y */
n -= 1.0f;
h += y-n; /* height gets larger as y >= n */
y = n;
/* Adjust the top edge upwards to a pixel boundary */
n = (float)(int)h; /* n is now the integer height <= h */
if (n != h) /* If h isn't an integer already, bump it */
h = 1.0f + n;/* up to the next integer. */
} else {
/* Adjust the top edge, upwards to a pixel boundary */
n = (float)(int)y; /* n is now on a pixel boundary */
if (n > y) /* Ensure it's the pixel boundary <= y */
n -= 1.0f;
if (n != y) /* If y isn't on a pixel boundary already, */
n += 1.0f; /* make n be the pixel boundary above y. */
h -= n-y; /* Expand height (more negative!) as n >= y */
y = n;
/* Adjust the bottom edge downwards to a pixel boundary */
n = (float)(int)h;
if (n != h)
h = n - 1.0f;
}
}
return fz_scale_pixmap(src, x, y, w, h);
}
 
fz_pixmap *
fz_scale_pixmap(fz_pixmap *src, float x, float y, float w, float h)
{
fz_scale_filter *filter = &fz_scale_filter_simple;
fz_weights *contrib_rows = NULL;
fz_weights *contrib_cols = NULL;
fz_pixmap *output = NULL;
int *temp = NULL;
int max_row, temp_span, temp_rows, row;
int dst_w_int, dst_h_int, dst_x_int, dst_y_int;
int flip_x, flip_y;
 
DBUG(("Scale: (%d,%d) to (%g,%g) at (%g,%g)\n",src->w,src->h,w,h,x,y));
 
/* Find the destination bbox, width/height, and sub pixel offset,
* allowing for whether we're flipping or not. */
/* Note that the x and y sub pixel offsets here are different.
* The (x,y) position given describes where the bottom left corner
* of the source image should be mapped to (i.e. where (0,h) in image
* space ends up, not the more logical and sane (0,0)). Also there
* are differences in the way we scale horizontally and vertically.
* When scaling rows horizontally, we always read forwards through
* the source, and store either forwards or in reverse as required.
* When scaling vertically, we always store out forwards, but may
* feed source rows in in a different order.
*
* Consider the image rectange 'r' to which the image is mapped,
* and the (possibly) larger rectangle 'R', given by expanding 'r' to
* complete pixels.
*
* x can either be r.xmin-R.xmin or R.xmax-r.xmax depending on whether
* the image is x flipped or not. Whatever happens 0 <= x < 1.
* y is always R.ymax - r.ymax.
*/
/* dst_x_int is calculated to be the left of the scaled image, and
* x (the sub_pixel_offset) is the distance in from either the left
* or right pixel expanded edge. */
flip_x = (w < 0);
if (flip_x)
{
float tmp;
w = -w;
dst_x_int = floor(x-w);
tmp = ceilf(x);
dst_w_int = (int)tmp;
x = tmp - x;
dst_w_int -= dst_x_int;
}
else
{
dst_x_int = floor(x);
x -= (float)dst_x_int;
dst_w_int = (int)ceilf(x + w);
}
flip_y = (h < 0);
/* dst_y_int is calculated to be the bottom of the scaled image, but
* y (the sub pixel offset) has to end up being the value at the top.
*/
if (flip_y)
{
h = -h;
dst_y_int = floor(y-h);
dst_h_int = (int)ceilf(y) - dst_y_int;
} else {
dst_y_int = floor(y);
y += h;
dst_h_int = (int)ceilf(y) - dst_y_int;
}
/* y is the top edge position in floats. We want it to be the
* distance down from the next pixel boundary. */
y = ceilf(y) - y;
 
DBUG(("Result image: (%d,%d) at (%d,%d) (subpix=%g,%g)\n", dst_w_int, dst_h_int, dst_x_int, dst_y_int, x, y));
 
/* Step 1: Calculate the weights for columns and rows */
#ifdef SINGLE_PIXEL_SPECIALS
if (src->w == 1)
{
contrib_cols = NULL;
}
else
#endif /* SINGLE_PIXEL_SPECIALS */
{
contrib_cols = make_weights(src->w, x, w, filter, 0, dst_w_int, src->n, flip_x);
if (contrib_cols == NULL)
goto cleanup;
}
#ifdef SINGLE_PIXEL_SPECIALS
if (src->h == 1)
{
contrib_rows = NULL;
}
else
#endif /* SINGLE_PIXEL_SPECIALS */
{
contrib_rows = make_weights(src->h, y, h, filter, 1, dst_h_int, src->n, flip_y);
if (contrib_rows == NULL)
goto cleanup;
}
 
assert(contrib_cols == NULL || contrib_cols->count == dst_w_int);
assert(contrib_rows == NULL || contrib_rows->count == dst_h_int);
output = fz_new_pixmap(src->colorspace, dst_w_int, dst_h_int);
output->x = dst_x_int;
output->y = dst_y_int;
 
/* Step 2: Apply the weights */
#ifdef SINGLE_PIXEL_SPECIALS
if (contrib_rows == NULL)
{
/* Only 1 source pixel high. */
if (contrib_cols == NULL)
{
/* Only 1 pixel in the entire image! */
duplicate_single_pixel(output->samples, src->samples, src->n, dst_w_int, dst_h_int);
}
else
{
/* Scale the row once, then copy it. */
scale_single_row(output->samples, src->samples, contrib_cols, src->w, dst_h_int);
}
}
else if (contrib_cols == NULL)
{
/* Only 1 source pixel wide. Scale the col and duplicate. */
scale_single_col(output->samples, src->samples, contrib_rows, src->h, src->n, dst_w_int, flip_y);
}
else
#endif /* SINGLE_PIXEL_SPECIALS */
{
void (*row_scale)(int *dst, unsigned char *src, fz_weights *weights);
 
temp_span = contrib_cols->count * src->n;
temp_rows = contrib_rows->max_len;
if (temp_span <= 0 || temp_rows > INT_MAX / temp_span)
goto cleanup;
temp = fz_calloc(temp_span*temp_rows, sizeof(int));
if (temp == NULL)
goto cleanup;
switch (src->n)
{
default:
row_scale = scale_row_to_temp;
break;
case 1: /* Image mask case */
row_scale = scale_row_to_temp1;
break;
case 2: /* Greyscale with alpha case */
row_scale = scale_row_to_temp2;
break;
case 4: /* RGBA */
row_scale = scale_row_to_temp4;
break;
}
max_row = 0;
for (row = 0; row < contrib_rows->count; row++)
{
/*
Which source rows do we need to have scaled into the
temporary buffer in order to be able to do the final
scale?
*/
int row_index = contrib_rows->index[row];
int row_min = contrib_rows->index[row_index++];
int row_len = contrib_rows->index[row_index++];
while (max_row < row_min+row_len)
{
/* Scale another row */
assert(max_row < src->h);
DBUG(("scaling row %d to temp\n", max_row));
(*row_scale)(&temp[temp_span*(max_row % temp_rows)], &src->samples[(flip_y ? (src->h-1-max_row): max_row)*src->w*src->n], contrib_cols);
max_row++;
}
 
DBUG(("scaling row %d from temp\n", row));
scale_row_from_temp(&output->samples[row*output->w*output->n], temp, contrib_rows, temp_span, row);
}
fz_free(temp);
}
 
cleanup:
fz_free(contrib_rows);
fz_free(contrib_cols);
return output;
}
/contrib/media/updf/draw/draw_unpack.c
0,0 → 1,235
#include "fitz.h"
 
/* Unpack image samples and optionally pad pixels with opaque alpha */
 
#define get1(buf,x) ((buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1 )
#define get2(buf,x) ((buf[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3 )
#define get4(buf,x) ((buf[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15 )
#define get8(buf,x) (buf[x])
#define get16(buf,x) (buf[x << 1])
 
static unsigned char get1_tab_1[256][8];
static unsigned char get1_tab_1p[256][16];
static unsigned char get1_tab_255[256][8];
static unsigned char get1_tab_255p[256][16];
 
static void
init_get1_tables(void)
{
static int once = 0;
unsigned char bits[1];
int i, k, x;
 
/* TODO: mutex lock here */
 
if (once)
return;
 
for (i = 0; i < 256; i++)
{
bits[0] = i;
for (k = 0; k < 8; k++)
{
x = get1(bits, k);
 
get1_tab_1[i][k] = x;
get1_tab_1p[i][k * 2] = x;
get1_tab_1p[i][k * 2 + 1] = 255;
 
get1_tab_255[i][k] = x * 255;
get1_tab_255p[i][k * 2] = x * 255;
get1_tab_255p[i][k * 2 + 1] = 255;
}
}
 
once = 1;
}
 
void
fz_unpack_tile(fz_pixmap *dst, unsigned char * restrict src, int n, int depth, int stride, int scale)
{
int pad, x, y, k;
int w = dst->w;
 
pad = 0;
if (dst->n > n)
pad = 255;
 
if (depth == 1)
init_get1_tables();
 
if (scale == 0)
{
switch (depth)
{
case 1: scale = 255; break;
case 2: scale = 85; break;
case 4: scale = 17; break;
}
}
 
for (y = 0; y < dst->h; y++)
{
unsigned char *sp = src + y * stride;
unsigned char *dp = dst->samples + y * (dst->w * dst->n);
 
/* Specialized loops */
 
if (n == 1 && depth == 1 && scale == 1 && !pad)
{
int w3 = w >> 3;
for (x = 0; x < w3; x++)
{
memcpy(dp, get1_tab_1[*sp++], 8);
dp += 8;
}
x = x << 3;
if (x < w)
memcpy(dp, get1_tab_1[*sp], w - x);
}
 
else if (n == 1 && depth == 1 && scale == 255 && !pad)
{
int w3 = w >> 3;
for (x = 0; x < w3; x++)
{
memcpy(dp, get1_tab_255[*sp++], 8);
dp += 8;
}
x = x << 3;
if (x < w)
memcpy(dp, get1_tab_255[*sp], w - x);
}
 
else if (n == 1 && depth == 1 && scale == 1 && pad)
{
int w3 = w >> 3;
for (x = 0; x < w3; x++)
{
memcpy(dp, get1_tab_1p[*sp++], 16);
dp += 16;
}
x = x << 3;
if (x < w)
memcpy(dp, get1_tab_1p[*sp], (w - x) << 1);
}
 
else if (n == 1 && depth == 1 && scale == 255 && pad)
{
int w3 = w >> 3;
for (x = 0; x < w3; x++)
{
memcpy(dp, get1_tab_255p[*sp++], 16);
dp += 16;
}
x = x << 3;
if (x < w)
memcpy(dp, get1_tab_255p[*sp], (w - x) << 1);
}
 
else if (depth == 8 && !pad)
{
int len = w * n;
while (len--)
*dp++ = *sp++;
}
 
else if (depth == 8 && pad)
{
for (x = 0; x < w; x++)
{
for (k = 0; k < n; k++)
*dp++ = *sp++;
*dp++ = 255;
}
}
 
else
{
int b = 0;
for (x = 0; x < w; x++)
{
for (k = 0; k < n; k++)
{
switch (depth)
{
case 1: *dp++ = get1(sp, b) * scale; break;
case 2: *dp++ = get2(sp, b) * scale; break;
case 4: *dp++ = get4(sp, b) * scale; break;
case 8: *dp++ = get8(sp, b); break;
case 16: *dp++ = get16(sp, b); break;
}
b++;
}
if (pad)
*dp++ = 255;
}
}
}
}
 
/* Apply decode array */
 
void
fz_decode_indexed_tile(fz_pixmap *pix, float *decode, int maxval)
{
int add[FZ_MAX_COLORS];
int mul[FZ_MAX_COLORS];
unsigned char *p = pix->samples;
int len = pix->w * pix->h;
int n = pix->n - 1;
int needed;
int k;
 
needed = 0;
for (k = 0; k < n; k++)
{
int min = decode[k * 2] * 256;
int max = decode[k * 2 + 1] * 256;
add[k] = min;
mul[k] = (max - min) / maxval;
needed |= min != 0 || max != maxval * 256;
}
 
if (!needed)
return;
 
while (len--)
{
for (k = 0; k < n; k++)
p[k] = (add[k] + (((p[k] << 8) * mul[k]) >> 8)) >> 8;
p += n + 1;
}
}
 
void
fz_decode_tile(fz_pixmap *pix, float *decode)
{
int add[FZ_MAX_COLORS];
int mul[FZ_MAX_COLORS];
unsigned char *p = pix->samples;
int len = pix->w * pix->h;
int n = MAX(1, pix->n - 1);
int needed;
int k;
 
needed = 0;
for (k = 0; k < n; k++)
{
int min = decode[k * 2] * 255;
int max = decode[k * 2 + 1] * 255;
add[k] = min;
mul[k] = max - min;
needed |= min != 0 || max != 255;
}
 
if (!needed)
return;
 
while (len--)
{
for (k = 0; k < n; k++)
p[k] = add[k] + fz_mul255(p[k], mul[k]);
p += pix->n;
}
}