diff options
Diffstat (limited to 'fitzdraw')
-rw-r--r-- | fitzdraw/Jamfile | 21 | ||||
-rw-r--r-- | fitzdraw/archppc.c | 67 | ||||
-rw-r--r-- | fitzdraw/archsparc.c | 23 | ||||
-rw-r--r-- | fitzdraw/archx86.c | 232 | ||||
-rw-r--r-- | fitzdraw/blendmodes.c | 213 | ||||
-rw-r--r-- | fitzdraw/glyphcache.c | 406 | ||||
-rw-r--r-- | fitzdraw/imagedraw.c | 242 | ||||
-rw-r--r-- | fitzdraw/imagescale.c | 358 | ||||
-rw-r--r-- | fitzdraw/imageunpack.c | 233 | ||||
-rw-r--r-- | fitzdraw/meshdraw.c | 408 | ||||
-rw-r--r-- | fitzdraw/pathfill.c | 134 | ||||
-rw-r--r-- | fitzdraw/pathscan.c | 531 | ||||
-rw-r--r-- | fitzdraw/pathstroke.c | 737 | ||||
-rw-r--r-- | fitzdraw/pixmap.c | 188 | ||||
-rw-r--r-- | fitzdraw/porterduff.c | 361 | ||||
-rw-r--r-- | fitzdraw/render.c | 969 |
16 files changed, 5123 insertions, 0 deletions
diff --git a/fitzdraw/Jamfile b/fitzdraw/Jamfile new file mode 100644 index 00000000..c76d9c6d --- /dev/null +++ b/fitzdraw/Jamfile @@ -0,0 +1,21 @@ +SubDir TOP fitzdraw ; + +Library libfitzdraw : + glyphcache.c + pixmap.c + porterduff.c + meshdraw.c + imagedraw.c + imageunpack.c + imagescale.c + pathscan.c + pathfill.c + pathstroke.c + render.c + blendmodes.c + ; + +if $(OSPLAT) = PPC { Library libfitzdraw : archppc.c ; } +if $(OSPLAT) = SPARC { Library libfitzdraw : archsparc.c ; } +if $(OSPLAT) = X86 { Library libfitzdraw : archx86.c ; } + diff --git a/fitzdraw/archppc.c b/fitzdraw/archppc.c new file mode 100644 index 00000000..30dbd780 --- /dev/null +++ b/fitzdraw/archppc.c @@ -0,0 +1,67 @@ +/* + * PowerPC specific render optims live here + */ + +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef unsigned char byte; + +#ifdef HAVE_ALTIVEC + +static void srow1ppc(byte *src, byte *dst, int w, int denom) +{ + int x, left; + int sum; + + left = 0; + sum = 0; + + for (x = 0; x < w; x++) + { + sum += *src++; + if (++left == denom) + { + left = 0; + *dst++ = sum / denom; + sum = 0; + } + } + + if (left) + *dst++ = sum / left; +} + +static void scol1ppc(byte *src, byte *dst, int w, int denom) +{ + int x, y; + unsigned char *s; + int sum; + + for (x = 0; x < w; x++) + { + s = src + x; + sum = 0; + for (y = 0; y < denom; y++) + sum += s[y * w]; + *dst++ = sum / denom; + } +} + +#endif /* HAVE_ALTIVEC */ + +#if defined (ARCH_PPC) +void +fz_accelerate(void) +{ +# ifdef HAVE_ALTIVEC + if (fz_cpuflags & HAVE_ALTIVEC) + { + fz_srow1 = srow1ppc; + fz_scol1 = scol1ppc; + } +# endif +} +#endif + diff --git a/fitzdraw/archsparc.c b/fitzdraw/archsparc.c new file mode 100644 index 00000000..b78e5a4d --- /dev/null +++ b/fitzdraw/archsparc.c @@ -0,0 +1,23 @@ +/* +SPARC specific render optims live here +*/ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +#ifdef HAVE_VIS + +#endif + +#if defined (ARCH_SPARC) +void +fz_accelerate(void) +{ +# ifdef HAVE_VIS + if (fz_cpuflags & HAVE_VIS) + { + } +# endif +} +#endif + diff --git a/fitzdraw/archx86.c b/fitzdraw/archx86.c new file mode 100644 index 00000000..261b8a16 --- /dev/null +++ b/fitzdraw/archx86.c @@ -0,0 +1,232 @@ +/* + * x86 specific render optims live here + */ + +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef unsigned char byte; + +/* always surround cpu specific code with HAVE_XXX */ +#ifdef HAVE_MMX + +/* -mmmx for gcc >= 3.4 enables the mmx intrinsic functions, icc and VC + shouldn't require anything */ +#include <mmintrin.h> + +static void duff_4i1o4mmx(byte *sp0, int sw, byte *mp0, int mw, byte *dp0, int dw, int w0, int h) +{ + /* + rendering all pages of + x11pdf ~/doc/OpenGL/Presentations/CEDEC2003_Venus_and_Vulcan.pdf + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 30.50 20.04 20.04 261 76.76 76.76 duff_4i1o4 + 21.67 22.02 10.95 221 49.55 49.55 duff_4i1o4mmx + */ + __m64 mzero = _mm_setzero_si64(); + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + + unsigned *s = (unsigned *)sp; + unsigned *d = (unsigned *)dp; + + int w = w0; + + /* TODO: unroll and process two pixels/iteration */ + while (w--) + { + int ts = *s++; + int ma = *mp++ + 1; + int sa = ((ts & 0xff) * ma) >> 8; + int ssa = 256 - sa; + + __m64 d0 = _mm_cvtsi32_si64(*d); + __m64 s0 = _mm_cvtsi32_si64(ts); + + /* 4 x 9 bit alpha value */ + __m64 mma = _mm_set1_pi16(ma); + __m64 mssa = _mm_set1_pi16(ssa); + + /* unpack 0000argb => a0r0g0b0 */ + __m64 d1 = _mm_unpacklo_pi8(d0, mzero); + __m64 s1 = _mm_unpacklo_pi8(s0, mzero); + + /* s1 * ma => a0r0g0b0 */ + __m64 msma = _mm_mullo_pi16(s1, mma); + /* d1 * mssa */ + __m64 mdssa = _mm_mullo_pi16(d1, mssa); + + __m64 res0 = _mm_add_pi16(msma, mdssa); + /* TODO: is it possible to get rid of the shift? */ + __m64 res1 = _mm_srli_pi16(res0, 8); + + /* pack */ + __m64 res2 = _mm_packs_pu16(res1, mzero); + + *d++ = _mm_cvtsi64_si32(res2); + } + + sp0 += sw; + mp0 += mw; + dp0 += dw; + } + + _mm_empty(); +} + +static inline unsigned +getargb(unsigned *s, int w, int h, int u, int v) +{ + if ((u < 0) | (u >= w) | (v < 0) | (v >= h)) return 0; + return s[w * v + u]; +} + +static void img_4o4mmx(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + /* since mmx does not have an unsigned multiply instruction we use + 17.15 fixed point */ + u0 >>= 1; v0 >>= 1; + fa >>= 1; fb >>= 1; + fc >>= 1; fd >>= 1; + + while (h--) + { + unsigned *s = (unsigned *)src; + unsigned *d = (unsigned *)dst0; + int u = u0; + int v = v0; + int w = w0; + + __m64 mzero = _mm_setzero_si64(); + __m64 m256 = _mm_set1_pi16(256); + __m64 malphamask = _mm_cvtsi32_si64(0xff); + + while (w--) + { + int iu = u >> 15; + int iv = v >> 15; + + int fu = u & 0x7fff; + int fv = v & 0x7fff; + + int atedge = + (iu < 0) | (iu >= (srcw - 1)) | + (iv < 0) | (iv >= (srch - 1)); + + __m64 ms0s1; + __m64 ms2s3; + + if (atedge) + { + unsigned s0, s1, s2, s3; + + /* edge cases use scalar loads */ + s0 = getargb(s, srcw, srch, iu + 0, iv + 0); + s1 = getargb(s, srcw, srch, iu + 1, iv + 0); + s2 = getargb(s, srcw, srch, iu + 0, iv + 1); + s3 = getargb(s, srcw, srch, iu + 1, iv + 1); + + /* move to mmx registers */ + ms0s1 = _mm_set_pi32(s1, s0); + ms2s3 = _mm_set_pi32(s3, s2); + } + else + { + __m64 *m0s = (__m64*)(s + srcw * (iv + 0) + iu); + __m64 *m2s = (__m64*)(s + srcw * (iv + 1) + iu); + + /* faster vector loads for interior */ + ms0s1 = *m0s; + ms2s3 = *m2s; + } + + /* unpack src into 4x16bit vectors */ + __m64 ms0 = _mm_unpacklo_pi8(ms0s1, mzero); + __m64 ms1 = _mm_unpackhi_pi8(ms0s1, mzero); + __m64 ms2 = _mm_unpacklo_pi8(ms2s3, mzero); + __m64 ms3 = _mm_unpackhi_pi8(ms2s3, mzero); + + /* lerp fu */ + + __m64 mfu = _mm_set1_pi16(fu); + + /* t2 = (s1 - s0) * fu + s0 */ + __m64 t0 = _mm_sub_pi16(ms1, ms0); + __m64 t1 = _mm_mulhi_pi16(t0, mfu); + t1 = _mm_adds_pi16(t1, t1); + __m64 t2 = _mm_add_pi16(t1, ms0); + + /* t3 = (s3 - s2) * fu + s2 */ + __m64 t3 = _mm_sub_pi16(ms3, ms2); + __m64 t4 = _mm_mulhi_pi16(t3, mfu); + t4 = _mm_adds_pi16(t4, t4); + __m64 t5 = _mm_add_pi16(t4, ms2); + + /* lerp fv */ + + __m64 mfv = _mm_set1_pi16(fv); + + /* t8 = (t5 - t2) * fv + t2 */ + __m64 t6 = _mm_sub_pi16(t5, t2); + __m64 t7 = _mm_mulhi_pi16(t6, mfv); + t7 = _mm_adds_pi16(t7, t7); + __m64 t8 = _mm_add_pi16(t7, t2); + + /* load and prepare dst */ + __m64 d0 = _mm_cvtsi32_si64(*d); + + __m64 d1 = _mm_unpacklo_pi8(d0, mzero); + + /* get src alpha */ + + /* splat alpha */ + __m64 a0001 = _mm_and_si64(malphamask, t8); + __m64 a0011 = _mm_unpacklo_pi16(a0001, a0001); + __m64 a1111 = _mm_unpacklo_pi16(a0011, a0011); + + /* 255+1 - sa */ + __m64 sna = _mm_sub_pi16(m256, a1111); + + /* blend src with dst */ + __m64 d2 = _mm_mullo_pi16(d1, sna); + __m64 d3 = _mm_srli_pi16(d2, 8); + __m64 d4 = _mm_add_pi16(t8, d3); + + /* pack and store new dst */ + __m64 d5 = _mm_packs_pu16(d4, mzero); + + *d++ = _mm_cvtsi64_si32(d5); + + u += fa; + v += fb; + } + + dst0 += dstw; + u0 += fc; + v0 += fd; + } + + _mm_empty(); +} + +#endif /* HAVE_MMX */ + +#if defined (ARCH_X86) || defined(ARCH_X86_64) +void +fz_accelerate(void) +{ +# ifdef HAVE_MMX + if (fz_cpuflags & HAVE_MMX) + { + fz_duff_4i1o4 = duff_4i1o4mmx; + fz_img_4o4 = img_4o4mmx; + } +# endif +} +#endif + diff --git a/fitzdraw/blendmodes.c b/fitzdraw/blendmodes.c new file mode 100644 index 00000000..f3ba8ac5 --- /dev/null +++ b/fitzdraw/blendmodes.c @@ -0,0 +1,213 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef unsigned char byte; + +/* +PDF 1.4 blend modes, except Normal and Multiply which are Over and In respectively. +Only the actual blend routines are here, not the node rendering logic which lives in render.c. +These are slow. +*/ + + +/* These functions apply to a single component, 0-255 range typically */ + +static int +fz_screen_byte(int bd, int s) +{ + return bd + s - fz_mul255(bd, s); +} + + +static int +fz_hardlight_byte(int bd, int s) +{ + int s2 = s << 1; + if (s <= 127) + return fz_mul255(bd, s2); + else + return fz_screen_byte(bd, s2); +} + + +static int +fz_overlay_byte(int bd, int s) +{ + return fz_hardlight_byte(s, bd); // note swapped order +} + + +static int +fz_darken_byte(int bd, int s) +{ + return MIN(bd, s); +} + + +static int +fz_lighten_byte(int bd, int s) +{ + return MAX(bd, s); +} + + +static int +fz_colordodge_byte(int bd, int s) +{ + if (s < 255) + return MIN(255, 255 * bd / (255 - s)); + else + return 255; +} + + +static int +fz_colorburn_byte(int bd, int s) +{ + if (s > 0) + return 255 - MIN(255, 255 * (255 - bd) / s); + else + return 0; +} + + +static int +fz_softlight_byte(int bd, int s) +{ + /* review this */ + if (s < 128) { + return bd - fz_mul255(fz_mul255((255 - (s<<1)), bd), 255 - bd); + } + else { + int dbd; + if (bd < 64) + dbd = fz_mul255(fz_mul255((bd << 4) - 12, bd) + 4, bd); + else + dbd = (int)sqrtf(255.0f * bd); + return bd + fz_mul255(((s<<1) - 255), (dbd - bd)); + } +} + + +static int +fz_difference_byte(int bd, int s) +{ + return ABS(bd - s); +} + + +static int +fz_exclusion_byte(int bd, int s) +{ + return bd + s - (fz_mul255(bd, s)<<1); +} + + +/* Non-separable blend modes */ + + +static int +lum(int r, int g, int b) +{ + /* 0.3, 0.59, 0.11 in 16.16 fixed point */ + return (19662 * r + 38666 * g + 7208 * b) >> 16; +} + + +static void +clipcolor(int r, int g, int b, int *dr, int *dg, int *db) +{ + int l = lum(r, g, b); + int n = MIN(MIN(r, g), b); + int x = MAX(MAX(r, g), b); + if (n < 0) { + *dr = l + 255 * (r - l) / (l - n); + *dg = l + 255 * (g - l) / (l - n); + *db = l + 255 * (b - l) / (l - n); + } + else { + *dr = l + 255 * (255 - l) / (x - l); + *dg = l + 255 * (255 - l) / (x - l); + *db = l + 255 * (255 - l) / (x - l); + } +} + + +static void +setlum(int r, int g, int b, int l, int *dr, int *dg, int *db) +{ + int d = 255 - lum(r, g, b); + clipcolor(r + d, g + d, b + d, dr, dg, db); +} + + +static int +sat(int r, int g, int b) +{ + return MAX(MAX(r, g), b) - MIN(MIN(r, g), b); +} + + +static void +setsat(int r, int g, int b, int s, int *dr, int *dg, int *db) +{ + int *m[3] = { &r, &g, &b }; /* min, med, max */ + int *t; +#define SWAP(a, b) (t = a, a = b, b = t) + if (*m[0] > *m[1]) + SWAP(m[0], m[1]); + if (*m[0] > *m[2]) + SWAP(m[0], m[2]); + if (*m[1] > *m[2]) + SWAP(m[1], m[2]); + + if (*m[2] > *m[0]) { + *m[1] = (*m[1] - *m[0]) * s / (*m[2] - *m[0]); + *m[2] = s; + } + else { + *m[1] = 0; + *m[2] = 0; + } + *dr = r; + *dg = g; + *db = b; +} + + +static void +fz_hue_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + int tr, tg, tb; + setsat(sr, sg, sb, sat(*bdr, *bdg, *bdb), &tr, &tg, &tb); + setlum(tr, tg, tb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); +} + + +static void +fz_saturation_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + int tr, tg, tb; + setsat(*bdr, *bdg, *bdb, sat(sr, sg, sb), &tr, &tg, &tb); + setlum(tr, tg, tb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); +} + + +static void +fz_color_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + setlum(sr, sg, sb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); +} + + +static void +fz_luminosity_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + setlum(*bdr, *bdg, *bdb, lum(sr, sg, sb), bdr, bdg, bdb); +} + + +//fz_separable_blend(, fz_blendkind mode) +//{ +//} diff --git a/fitzdraw/glyphcache.c b/fitzdraw/glyphcache.c new file mode 100644 index 00000000..fe03524e --- /dev/null +++ b/fitzdraw/glyphcache.c @@ -0,0 +1,406 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef struct fz_hash_s fz_hash; +typedef struct fz_key_s fz_key; +typedef struct fz_val_s fz_val; + +struct fz_glyphcache_s +{ + int slots; + int size; + fz_hash *hash; + fz_val *lru; + unsigned char *buffer; + int load; + int used; +}; + +struct fz_key_s +{ + void *fid; + int a, b; + int c, d; + unsigned short cid; + unsigned char e, f; +}; + +struct fz_hash_s +{ + fz_key key; + fz_val *val; +}; + +struct fz_val_s +{ + fz_hash *ent; + unsigned char *samples; + short w, h, x, y; + int uses; +}; + +static unsigned int hashkey(fz_key *key) +{ + unsigned char *s = (unsigned char*)key; + unsigned int hash = 0; + unsigned int i; + for (i = 0; i < sizeof(fz_key); i++) + { + hash += s[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +fz_error * +fz_newglyphcache(fz_glyphcache **arenap, int slots, int size) +{ + fz_glyphcache *arena; + + arena = *arenap = fz_malloc(sizeof(fz_glyphcache)); + if (!arena) + return fz_outofmem; + + arena->slots = slots; + arena->size = size; + + arena->hash = nil; + arena->lru = nil; + arena->buffer = nil; + + arena->hash = fz_malloc(sizeof(fz_hash) * slots); + if (!arena->hash) + goto cleanup; + + arena->lru = fz_malloc(sizeof(fz_val) * slots); + if (!arena->lru) + goto cleanup; + + arena->buffer = fz_malloc(size); + if (!arena->buffer) + goto cleanup; + + memset(arena->hash, 0, sizeof(fz_hash) * slots); + memset(arena->lru, 0, sizeof(fz_val) * slots); + memset(arena->buffer, 0, size); + arena->load = 0; + arena->used = 0; + + return fz_okay; + +cleanup: + fz_free(arena->hash); + fz_free(arena->lru); + fz_free(arena->buffer); + fz_free(arena); + return fz_outofmem; +} + +void +fz_dropglyphcache(fz_glyphcache *arena) +{ + fz_free(arena->hash); + fz_free(arena->lru); + fz_free(arena->buffer); + fz_free(arena); +} + +static int hokay = 0; +static int hcoll = 0; +static int hdist = 0; +static int coos = 0; +static int covf = 0; + +static int ghits = 0; +static int gmisses = 0; + +static fz_val * +hashfind(fz_glyphcache *arena, fz_key *key) +{ + fz_hash *tab = arena->hash; + int pos = hashkey(key) % arena->slots; + + while (1) + { + if (!tab[pos].val) + return nil; + + if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0) + return tab[pos].val; + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; + } +} + +static void +hashinsert(fz_glyphcache *arena, fz_key *key, fz_val *val) +{ + fz_hash *tab = arena->hash; + int pos = hashkey(key) % arena->slots; +int didcoll = 0; + + while (1) + { + if (!tab[pos].val) + { + tab[pos].key = *key; + tab[pos].val = val; + tab[pos].val->ent = &tab[pos]; +if (didcoll) hcoll ++; else hokay ++; hdist += didcoll; + return; + } + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; +didcoll ++; + } +} + +/* +static void +hashremove(fz_glyphcache *arena, fz_key *key) +{ + fz_hash *tab = arena->hash; + unsigned int pos = hashkey(key) % arena->slots; + unsigned int hole; + unsigned int look; + unsigned int code; + + while (1) + { + if (!tab[pos].val) + return; // boo hoo! tried to remove non-existant key + + if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0) + { + tab[pos].val = nil; + + hole = pos; + look = hole + 1; + if (look == arena->slots) + look = 0; + + while (tab[look].val) + { + code = (hashkey(&tab[look].key) % arena->slots); + if ((code <= hole && hole < look) || + (look < code && code <= hole) || + (hole < look && look < code)) + { + tab[hole] = tab[look]; + tab[hole].val->ent = &tab[hole]; + tab[look].val = nil; + hole = look; + } + + look = look + 1; + if (look == arena->slots) + look = 0; + } + + return; + } + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; + } +} +*/ + +void +fz_debugglyphcache(fz_glyphcache *arena) +{ + printf("cache load %d / %d (%d / %d bytes)\n", + arena->load, arena->slots, arena->used, arena->size); + printf("no-colliders: %d colliders: %d\n", hokay, hcoll); + printf("avg dist: %d / %d: %g\n", hdist, hcoll, (double)hdist / hcoll); + printf("out-of-space evicts: %d\n", coos); + printf("out-of-hash evicts: %d\n", covf); + printf("hits = %d misses = %d ratio = %g\n", ghits, gmisses, (float)ghits / (ghits + gmisses)); +/* + int i; + for (i = 0; i < arena->slots; i++) + { + if (!arena->hash[i].val) + printf("glyph % 4d: empty\n", i); + else { + fz_key *k = &arena->hash[i].key; + fz_val *b = arena->hash[i].val; + printf("glyph % 4d: %p %d [%g %g %g %g + %d %d] " + "-> [%dx%d %d,%d]\n", i, + k->fid, k->cid, + k->a / 65536.0, + k->b / 65536.0, + k->c / 65536.0, + k->d / 65536.0, + k->e, k->f, + b->w, b->h, b->x, b->y); + } + } + + for (i = 0; i < arena->load; i++) + printf("lru %04d: glyph %d (%d)\n", i, + arena->lru[i].ent - arena->hash, arena->lru[i].uses); +*/ +} + +static void +bubble(fz_glyphcache *arena, int i) +{ + fz_val tmp; + + if (i == 0 || arena->load < 2) + return; + + tmp = arena->lru[i - 1]; + arena->lru[i - 1] = arena->lru[i]; + arena->lru[i] = tmp; + + arena->lru[i - 1].ent->val = &arena->lru[i - 1]; + arena->lru[i].ent->val = &arena->lru[i]; +} + +/* +static void +evictlast(fz_glyphcache *arena) +{ + fz_val *lru = arena->lru; + unsigned char *s, *e; + int i, k; + fz_key key; + + if (arena->load == 0) + return; + + k = arena->load - 1; + s = lru[k].samples; + e = s + lru[k].w * lru[k].h; + + // pack buffer to fill hole + memmove(s, e, arena->buffer + arena->used - e); + memset(arena->buffer + arena->used - (e - s), 0, e - s); + arena->used -= e - s; + + // update lru pointers + for (i = 0; i < k; i++) // XXX this is DOG slow! XXX + if (lru[i].samples >= e) + lru[i].samples -= e - s; + + // remove hash entry + key = lru[k].ent->key; + hashremove(arena, &key); + + arena->load --; +} +*/ + +static void +evictall(fz_glyphcache *arena) +{ + memset(arena->hash, 0, sizeof(fz_hash) * arena->slots); + memset(arena->lru, 0, sizeof(fz_val) * arena->slots); + memset(arena->buffer, 0, arena->size); + arena->load = 0; + arena->used = 0; +} + +fz_error * +fz_renderglyph(fz_glyphcache *arena, fz_glyph *glyph, fz_font *font, int cid, fz_matrix ctm) +{ + fz_error *error; + fz_key key; + fz_val *val; + int size; + + key.fid = font; + key.cid = cid; + key.a = ctm.a * 65536; + key.b = ctm.b * 65536; + key.c = ctm.c * 65536; + key.d = ctm.d * 65536; + key.e = (ctm.e - fz_floor(ctm.e)) * 256; + key.f = (ctm.f - fz_floor(ctm.f)) * 256; + + val = hashfind(arena, &key); + if (val) + { + val->uses ++; + glyph->w = val->w; + glyph->h = val->h; + glyph->x = val->x; + glyph->y = val->y; + glyph->samples = val->samples; + + bubble(arena, val - arena->lru); + + ghits++; + + return fz_okay; + } + + gmisses++; + + ctm.e = fz_floor(ctm.e) + key.e / 256.0; + ctm.f = fz_floor(ctm.f) + key.f / 256.0; + + if (font->ftface) + { + error = fz_renderftglyph(glyph, font, cid, ctm); + if (error) + return error; + } + else if (font->t3procs) + { + error = fz_rendert3glyph(glyph, font, cid, ctm); + if (error) + return error; + } + else + { + return fz_throw("uninitialized font structure"); + } + + size = glyph->w * glyph->h; + + if (size > arena->size / 6) + return fz_okay; + + while (arena->load > arena->slots * 75 / 100) + { + covf ++; + evictall(arena); + } + + while (arena->used + size >= arena->size) + { + coos ++; + evictall(arena); + } + + val = &arena->lru[arena->load++]; + val->uses = 0; + val->w = glyph->w; + val->h = glyph->h; + val->x = glyph->x; + val->y = glyph->y; + val->samples = arena->buffer + arena->used; + + arena->used += size; + + memcpy(val->samples, glyph->samples, glyph->w * glyph->h); + glyph->samples = val->samples; + + hashinsert(arena, &key, val); + + return fz_okay; +} + diff --git a/fitzdraw/imagedraw.c b/fitzdraw/imagedraw.c new file mode 100644 index 00000000..1ad74bf7 --- /dev/null +++ b/fitzdraw/imagedraw.c @@ -0,0 +1,242 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef unsigned char byte; + +#define lerp(a,b,t) (a + (((b - a) * t) >> 16)) + +static inline byte getcomp(byte *s, int w, int h, int u, int v, int n, int k) +{ + if (u < 0 || u >= w) return 0; + if (v < 0 || v >= h) return 0; + return s[(w * v + u) * n + k]; +} + +static inline int samplecomp(byte *s, int w, int h, int u, int v, int n, int k) +{ + int ui = u >> 16; + int vi = v >> 16; + int ud = u & 0xFFFF; + int vd = v & 0xFFFF; + int a = getcomp(s, w, h, ui, vi, n, k); + int b = getcomp(s, w, h, ui+1, vi, n, k); + int c = getcomp(s, w, h, ui, vi+1, n, k); + int d = getcomp(s, w, h, ui+1, vi+1, n, k); + int ab = lerp(a, b, ud); + int cd = lerp(c, d, ud); + return lerp(ab, cd, vd); +} + +static inline byte getmask(byte *s, int w, int h, int u, int v) +{ + if (u < 0 || u >= w) return 0; + if (v < 0 || v >= h) return 0; + return s[w * v + u]; +} + +static inline int samplemask(byte *s, int w, int h, int u, int v) +{ + int ui = u >> 16; + int vi = v >> 16; + int ud = u & 0xFFFF; + int vd = v & 0xFFFF; + int a = getmask(s, w, h, ui, vi); + int b = getmask(s, w, h, ui+1, vi); + int c = getmask(s, w, h, ui, vi+1); + int d = getmask(s, w, h, ui+1, vi+1); + int ab = lerp(a, b, ud); + int cd = lerp(c, d, ud); + return lerp(ab, cd, vd); +} + +static inline void lerpargb(byte *dst, byte *a, byte *b, int t) +{ + dst[0] = lerp(a[0], b[0], t); + dst[1] = lerp(a[1], b[1], t); + dst[2] = lerp(a[2], b[2], t); + dst[3] = lerp(a[3], b[3], t); +} + +static inline byte *getargb(byte *s, int w, int h, int u, int v) +{ + static byte zero[4] = { 0, 0, 0, 0 }; + if (u < 0 || u >= w) return zero; + if (v < 0 || v >= h) return zero; + return s + ((w * v + u) << 2); +} + +static inline void sampleargb(byte *s, int w, int h, int u, int v, byte *abcd) +{ + byte ab[4]; + byte cd[4]; + int ui = u >> 16; + int vi = v >> 16; + int ud = u & 0xFFFF; + int vd = v & 0xFFFF; + byte *a = getargb(s, w, h, ui, vi); + byte *b = getargb(s, w, h, ui+1, vi); + byte *c = getargb(s, w, h, ui, vi+1); + byte *d = getargb(s, w, h, ui+1, vi+1); + lerpargb(ab, a, b, ud); + lerpargb(cd, c, d, ud); + lerpargb(abcd, ab, cd, vd); +} + +static void img_ncn(FZ_PSRC, int srcn, FZ_PDST, FZ_PCTM) +{ + int k; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + for (k = 0; k < srcn; k++) + { + dstp[k] = samplecomp(src, srcw, srch, u, v, srcn, k); + dstp += srcn; + u += fa; + v += fb; + } + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_1c1(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + dstp[0] = samplemask(src, srcw, srch, u, v); + dstp ++; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_4c4(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + sampleargb(src, srcw, srch, u, v, dstp); + dstp += 4; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_1o1(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + byte srca; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + srca = samplemask(src, srcw, srch, u, v); + dstp[0] = srca + fz_mul255(dstp[0], 255 - srca); + dstp ++; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_4o4(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + byte argb[4]; + byte ssa; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + sampleargb(src, srcw, srch, u, v, argb); + ssa = 255 - argb[0]; + dstp[0] = argb[0] + fz_mul255(dstp[0], ssa); + dstp[1] = argb[1] + fz_mul255(dstp[1], ssa); + dstp[2] = argb[2] + fz_mul255(dstp[2], ssa); + dstp[3] = argb[3] + fz_mul255(dstp[3], ssa); + dstp += 4; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_w4i1o4(byte *argb, FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + byte alpha = argb[0]; + byte r = argb[4]; + byte g = argb[5]; + byte b = argb[6]; + byte cov; + byte ca; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + cov = samplemask(src, srcw, srch, u, v); + ca = fz_mul255(cov, alpha); + dstp[0] = ca + fz_mul255(dstp[0], 255 - ca); + dstp[1] = fz_mul255((short)r - dstp[1], ca) + dstp[1]; + dstp[2] = fz_mul255((short)g - dstp[2], ca) + dstp[2]; + dstp[3] = fz_mul255((short)b - dstp[3], ca) + dstp[3]; + dstp += 4; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +void (*fz_img_ncn)(FZ_PSRC, int sn, FZ_PDST, FZ_PCTM) = img_ncn; +void (*fz_img_1c1)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_1c1; +void (*fz_img_4c4)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_4c4; +void (*fz_img_1o1)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_1o1; +void (*fz_img_4o4)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_4o4; +void (*fz_img_w4i1o4)(byte*,FZ_PSRC,FZ_PDST,FZ_PCTM) = img_w4i1o4; + diff --git a/fitzdraw/imagescale.c b/fitzdraw/imagescale.c new file mode 100644 index 00000000..f25d4d5a --- /dev/null +++ b/fitzdraw/imagescale.c @@ -0,0 +1,358 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef unsigned char byte; + +static inline void srown(byte * restrict src, byte * restrict dst, int w, int denom, int n) +{ + int invdenom = (1<<16) / denom; + int x, left, k; + unsigned sum[FZ_MAXCOLORS]; + + left = 0; + for (k = 0; k < n; k++) + sum[k] = 0; + + for (x = 0; x < w; x++) + { + for (k = 0; k < n; k++) + sum[k] += src[x * n + k]; + if (++left == denom) + { + left = 0; + for (k = 0; k < n; k++) + { + dst[k] = (sum[k] * invdenom + (1<<15)) >> 16; + sum[k] = 0; + } + dst += n; + } + } + + /* left overs */ + if (left) + for (k = 0; k < n; k++) + dst[k] = sum[k] / left; +} + +/* special-case common 1-5 components - the compiler optimizes this */ +static inline void srowc(byte * restrict src, byte * restrict dst, int w, int denom, int n) +{ + int invdenom = (1<<16) / denom; + int x, left; + unsigned sum1 = 0; + unsigned sum2 = 0; + unsigned sum3 = 0; + unsigned sum4 = 0; + unsigned sum5 = 0; + + assert(n <= 5); + + left = 0; + + for (x = 0; x < w; x++) + { + sum1 += src[x * n + 0]; + /* the compiler eliminates these if-tests */ + if (n >= 2) + sum2 += src[x * n + 1]; + if (n >= 3) + sum3 += src[x * n + 2]; + if (n >= 4) + sum4 += src[x * n + 3]; + if (n >= 5) + sum5 += src[x * n + 4]; + + if (++left == denom) + { + left = 0; + + dst[0] = (sum1 * invdenom + (1<<15)) >> 16; + sum1 = 0; + if (n >= 2) { + dst[1] = (sum2 * invdenom + (1<<15)) >> 16; + sum2 = 0; + } + if (n >= 3) { + dst[2] = (sum3 * invdenom + (1<<15)) >> 16; + sum3 = 0; + } + if (n >= 4) { + dst[3] = (sum4 * invdenom + (1<<15)) >> 16; + sum4 = 0; + } + if (n >= 5) { + dst[4] = (sum5 * invdenom + (1<<15)) >> 16; + sum5 = 0; + } + + + dst += n; + } + } + + /* left overs */ + if (left) { + dst[0] = sum1 / left; + if (n >=2) + dst[1] = sum2 / left; + if (n >=3) + dst[2] = sum3 / left; + if (n >=4) + dst[3] = sum4 / left; + if (n >=5) + dst[4] = sum5 / left; + } +} + +static void srow1(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 1); +} + +static void srow2(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 2); +} + +static void srow4(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 4); +} + +static void srow5(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 5); +} + +static inline void scoln(byte * restrict src, byte * restrict dst, int w, int denom, int n) +{ + int invdenom = (1<<16) / denom; + int x, y, k; + byte *s; + int sum[FZ_MAXCOLORS]; + + for (x = 0; x < w; x++) + { + s = src + (x * n); + for (k = 0; k < n; k++) + sum[k] = 0; + for (y = 0; y < denom; y++) + for (k = 0; k < n; k++) + sum[k] += s[y * w * n + k]; + for (k = 0; k < n; k++) + dst[k] = (sum[k] * invdenom + (1<<15)) >> 16; + dst += n; + } +} + +static void scol1(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 1); +} + +static void scol2(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 2); +} + +static void scol4(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 4); +} + +static void scol5(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 5); +} + +void (*fz_srown)(byte *src, byte *dst, int w, int denom, int n) = srown; +void (*fz_srow1)(byte *src, byte *dst, int w, int denom) = srow1; +void (*fz_srow2)(byte *src, byte *dst, int w, int denom) = srow2; +void (*fz_srow4)(byte *src, byte *dst, int w, int denom) = srow4; +void (*fz_srow5)(byte *src, byte *dst, int w, int denom) = srow5; + +void (*fz_scoln)(byte *src, byte *dst, int w, int denom, int n) = scoln; +void (*fz_scol1)(byte *src, byte *dst, int w, int denom) = scol1; +void (*fz_scol2)(byte *src, byte *dst, int w, int denom) = scol2; +void (*fz_scol4)(byte *src, byte *dst, int w, int denom) = scol4; +void (*fz_scol5)(byte *src, byte *dst, int w, int denom) = scol5; + +fz_error * +fz_newscaledpixmap(fz_pixmap **dstp, int w, int h, int n, int xdenom, int ydenom) +{ + int ow = (w + xdenom - 1) / xdenom; + int oh = (h + ydenom - 1) / ydenom; + return fz_newpixmap(dstp, 0, 0, ow, oh, n); +} + +/* TODO: refactor */ +fz_error * +fz_scalepixmaptile(fz_pixmap *dst, int xoffs, int yoffs, fz_pixmap *src, int xdenom, int ydenom) +{ + unsigned char *buf; + unsigned char *dstsamples; + int y, iy, oy; + int ow, oh, n; + int remaining; + + void (*srowx)(byte *src, byte *dst, int w, int denom) = nil; + void (*scolx)(byte *src, byte *dst, int w, int denom) = nil; + + ow = (src->w + xdenom - 1) / xdenom; + oh = (src->h + ydenom - 1) / ydenom; + xoffs /= xdenom; + yoffs /= ydenom; + n = src->n; + + assert(xoffs == 0); /* don't handle stride properly yet */ + assert(dst->n == n); + assert(dst->w >= xoffs + ow && dst->h >= yoffs + oh); + + buf = fz_malloc(ow * n * ydenom); + if (!buf) + return fz_outofmem; + + switch (n) + { + case 1: srowx = fz_srow1; scolx = fz_scol1; break; + case 2: srowx = fz_srow2; scolx = fz_scol2; break; + case 4: srowx = fz_srow4; scolx = fz_scol4; break; + case 5: srowx = fz_srow5; scolx = fz_scol5; break; + } + + dstsamples = dst->samples + (yoffs * dst->w + xoffs)*dst->n; + if (srowx && scolx) + { + for (y = 0, oy = 0; y < (src->h / ydenom) * ydenom; y += ydenom, oy++) + { + for (iy = 0; iy < ydenom; iy++) + srowx(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom); + scolx(buf, dstsamples + oy * dst->w * n, ow, ydenom); + } + + remaining = src->h - y; + if (remaining) + { + for (iy = 0; iy < remaining; iy++) + srowx(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom); + scolx(buf, dstsamples + oy * dst->w * n, ow, ydenom); + } + } + + else + { + for (y = 0, oy = 0; y < (src->h / ydenom) * ydenom; y += ydenom, oy++) + { + for (iy = 0; iy < ydenom; iy++) + fz_srown(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom, n); + fz_scoln(buf, dstsamples + oy * dst->w * n, ow, ydenom, n); + } + + remaining = src->h - y; + if (remaining) + { + for (iy = 0; iy < remaining; iy++) + fz_srown(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom, n); + fz_scoln(buf, dstsamples + oy * dst->w * n, ow, ydenom, n); + } + } + + fz_free(buf); + return fz_okay; +} + +fz_error * +fz_scalepixmap(fz_pixmap **dstp, fz_pixmap *src, int xdenom, int ydenom) +{ + fz_error *error; + fz_pixmap *dst; + unsigned char *buf; + int y, iy, oy; + int ow, oh, n; + int remaining; + + void (*srowx)(byte *src, byte *dst, int w, int denom) = nil; + void (*scolx)(byte *src, byte *dst, int w, int denom) = nil; + + ow = (src->w + xdenom - 1) / xdenom; + oh = (src->h + ydenom - 1) / ydenom; + n = src->n; + + buf = fz_malloc(ow * n * ydenom); + if (!buf) + return fz_outofmem; + + error = fz_newpixmap(&dst, 0, 0, ow, oh, src->n); + if (error) + { + fz_free(buf); + return error; + } + + switch (n) + { + case 1: srowx = fz_srow1; scolx = fz_scol1; break; + case 2: srowx = fz_srow2; scolx = fz_scol2; break; + case 4: srowx = fz_srow4; scolx = fz_scol4; break; + case 5: srowx = fz_srow5; scolx = fz_scol5; break; + } + + if (srowx && scolx) + { + for (y = 0, oy = 0; y < (src->h / ydenom) * ydenom; y += ydenom, oy++) + { + for (iy = 0; iy < ydenom; iy++) + srowx(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom); + scolx(buf, dst->samples + oy * dst->w * n, dst->w, ydenom); + } + + remaining = src->h - y; + if (remaining) + { + for (iy = 0; iy < remaining; iy++) + srowx(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom); + scolx(buf, dst->samples + oy * dst->w * n, dst->w, ydenom); + } + } + + else + { + for (y = 0, oy = 0; y < (src->h / ydenom) * ydenom; y += ydenom, oy++) + { + for (iy = 0; iy < ydenom; iy++) + fz_srown(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom, n); + fz_scoln(buf, dst->samples + oy * dst->w * n, dst->w, ydenom, n); + } + + remaining = src->h - y; + if (remaining) + { + for (iy = 0; iy < remaining; iy++) + fz_srown(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom, n); + fz_scoln(buf, dst->samples + oy * dst->w * n, dst->w, ydenom, n); + } + } + + fz_free(buf); + *dstp = dst; + return fz_okay; +} + diff --git a/fitzdraw/imageunpack.c b/fitzdraw/imageunpack.c new file mode 100644 index 00000000..a07d4f16 --- /dev/null +++ b/fitzdraw/imageunpack.c @@ -0,0 +1,233 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef unsigned char byte; + +/* + * Apply decode parameters + */ + +static void decodetile(fz_pixmap *pix, int skip, float *decode) +{ + int min[FZ_MAXCOLORS]; + int max[FZ_MAXCOLORS]; + int sub[FZ_MAXCOLORS]; + int needed = 0; + int n = pix->n; + byte *p = pix->samples; + int wh = pix->w * pix->h; + int i; + int justinvert = 1; + + min[0] = 0; + max[0] = 255; + sub[0] = 255; + + for (i = skip; i < n; i++) + { + min[i] = decode[(i - skip) * 2] * 255; + max[i] = decode[(i - skip) * 2 + 1] * 255; + sub[i] = max[i] - min[i]; + needed |= (min[i] != 0) | (max[i] != 255); + justinvert &= min[i] == 255 && max[i] == 0 && sub[i] == -255; + } + + if (!needed) + return; + + switch (n) { + case 1: + while (wh--) + { + p[0] = min[0] + fz_mul255(sub[0], p[0]); + p ++; + } + break; + case 2: + if (justinvert) { + unsigned *wp = (unsigned *)p; + + if ((((unsigned)wp) & 3) == 0) { + int hwh = wh / 2; + wh = wh - 2 * hwh; + while(hwh--) { + unsigned in = *wp; +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned out = in ^ 0xff00ff00; +#else + unsigned out = in ^ 0x00ff00ff; +#endif + *wp++ = out; + } + p = (byte *)wp; + } + if (wh--) { + p[0] = p[0]; + p[1] = 255 - p[1]; + p += 2; + } + } + else + while (wh--) + { + p[0] = min[0] + fz_mul255(sub[0], p[0]); + p[1] = min[1] + fz_mul255(sub[1], p[1]); + p += 2; + } + break; + default: + while (wh--) + { + for (i = 0; i < n; i++) + p[i] = min[i] + fz_mul255(sub[i], p[i]); + p += n; + } + } +} + +/* + * Unpack image samples and optionally pad pixels with opaque alpha + */ + +#define tbit(buf,x) ((buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1 ) * 255 +#define ttwo(buf,x) ((buf[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3 ) * 85 +#define tnib(buf,x) ((buf[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15 ) * 17 +#define toct(buf,x) (buf[x]) + +static byte t1pad0[256][8]; +static byte t1pad1[256][16]; + +static void init1() +{ + static int inited = 0; + byte bits[1]; + int i, k, x; + + if (inited) + return; + + for (i = 0; i < 256; i++) + { + bits[0] = i; + for (k = 0; k < 8; k++) + { + x = tbit(bits, k); + t1pad0[i][k] = x; + t1pad1[i][k * 2 + 0] = 255; + t1pad1[i][k * 2 + 1] = x; + } + } + + inited = 1; +} + +static void loadtile1(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) +{ + byte *sp; + byte *dp; + int x; + + init1(); + + if (pad == 0) + { + int w3 = w >> 3; + while (h--) + { + sp = src; + dp = dst; + for (x = 0; x < w3; x++) + { + memcpy(dp, t1pad0[*sp++], 8); + dp += 8; + } + x = x << 3; + if (x < w) + memcpy(dp, t1pad0[*sp], w - x); + src += sw; + dst += dw; + } + } + + else if (pad == 1) + { + int w3 = w >> 3; + while (h--) + { + sp = src; + dp = dst; + for (x = 0; x < w3; x++) + { + memcpy(dp, t1pad1[*sp++], 16); + dp += 16; + } + x = x << 3; + if (x < w) + memcpy(dp, t1pad1[*sp], (w - x) << 1); + src += sw; + dst += dw; + } + } + + else + { + while (h--) + { + dp = dst; + for (x = 0; x < w; x++) + { + if ((x % pad) == 0) + *dp++ = 255; + *dp++ = tbit(src, x); + } + src += sw; + dst += dw; + } + } +} + +#define TILE(getf) \ +{ \ + int x; \ + if (!pad) \ + while (h--) \ + { \ + for (x = 0; x < w; x++) \ + dst[x] = getf(src, x); \ + src += sw; \ + dst += dw; \ + } \ + else { \ + int tpad; \ + while (h--) \ + { \ + byte *dp = dst; \ + tpad = 0; \ + for (x = 0; x < w; x++) \ + { \ + if (!tpad--) { \ + tpad = pad-1; \ + *dp++ = 255; \ + } \ + *dp++ = getf(src, x); \ + } \ + src += sw; \ + dst += dw; \ + } \ + } \ +} + +static void loadtile2(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) + TILE(ttwo) +static void loadtile4(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) + TILE(tnib) +static void loadtile8(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) + TILE(toct) + +void (*fz_decodetile)(fz_pixmap *pix, int skip, float *decode) = decodetile; +void (*fz_loadtile1)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile1; +void (*fz_loadtile2)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile2; +void (*fz_loadtile4)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile4; +void (*fz_loadtile8)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile8; + diff --git a/fitzdraw/meshdraw.c b/fitzdraw/meshdraw.c new file mode 100644 index 00000000..abdcaaa1 --- /dev/null +++ b/fitzdraw/meshdraw.c @@ -0,0 +1,408 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +/* + * polygon clipping + */ + +enum { IN, OUT, ENTER, LEAVE }; +enum { MAXV = 3 + 4 }; +enum { MAXN = 2 + FZ_MAXCOLORS }; + +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 copyvert(float *dst, float *src, int n) +{ + while (n--) + *dst++ = *src++; +} + +static int clippoly(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++) + { + copyvert(cv1, src[v1], n); + copyvert(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: + copyvert(dst[cp++], cv2, n); + break; + case OUT: + break; + case LEAVE: + copyvert(dst[cp++], cv2, n); + break; + case ENTER: + copyvert(dst[cp++], cv1, n); + copyvert(dst[cp++], cv2, n); + break; + } + v1 = v2; + } + + return cp; +} + +/* + * gouraud shaded polygon scan conversion + */ + +static inline void +drawscan(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_MAXCOLORS]; + int dv[FZ_MAXCOLORS]; + 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--) + { + *p++ = 255; + for (k = 0; k < n; k++) + { + *p++ = v[k] >> 16; + v[k] += dv[k]; + } + } +} + +static inline int +findnext(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 inline void +loadedge(int gel[MAXV][MAXN], int s, int e, int *ael, int *del, int n) +{ + int swp, k, dy; + + if (gel[s][1] > gel[s][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 +stepedge(int *ael, int *del, int n) +{ + int k; + ael[0] += del[0]; + for (k = 2; k < n; k++) + ael[k] += del[k]; +} + +void +fz_drawtriangle(fz_pixmap *pix, float *av, float *bv, float *cv, int n) +{ + float poly[MAXV][MAXN]; + float temp[MAXV][MAXN]; + float cx0 = pix->x; + float cy0 = pix->y; + float cx1 = pix->x + pix->w; + float cy1 = pix->y + pix->h; + + 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; + + copyvert(poly[0], av, n); + copyvert(poly[1], bv, n); + copyvert(poly[2], cv, n); + + len = clippoly(poly, temp, 3, n, cx0, 0, 0); + len = clippoly(temp, poly, len, n, cx1, 0, 1); + len = clippoly(poly, temp, len, n, cy0, 1, 0); + len = clippoly(temp, poly, len, n, cy1, 1, 1); + + if (len < 3) + return; + + for (i = 0; i < len; i++) + { + gel[i][0] = fz_floor(poly[i][0] + 0.5) * 65536; /* trunc and fix */ + gel[i][1] = fz_floor(poly[i][1] + 0.5); /* 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 (findnext(gel, len, top, &s0, &e0, 1)) + return; + if (findnext(gel, len, top, &s1, &e1, -1)) + return; + + loadedge(gel, s0, e0, ael[0], del[0], n); + loadedge(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]) + drawscan(pix, y, x0, x1, ael[0]+2, ael[1]+2, n-2); + else + drawscan(pix, y, x1, x0, ael[1]+2, ael[0]+2, n-2); + + stepedge(ael[0], del[0], n); + stepedge(ael[1], del[1], n); + y ++; + + if (y >= gel[e0][1]) + { + if (findnext(gel, len, e0, &s0, &e0, 1)) + return; + loadedge(gel, s0, e0, ael[0], del[0], n); + } + + if (y >= gel[e1][1]) + { + if (findnext(gel, len, e1, &s1, &e1, -1)) + return; + loadedge(gel, s1, e1, ael[1], del[1], n); + } + } +} + +/* + * mesh drawing + */ + +fz_error * +fz_rendershade(fz_shade *shade, fz_matrix ctm, fz_colorspace *destcs, fz_pixmap *dest) +{ + unsigned char clut[256][3]; + unsigned char *s, *d; + fz_error *error; + fz_pixmap *temp; + float rgb[3]; + float tri[3][MAXN]; + fz_point p; + int i, j, k, n; + + assert(dest->n == 4); + + ctm = fz_concat(shade->matrix, ctm); + + if (shade->usefunction) + { + n = 3; + error = fz_newpixmap(&temp, dest->x, dest->y, dest->w, dest->h, 2); + if (error) + return error; + } + else if (shade->cs != destcs) + { + n = 2 + shade->cs->n; + error = fz_newpixmap(&temp, dest->x, dest->y, dest->w, dest->h, + shade->cs->n + 1); + if (error) + return error; + } + else + { + n = 2 + shade->cs->n; + temp = dest; + } + + fz_clearpixmap(temp); + + for (i = 0; i < shade->meshlen; i++) + { + for (k = 0; k < 3; k++) + { + p.x = shade->mesh[(i * 3 + k) * n + 0]; + p.y = shade->mesh[(i * 3 + k) * n + 1]; + p = fz_transformpoint(ctm, p); + if (isnan(p.y) || isnan(p.x)) // How is this happening? + goto baddata; + tri[k][0] = p.x; + tri[k][1] = p.y; + for (j = 2; j < n; j++) + tri[k][j] = shade->mesh[( i * 3 + k) * n + j] * 255; + } + fz_drawtriangle(temp, tri[0], tri[1], tri[2], n); + baddata: + ; + } + + if (shade->usefunction) + { + for (i = 0; i < 256; i++) + { + fz_convertcolor(shade->cs, shade->function[i], destcs, rgb); + clut[i][0] = rgb[0] * 255; + clut[i][1] = rgb[1] * 255; + clut[i][2] = rgb[2] * 255; + } + + n = temp->w * temp->h; + s = temp->samples; + d = dest->samples; + + while (n--) + { + d[0] = s[0]; + d[1] = fz_mul255(s[0], clut[s[1]][0]); + d[2] = fz_mul255(s[0], clut[s[1]][1]); + d[3] = fz_mul255(s[0], clut[s[1]][2]); + s += 2; + d += 4; + } + + fz_droppixmap(temp); + } + + else if (shade->cs != destcs) + { + fz_convertpixmap(shade->cs, temp, destcs, dest); + fz_droppixmap(temp); + } + + return fz_okay; +} + diff --git a/fitzdraw/pathfill.c b/fitzdraw/pathfill.c new file mode 100644 index 00000000..3040e6a3 --- /dev/null +++ b/fitzdraw/pathfill.c @@ -0,0 +1,134 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +static fz_error * +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; + return fz_insertgel(gel, tx0, ty0, tx1, ty1); +} + +static fz_error * +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) +{ + fz_error *error; + 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) + return line(gel, ctm, xa, ya, xd, yd); + + 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; + + error = bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + if (error) + return error; + return bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +fz_error * +fz_fillpath(fz_gel *gel, fz_pathnode *path, fz_matrix ctm, float flatness) +{ + fz_error *error; + 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->els[i++].k) + { + case FZ_MOVETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + cx = bx = x1; + cy = by = y1; + break; + + case FZ_LINETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + error = line(gel, &ctm, cx, cy, x1, y1); + if (error) + return error; + cx = x1; + cy = y1; + break; + + case FZ_CURVETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + x2 = path->els[i++].v; + y2 = path->els[i++].v; + x3 = path->els[i++].v; + y3 = path->els[i++].v; + error = bezier(gel, &ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3); + if (error) + return error; + cx = x3; + cy = y3; + break; + + case FZ_CLOSEPATH: + error = line(gel, &ctm, cx, cy, bx, by); + if (error) + return error; + cx = bx; + cy = by; + break; + } + } + + if (i && (cx != bx || cy != by)) + { + error = line(gel, &ctm, cx, cy, bx, by); + if (error) + return error; + } + + return fz_okay; +} + diff --git a/fitzdraw/pathscan.c b/fitzdraw/pathscan.c new file mode 100644 index 00000000..0ea46502 --- /dev/null +++ b/fitzdraw/pathscan.c @@ -0,0 +1,531 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +enum { HSCALE = 17, VSCALE = 15, SF = 1 }; + +/* + * 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) + */ + +fz_error * +fz_newgel(fz_gel **gelp) +{ + fz_gel *gel; + + gel = *gelp = fz_malloc(sizeof(fz_gel)); + if (!gel) + return fz_outofmem; + + gel->edges = nil; + + gel->cap = 512; + gel->len = 0; + gel->edges = fz_malloc(sizeof(fz_edge) * gel->cap); + if (!gel->edges) { + fz_free(gel); + return fz_outofmem; + } + + gel->clip.x0 = gel->clip.y0 = INT_MAX; + gel->clip.x1 = gel->clip.y1 = INT_MIN; + + gel->bbox.x0 = gel->bbox.y0 = INT_MAX; + gel->bbox.x1 = gel->bbox.y1 = INT_MIN; + + return fz_okay; +} + +void +fz_resetgel(fz_gel *gel, fz_irect clip) +{ + if (fz_isinfiniterect(clip)) + { + gel->clip.x0 = gel->clip.y0 = INT_MAX; + gel->clip.x1 = gel->clip.y1 = INT_MIN; + } + else { + gel->clip.x0 = clip.x0 * HSCALE; + gel->clip.x1 = clip.x1 * HSCALE; + gel->clip.y0 = clip.y0 * VSCALE; + gel->clip.y1 = clip.y1 * VSCALE; + } + + gel->bbox.x0 = gel->bbox.y0 = INT_MAX; + gel->bbox.x1 = gel->bbox.y1 = INT_MIN; + + gel->len = 0; +} + +void +fz_dropgel(fz_gel *gel) +{ + fz_free(gel->edges); + fz_free(gel); +} + +fz_irect +fz_boundgel(fz_gel *gel) +{ + fz_irect bbox; + bbox.x0 = fz_idiv(gel->bbox.x0, HSCALE); + bbox.y0 = fz_idiv(gel->bbox.y0, VSCALE); + bbox.x1 = fz_idiv(gel->bbox.x1, HSCALE) + 1; + bbox.y1 = fz_idiv(gel->bbox.y1, VSCALE) + 1; + return bbox; +} + +enum { INSIDE, OUTSIDE, LEAVE, ENTER }; + +#define cliplerpy(v,m,x0,y0,x1,y1,t) cliplerpx(v,m,y0,x0,y1,x1,t) + +static int +cliplerpx(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; + } +} + +fz_error * +fz_insertgel(fz_gel *gel, float fx0, float fy0, float fx1, float fy1) +{ + fz_edge *edge; + int dx, dy; + int winding; + int width; + int tmp; + int v; + int d; + + int x0 = fz_floor(fx0 * HSCALE); + int y0 = fz_floor(fy0 * VSCALE); + int x1 = fz_floor(fx1 * HSCALE); + int y1 = fz_floor(fy1 * VSCALE); + + d = cliplerpy(gel->clip.y0, 0, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return fz_okay; + if (d == LEAVE) { y1 = gel->clip.y0; x1 = v; } + if (d == ENTER) { y0 = gel->clip.y0; x0 = v; } + + d = cliplerpy(gel->clip.y1, 1, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return fz_okay; + if (d == LEAVE) { y1 = gel->clip.y1; x1 = v; } + if (d == ENTER) { y0 = gel->clip.y1; x0 = v; } + + if (y0 == y1) + return fz_okay; + + 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) { + int newcap = gel->cap + 512; + fz_edge *newedges = fz_realloc(gel->edges, sizeof(fz_edge) * newcap); + if (!newedges) + return fz_outofmem; + gel->cap = newcap; + gel->edges = newedges; + } + + edge = &gel->edges[gel->len++]; + + dy = y1 - y0; + dx = x1 - x0; + width = dx < 0 ? -dx : dx; + + edge->xdir = dx > 0 ? 1 : -1; + edge->ydir = winding; + edge->x = x0; + edge->y = y0; + edge->h = dy; + edge->adjdown = 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->adjup = width; + } + + /* x-major edge */ + else { + edge->xmove = (width / dy) * edge->xdir; + edge->adjup = width % dy; + } + + return fz_okay; +} + +void +fz_sortgel(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; + } +} + +/* + * Active Edge List -- keep track of active edges while sweeping + */ + +fz_error * +fz_newael(fz_ael **aelp) +{ + fz_ael *ael; + + ael = *aelp = fz_malloc(sizeof(fz_ael)); + if (!ael) + return fz_outofmem; + + ael->cap = 64; + ael->len = 0; + ael->edges = fz_malloc(sizeof(fz_edge*) * ael->cap); + if (!ael->edges) { + fz_free(ael); + return fz_outofmem; + } + + return fz_okay; +} + +void +fz_dropael(fz_ael *ael) +{ + fz_free(ael->edges); + fz_free(ael); +} + +static inline void +sortael(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 fz_error * +insertael(fz_ael *ael, fz_gel *gel, int y, int *e) +{ + /* insert edges that start here */ + while (*e < gel->len && gel->edges[*e].y == y) { + if (ael->len + 1 == ael->cap) { + int newcap = ael->cap + 64; + fz_edge **newedges = fz_realloc(ael->edges, sizeof(fz_edge*) * newcap); + if (!newedges) + return fz_outofmem; + ael->edges = newedges; + ael->cap = newcap; + } + ael->edges[ael->len++] = &gel->edges[(*e)++]; + } + + /* shell-sort the edges by increasing x */ + sortael(ael->edges, ael->len); + + return fz_okay; +} + +static void +advanceael(fz_ael *ael) +{ + fz_edge *edge; + int i = 0; + + while (i < ael->len) + { + edge = ael->edges[i]; + + edge->h --; + + /* terminator! */ + if (edge->h == 0) { + ael->edges[i] = ael->edges[--ael->len]; + } + + else { + edge->x += edge->xmove; + edge->e += edge->adjup; + if (edge->e > 0) { + edge->x += edge->xdir; + edge->e -= edge->adjdown; + } + i ++; + } + } +} + +/* + * Scan convert + */ + +static inline void +addspan(unsigned char *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 / HSCALE; + x0sub = x0 % HSCALE; + x1pix = x1 / HSCALE; + x1sub = x1 % HSCALE; + + if (x0pix == x1pix) + { + list[x0pix] += x1sub - x0sub; + list[x0pix+1] += x0sub - x1sub; + } + + else + { + list[x0pix] += HSCALE - x0sub; + list[x0pix+1] += x0sub; + list[x1pix] += x1sub - HSCALE; + list[x1pix+1] += -x1sub; + } +} + +static inline void +nonzerowinding(fz_ael *ael, unsigned char *list, int xofs) +{ + int winding = 0; + int x = 0; + int i; + for (i = 0; i < ael->len; i++) + { + if (!winding && (winding + ael->edges[i]->ydir)) + x = ael->edges[i]->x; + if (winding && !(winding + ael->edges[i]->ydir)) + addspan(list, x, ael->edges[i]->x, xofs); + winding += ael->edges[i]->ydir; + } +} + +static inline void +evenodd(fz_ael *ael, unsigned char *list, int xofs) +{ + int even = 0; + int x = 0; + int i; + for (i = 0; i < ael->len; i++) + { + if (!even) + x = ael->edges[i]->x; + else + addspan(list, x, ael->edges[i]->x, xofs); + even = !even; + } +} + +static inline void toalpha(unsigned char *list, int n) +{ + int d = 0; + while (n--) + { + d += *list; + *list++ = d; + } +} + +static inline void blit(fz_pixmap *pix, int x, int y, + unsigned char *list, int skipx, int len, + unsigned char *argb, int over) +{ + unsigned char *dst; + unsigned char cov; + + dst = pix->samples + ( (y - pix->y) * pix->w + (x - pix->x) ) * pix->n; + cov = 0; + + while (skipx--) + { + cov += *list; + *list = 0; + ++list; + } + + if (argb) + fz_path_w4i1o4(argb, list, cov, len, dst); + else if (over) + fz_path_1o1(list, cov, len, dst); + else + fz_path_1c1(list, cov, len, dst); +} + +fz_error * +fz_scanconvert(fz_gel *gel, fz_ael *ael, int eofill, fz_irect clip, + fz_pixmap *pix, unsigned char *argb, int over) +{ + fz_error *error; + unsigned char *deltas; + int y, e; + int yd, yc; + + int xmin = fz_idiv(gel->bbox.x0, HSCALE); + int xmax = fz_idiv(gel->bbox.x1, HSCALE) + 1; + + int xofs = xmin * HSCALE; + + int skipx = clip.x0 - xmin; + int clipn = clip.x1 - clip.x0; + + assert(clip.x0 >= xmin); + assert(clip.x1 <= xmax); + + if (gel->len == 0) + return fz_okay; + + deltas = fz_malloc(xmax - xmin + 1); + if (!deltas) + return fz_outofmem; + + memset(deltas, 0, xmax - xmin + 1); + + e = 0; + y = gel->edges[0].y; + yc = fz_idiv(y, VSCALE); + yd = yc; + + while (ael->len > 0 || e < gel->len) + { + yc = fz_idiv(y, VSCALE); + if (yc != yd) + { + if (yd >= clip.y0 && yd < clip.y1) + { + blit(pix, xmin + skipx, yd, deltas, skipx, clipn, argb, over); + } + } + yd = yc; + + error = insertael(ael, gel, y, &e); + if (error) { + fz_free(deltas); + return error; + } + + if (yd >= clip.y0 && yd < clip.y1) + { + if (eofill) + evenodd(ael, deltas, xofs); + else + nonzerowinding(ael, deltas, xofs); + } + + advanceael(ael); + + if (ael->len > 0) + y ++; + else if (e < gel->len) + y = gel->edges[e].y; + } + + if (yd >= clip.y0 && yd < clip.y1) + { + blit(pix, xmin + skipx, yd, deltas, skipx, clipn, argb, over); + } + + fz_free(deltas); + return fz_okay; +} + diff --git a/fitzdraw/pathstroke.c b/fitzdraw/pathstroke.c new file mode 100644 index 00000000..d68f38ed --- /dev/null +++ b/fitzdraw/pathstroke.c @@ -0,0 +1,737 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +enum { BUTT = 0, ROUND = 1, SQUARE = 2, MITER = 0, BEVEL = 2 }; + +struct sctx +{ + fz_gel *gel; + fz_matrix *ctm; + float flatness; + + int linecap; + int linejoin; + float linewidth; + float miterlimit; + fz_point beg[2]; + fz_point seg[2]; + int sn, bn; + int dot; + + fz_dash *dash; + int toggle; + int offset; + float phase; + fz_point cur; +}; + +static fz_error * +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; + return fz_insertgel(s->gel, tx0, ty0, tx1, ty1); +} + +static fz_error * +arc(struct sctx *s, + float xc, float yc, + float x0, float y0, + float x1, float y1) +{ + fz_error *error; + float th0, th1, r; + float theta; + float ox, oy, nx, ny; + int n, i; + + r = fabs(s->linewidth); + theta = 2 * M_SQRT2 * sqrt(s->flatness / r); + th0 = atan2(y0, x0); + th1 = atan2(y1, x1); + + if (r > 0) + { + if (th0 < th1) + th0 += M_PI * 2; + n = ceil((th0 - th1) / theta); + } + else + { + if (th1 < th0) + th1 += M_PI * 2; + n = ceil((th1 - th0) / theta); + } + + ox = x0; + oy = y0; + for (i = 1; i < n; i++) + { + theta = th0 + (th1 - th0) * i / n; + nx = cos(theta) * r; + ny = sin(theta) * r; + error = line(s, xc + ox, yc + oy, xc + nx, yc + ny); + if (error) return error; + ox = nx; + oy = ny; + } + + error = line(s, xc + ox, yc + oy, xc + x1, yc + y1); + if (error) return error; + + return fz_okay; +} + +static fz_error * +linestroke(struct sctx *s, fz_point a, fz_point b) +{ + fz_error *error; + + float dx = b.x - a.x; + float dy = b.y - a.y; + float scale = s->linewidth / sqrt(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + + error = line(s, a.x - dlx, a.y - dly, b.x - dlx, b.y - dly); + if (error) return error; + + error = line(s, b.x + dlx, b.y + dly, a.x + dlx, a.y + dly); + if (error) return error; + + return fz_okay; +} + +static fz_error * +linejoin(struct sctx *s, fz_point a, fz_point b, fz_point c) +{ + fz_error *error; + 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) + return fz_okay; + if (dx1 * dx1 + dy1 * dy1 < FLT_EPSILON) + return fz_okay; + + scale = linewidth / sqrt(dx0 * dx0 + dy0 * dy0); + dlx0 = dy0 * scale; + dly0 = -dx0 * scale; + + scale = linewidth / sqrt(dx1 * dx1 + dy1 * dy1); + dlx1 = dy1 * scale; + dly1 = -dx1 * scale; + + cross = dx1 * dy0 - dx0 * dy1; + + dmx = (dlx0 + dlx1) * 0.5; + dmy = (dly0 + dly1) * 0.5; + 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) + { + error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + if (error) return error; + error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + if (error) return error; + } + + if (linejoin == MITER) + { + scale = linewidth * linewidth / dmr2; + dmx *= scale; + dmy *= scale; + + if (cross < 0) + { + error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + if (error) return error; + error = line(s, b.x + dlx1, b.y + dly1, b.x + dmx, b.y + dmy); + if (error) return error; + error = line(s, b.x + dmx, b.y + dmy, b.x + dlx0, b.y + dly0); + if (error) return error; + } + else + { + error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + if (error) return error; + error = line(s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy); + if (error) return error; + error = line(s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1); + if (error) return error; + } + } + + if (linejoin == ROUND) + { + if (cross < 0) + { + error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + if (error) return error; + error = arc(s, b.x, b.y, dlx1, dly1, dlx0, dly0); + if (error) return error; + } + else + { + error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + if (error) return error; + error = arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1); + if (error) return error; + } + } + + return fz_okay; +} + +static fz_error * +linecap(struct sctx *s, fz_point a, fz_point b) +{ + fz_error *error; + float flatness = s->flatness; + float linewidth = s->linewidth; + int linecap = s->linecap; + + float dx = b.x - a.x; + float dy = b.y - a.y; + + float scale = linewidth / sqrt(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + + if (linecap == BUTT) + return line(s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly); + + if (linecap == ROUND) + { + int i; + int n = ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / linewidth))); + float ox = b.x - dlx; + float oy = b.y - dly; + for (i = 1; i < n; i++) + { + float theta = M_PI * i / n; + float cth = cos(theta); + float sth = sin(theta); + float nx = b.x - dlx * cth - dly * sth; + float ny = b.y - dly * cth + dlx * sth; + error = line(s, ox, oy, nx, ny); + if (error) return error; + ox = nx; + oy = ny; + } + error = line(s, ox, oy, b.x + dlx, b.y + dly); + if (error) return error; + } + + if (linecap == SQUARE) + { + error = line(s, b.x - dlx, b.y - dly, + b.x - dlx - dly, + b.y - dly + dlx); + if (error) return error; + error = line(s, b.x - dlx - dly, + b.y - dly + dlx, + b.x + dlx - dly, + b.y + dly + dlx); + if (error) return error; + error = line(s, b.x + dlx - dly, + b.y + dly + dlx, + b.x + dlx, b.y + dly); + if (error) return error; + } + + return fz_okay; +} + +static fz_error * +linedot(struct sctx *s, fz_point a) +{ + fz_error *error; + float flatness = s->flatness; + float linewidth = s->linewidth; + int n = ceil(M_PI / (M_SQRT2 * sqrt(flatness / linewidth))); + float ox = a.x - linewidth; + float oy = a.y; + int i; + for (i = 1; i < n; i++) + { + float theta = M_PI * 2 * i / n; + float cth = cos(theta); + float sth = sin(theta); + float nx = a.x - cth * linewidth; + float ny = a.y + sth * linewidth; + error = line(s, ox, oy, nx, ny); + if (error) return error; + ox = nx; + oy = ny; + } + error = line(s, ox, oy, a.x - linewidth, a.y); + if (error) return error; + return fz_okay; +} + +static fz_error * +strokeflush(struct sctx *s) +{ + fz_error *error; + + if (s->sn == 2) + { + error = linecap(s, s->beg[1], s->beg[0]); + if (error) return error; + error = linecap(s, s->seg[0], s->seg[1]); + if (error) return error; + } + else if (s->dot) + { + error = linedot(s, s->beg[0]); + if (error) return error; + } + + s->dot = 0; + + return fz_okay; +} + +static fz_error * +strokemoveto(struct sctx *s, fz_point cur) +{ + fz_error *error; + + error = strokeflush(s); + if (error) return error; + + s->seg[0] = cur; + s->beg[0] = cur; + s->sn = 1; + s->bn = 1; + + return fz_okay; +} + +static fz_error * +strokelineto(struct sctx *s, fz_point cur) +{ + fz_error *error; + + 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 < s->flatness * s->flatness * 0.25) + { + s->dot = 1; + return fz_okay; + } + + error = linestroke(s, s->seg[s->sn-1], cur); + if (error) return error; + + if (s->sn == 2) + { + error = linejoin(s, s->seg[0], s->seg[1], cur); + if (error) return error; + + 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; + + return fz_okay; +} + +static fz_error * +strokeclosepath(struct sctx *s) +{ + fz_error *error; + + if (s->sn == 2) + { + error = strokelineto(s, s->beg[0]); + if (error) return error; + + if (s->seg[1].x == s->beg[0].x && s->seg[1].y == s->beg[0].y) + error = linejoin(s, s->seg[0], s->beg[0], s->beg[1]); + else + error = linejoin(s, s->seg[1], s->beg[0], s->beg[1]); + if (error) return error; + } + + else if (s->dot) + { + error = linedot(s, s->beg[0]); + if (error) return error; + } + + s->bn = 0; + s->sn = 0; + s->dot = 0; + + return fz_okay; +} + +static fz_error * +strokebezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + fz_error *error; + 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) { + fz_point p; + p.x = xd; + p.y = yd; + return strokelineto(s, p); + } + + 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; + + error = strokebezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + if (error) + return error; + + return strokebezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +fz_error * +fz_strokepath(fz_gel *gel, fz_pathnode *path, fz_matrix ctm, float flatness, float linewidth) +{ + fz_error *error; + struct sctx s; + fz_point p0, p1, p2, p3; + int i; + + s.gel = gel; + s.ctm = &ctm; + s.flatness = flatness; + + s.linecap = path->linecap; + s.linejoin = path->linejoin; + s.linewidth = linewidth * 0.5; /* hairlines use a different value from the path value */ + s.miterlimit = path->miterlimit; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + i = 0; + + if (path->len > 0 && path->els[0].k != FZ_MOVETO) + return fz_throw("path must begin with moveto"); + + p0.x = p0.y = 0; /* FZ_MOVETO guarantees p0 to be set, silence compiler */ + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = strokemoveto(&s, p1); + if (error) + return error; + p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = strokelineto(&s, p1); + if (error) + return error; + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + p2.x = path->els[i++].v; + p2.y = path->els[i++].v; + p3.x = path->els[i++].v; + p3.y = path->els[i++].v; + error = strokebezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + if (error) + return error; + p0 = p3; + break; + + case FZ_CLOSEPATH: + error = strokeclosepath(&s); + if (error) + return error; + break; + } + } + + return strokeflush(&s); +} + +static fz_error * +dashmoveto(struct sctx *s, fz_point a) +{ + s->toggle = 1; + s->offset = 0; + s->phase = s->dash->phase; + + while (s->phase >= s->dash->array[s->offset]) + { + s->toggle = !s->toggle; + s->phase -= s->dash->array[s->offset]; + s->offset ++; + if (s->offset == s->dash->len) + s->offset = 0; + } + + s->cur = a; + + if (s->toggle) + return strokemoveto(s, a); + + return fz_okay; +} + +static fz_error * +dashlineto(struct sctx *s, fz_point b) +{ + fz_error *error; + 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 = sqrt(dx * dx + dy * dy); + used = 0; + + while (total - used > s->dash->array[s->offset] - s->phase) + { + used += s->dash->array[s->offset] - s->phase; + ratio = used / total; + m.x = a.x + ratio * dx; + m.y = a.y + ratio * dy; + + if (s->toggle) + error = strokelineto(s, m); + else + error = strokemoveto(s, m); + if (error) + return error; + + 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) + return strokelineto(s, b); + + return fz_okay; +} + +static fz_error * +dashbezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + fz_error *error; + 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) { + fz_point p; + p.x = xd; + p.y = yd; + return dashlineto(s, p); + } + + 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; + + error = dashbezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + if (error) return error; + return dashbezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +fz_error * +fz_dashpath(fz_gel *gel, fz_pathnode *path, fz_matrix ctm, float flatness, float linewidth) +{ + fz_error *error; + struct sctx s; + fz_point p0, p1, p2, p3, beg; + int i; + + s.gel = gel; + s.ctm = &ctm; + s.flatness = flatness; + + s.linecap = path->linecap; + s.linejoin = path->linejoin; + s.linewidth = linewidth * 0.5; + s.miterlimit = path->miterlimit; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + s.dash = path->dash; + s.toggle = 0; + s.offset = 0; + s.phase = 0; + + i = 0; + + if (path->len > 0 && path->els[0].k != FZ_MOVETO) + return fz_throw("path must begin with moveto"); + + p0.x = p0.y = 0; /* FZ_MOVETO guarantees p0 to be set, silence compiler */ + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = dashmoveto(&s, p1); + if (error) + return error; + beg = p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = dashlineto(&s, p1); + if (error) + return error; + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + p2.x = path->els[i++].v; + p2.y = path->els[i++].v; + p3.x = path->els[i++].v; + p3.y = path->els[i++].v; + error = dashbezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + if (error) + return error; + p0 = p3; + break; + + case FZ_CLOSEPATH: + error = dashlineto(&s, beg); + if (error) + return error; + break; + } + } + + return strokeflush(&s); +} + diff --git a/fitzdraw/pixmap.c b/fitzdraw/pixmap.c new file mode 100644 index 00000000..077009f8 --- /dev/null +++ b/fitzdraw/pixmap.c @@ -0,0 +1,188 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +fz_error * +fz_newpixmap(fz_pixmap **pixp, int x, int y, int w, int h, int n) +{ + fz_pixmap *pix; + + pix = *pixp = fz_malloc(sizeof(fz_pixmap)); + if (!pix) + return fz_outofmem; + + pix->x = x; + pix->y = y; + pix->w = w; + pix->h = h; + pix->n = n; + + pix->samples = fz_malloc(pix->w * pix->h * pix->n * sizeof(fz_sample)); + if (!pix->samples) { + fz_free(pix); + return fz_outofmem; + } + + return fz_okay; +} + +fz_error * +fz_newpixmapwithrect(fz_pixmap **pixp, fz_irect r, int n) +{ + return fz_newpixmap(pixp, + r.x0, r.y0, + r.x1 - r.x0, + r.y1 - r.y0, n); +} + +fz_error * +fz_newpixmapcopy(fz_pixmap **pixp, fz_pixmap *old) +{ + fz_error *error; + error = fz_newpixmap(pixp, old->x, old->y, old->w, old->h, old->n); + if (error) + return error; + memcpy((*pixp)->samples, old->samples, old->w * old->h * old->n); + return fz_okay; +} + +void +fz_droppixmap(fz_pixmap *pix) +{ + fz_free(pix->samples); + fz_free(pix); +} + +void +fz_clearpixmap(fz_pixmap *pix) +{ + memset(pix->samples, 0, pix->w * pix->h * pix->n * sizeof(fz_sample)); +} + +void +fz_gammapixmap(fz_pixmap *pix, float gamma) +{ + unsigned char table[256]; + int n = pix->w * pix->h * pix->n; + unsigned char *p = pix->samples; + int i; + for (i = 0; i < 256; i++) + table[i] = CLAMP(pow(i / 255.0, gamma) * 255.0, 0, 255); + while (n--) + { + *p = table[*p]; + p++; + } +} + +void +fz_debugpixmap(fz_pixmap *pix, char *prefix) +{ + static int counter = 0; + char colorname[40]; + char alphaname[40]; + FILE *color = NULL; + FILE *alpha = NULL; + int x, y; + + sprintf(alphaname, "%s-%04d-alpha.pgm", prefix, counter); + alpha = fopen(alphaname, "wb"); + if (!alpha) + goto cleanup; + + if (pix->n > 1) + { + if (pix->n > 2) + sprintf(colorname, "%s-%04d-color.ppm", prefix, counter); + else + sprintf(colorname, "%s-%04d-color.pgm", prefix, counter); + + color = fopen(colorname, "wb"); + if (!color) + goto cleanup; + } + + counter ++; + + if (pix->n == 5) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + fprintf(color, "P6\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int a = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + int cc = pix->samples[x * pix->n + y * pix->w * pix->n + 1]; + int mm = pix->samples[x * pix->n + y * pix->w * pix->n + 2]; + int yy = pix->samples[x * pix->n + y * pix->w * pix->n + 3]; + int kk = pix->samples[x * pix->n + y * pix->w * pix->n + 4]; + int r = 255 - MIN(cc + kk, 255); + int g = 255 - MIN(mm + kk, 255); + int b = 255 - MIN(yy + kk, 255); + fputc(a, alpha); + fputc(r, color); + fputc(g, color); + fputc(b, color); + } + } + } + + else if (pix->n == 4) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + fprintf(color, "P6\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int a = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + int r = pix->samples[x * pix->n + y * pix->w * pix->n + 1]; + int g = pix->samples[x * pix->n + y * pix->w * pix->n + 2]; + int b = pix->samples[x * pix->n + y * pix->w * pix->n + 3]; + fputc(a, alpha); + fputc(r, color); + fputc(g, color); + fputc(b, color); + } + } + } + + else if (pix->n == 2) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + fprintf(color, "P5\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int a = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + int g = pix->samples[x * pix->n + y * pix->w * pix->n + 1]; + fputc(a, alpha); + fputc(g, color); + } + } + } + + else if (pix->n == 1) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int g = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + fputc(g, alpha); + } + } + } + +cleanup: + if (alpha) fclose(alpha); + if (color) fclose(color); +} + diff --git a/fitzdraw/porterduff.c b/fitzdraw/porterduff.c new file mode 100644 index 00000000..8f2b9862 --- /dev/null +++ b/fitzdraw/porterduff.c @@ -0,0 +1,361 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +typedef unsigned char byte; + +/* + * Blend pixmap regions + */ + +/* dst = src over dst */ +static void +duff_non(byte * restrict sp0, int sw, int sn, byte * restrict dp0, int dw, int w0, int h) +{ + int k; + while (h--) + { + byte *sp = sp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte sa = sp[0]; + byte ssa = 255 - sa; + for (k = 0; k < sn; k++) + { + dp[k] = sp[k] + fz_mul255(dp[k], ssa); + } + sp += sn; + dp += sn; + } + sp0 += sw; + dp0 += dw; + } +} + +/* dst = src in msk */ +static void +duff_nimcn(byte * restrict sp0, int sw, int sn, byte * restrict mp0, int mw, int mn, byte * restrict dp0, int dw, int w0, int h) +{ + int k; + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + for (k = 0; k < sn; k++) + dp[k] = fz_mul255(sp[k], ma); + sp += sn; + mp += mn; + dp += sn; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +/* dst = src in msk over dst */ +static void +duff_nimon(byte * restrict sp0, int sw, int sn, byte * restrict mp0, int mw, int mn, byte * restrict dp0, int dw, int w0, int h) +{ + int k; + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + /* TODO: validate this */ + byte ma = mp[0]; + byte sa = fz_mul255(sp[0], ma); + byte ssa = 255 - sa; + for (k = 0; k < sn; k++) + { + dp[k] = fz_mul255(sp[k], ma) + fz_mul255(dp[k], ssa); + } + sp += sn; + mp += mn; + dp += sn; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_1o1(byte * restrict sp0, int sw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_non(sp0, sw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + dp[0] = sp[0] + fz_mul255(dp[0], 255 - sp[0]); + sp ++; + dp ++; + } + sp0 += sw; + dp0 += dw; + } +} + +static void duff_4o4(byte *sp0, int sw, byte *dp0, int dw, int w0, int h) +{ + /* duff_non(sp0, sw, 4, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ssa = 255 - sp[0]; + dp[0] = sp[0] + fz_mul255(dp[0], ssa); + dp[1] = sp[1] + fz_mul255(dp[1], ssa); + dp[2] = sp[2] + fz_mul255(dp[2], ssa); + dp[3] = sp[3] + fz_mul255(dp[3], ssa); + sp += 4; + dp += 4; + } + sp0 += sw; + dp0 += dw; + } +} + +static void duff_1i1c1(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimcn(sp0, sw, 1, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + dp[0] = fz_mul255(sp[0], mp[0]); + sp ++; + mp ++; + dp ++; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_4i1c4(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimcn(sp0, sw, 4, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + dp[0] = fz_mul255(sp[0], ma); + dp[1] = fz_mul255(sp[1], ma); + dp[2] = fz_mul255(sp[2], ma); + dp[3] = fz_mul255(sp[3], ma); + sp += 4; + mp += 1; + dp += 4; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_1i1o1(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimon(sp0, sw, 1, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + byte sa = fz_mul255(sp[0], ma); + byte ssa = 255 - sa; + dp[0] = fz_mul255(sp[0], ma) + fz_mul255(dp[0], ssa); + sp ++; + mp ++; + dp ++; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_4i1o4(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimon(sp0, sw, 4, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + byte sa = fz_mul255(sp[0], ma); + byte ssa = 255 - sa; + dp[0] = fz_mul255(sp[0], ma) + fz_mul255(dp[0], ssa); + dp[1] = fz_mul255(sp[1], ma) + fz_mul255(dp[1], ssa); + dp[2] = fz_mul255(sp[2], ma) + fz_mul255(dp[2], ssa); + dp[3] = fz_mul255(sp[3], ma) + fz_mul255(dp[3], ssa); + sp += 4; + mp += 1; + dp += 4; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +/* + * Path and text masks + */ + +static void path_1c1(byte * restrict src, byte cov, int len, byte * restrict dst) +{ + while (len--) + { + cov += *src; *src = 0; src++; + *dst++ = cov; + } +} + +static void path_1o1(byte * restrict src, byte cov, int len, byte * restrict dst) +{ + while (len--) + { + cov += *src; *src = 0; src++; + dst[0] = cov + fz_mul255(dst[0], 255 - cov); + dst++; + } +} + +// With 4 In 1 Over 4 +static void path_w4i1o4(byte * restrict argb, byte * restrict src, byte cov, int len, byte * restrict dst) +{ + byte alpha = argb[0]; + byte r = argb[4]; + byte g = argb[5]; + byte b = argb[6]; + while (len--) + { + byte ca; + cov += *src; *src = 0; src++; + ca = fz_mul255(cov, alpha); + dst[0] = ca + fz_mul255(dst[0], 255 - ca); + dst[1] = fz_mul255((short)r - dst[1], ca) + dst[1]; + dst[2] = fz_mul255((short)g - dst[2], ca) + dst[2]; + dst[3] = fz_mul255((short)b - dst[3], ca) + dst[3]; + dst += 4; + } +} + +static void text_1c1(byte * restrict src0, int srcw, byte * restrict dst0, int dstw, int w0, int h) +{ + while (h--) + { + byte * restrict src = src0; + byte * restrict dst = dst0; + int w = w0; + while (w--) + { + *dst++ = *src++; + } + src0 += srcw; + dst0 += dstw; + } +} + +static void text_1o1(byte * restrict src0, int srcw, byte * restrict dst0, int dstw, int w0, int h) +{ + while (h--) + { + byte *src = src0; + byte *dst = dst0; + int w = w0; + while (w--) + { + dst[0] = src[0] + fz_mul255(dst[0], 255 - src[0]); + src++; + dst++; + } + src0 += srcw; + dst0 += dstw; + } +} + +static void text_w4i1o4(byte * restrict argb, byte * restrict src0, int srcw, byte * restrict dst0, int dstw, int w0, int h) +{ + unsigned char alpha = argb[0]; + unsigned char r = argb[4]; + unsigned char g = argb[5]; + unsigned char b = argb[6]; + while (h--) + { + byte *src = src0; + byte *dst = dst0; + int w = w0; + while (w--) + { + byte ca = fz_mul255(src[0], alpha); + dst[0] = ca + fz_mul255(dst[0], 255 - ca); + dst[1] = fz_mul255((short)r - dst[1], ca) + dst[1]; + dst[2] = fz_mul255((short)g - dst[2], ca) + dst[2]; + dst[3] = fz_mul255((short)b - dst[3], ca) + dst[3]; + src ++; + dst += 4; + } + src0 += srcw; + dst0 += dstw; + } +} + +/* + * ... and the function pointers + */ + +void (*fz_duff_non)(byte*,int,int,byte*,int,int,int) = duff_non; +void (*fz_duff_nimcn)(byte*,int,int,byte*,int,int,byte*,int,int,int) = duff_nimcn; +void (*fz_duff_nimon)(byte*,int,int,byte*,int,int,byte*,int,int,int) = duff_nimon; +void (*fz_duff_1o1)(byte*,int,byte*,int,int,int) = duff_1o1; +void (*fz_duff_4o4)(byte*,int,byte*,int,int,int) = duff_4o4; +void (*fz_duff_1i1c1)(byte*,int,byte*,int,byte*,int,int,int) = duff_1i1c1; +void (*fz_duff_4i1c4)(byte*,int,byte*,int,byte*,int,int,int) = duff_4i1c4; +void (*fz_duff_1i1o1)(byte*,int,byte*,int,byte*,int,int,int) = duff_1i1o1; +void (*fz_duff_4i1o4)(byte*,int,byte*,int,byte*,int,int,int) = duff_4i1o4; + +void (*fz_path_1c1)(byte*,byte,int,byte*) = path_1c1; +void (*fz_path_1o1)(byte*,byte,int,byte*) = path_1o1; +void (*fz_path_w4i1o4)(byte*,byte*,byte,int,byte*) = path_w4i1o4; + +void (*fz_text_1c1)(byte*,int,byte*,int,int,int) = text_1c1; +void (*fz_text_1o1)(byte*,int,byte*,int,int,int) = text_1o1; +void (*fz_text_w4i1o4)(byte*,byte*,int,byte*,int,int,int) = text_w4i1o4; + diff --git a/fitzdraw/render.c b/fitzdraw/render.c new file mode 100644 index 00000000..90ead6a7 --- /dev/null +++ b/fitzdraw/render.c @@ -0,0 +1,969 @@ +#include "fitz-base.h" +#include "fitz-world.h" +#include "fitz-draw.h" + +#ifdef _MSC_VER +#define noDebug printf +#ifndef DEBUG +#define DEBUG +#endif +#else +#define noDEBUG(args...) printf(args) +#ifndef DEBUG +#define DEBUG(args...) +#endif +#endif +#define QUANT(x,a) (((int)((x) * (a))) / (a)) +#define HSUBPIX 5.0 +#define VSUBPIX 5.0 + +#define FNONE 0 +#define FOVER 1 +#define FRGB 4 + +static fz_error *rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm); + +fz_error * +fz_newrenderer(fz_renderer **gcp, fz_colorspace *pcm, int maskonly, int gcmem) +{ + fz_error *error; + fz_renderer *gc; + + gc = fz_malloc(sizeof(fz_renderer)); + if (!gc) + return fz_outofmem; + + gc->maskonly = maskonly; + gc->model = pcm; + gc->cache = nil; + gc->gel = nil; + gc->ael = nil; + + error = fz_newglyphcache(&gc->cache, gcmem / 24, gcmem); + if (error) + goto cleanup; + + error = fz_newgel(&gc->gel); + if (error) + goto cleanup; + + error = fz_newael(&gc->ael); + if (error) + goto cleanup; + + gc->dest = nil; + gc->over = nil; + gc->argb[0] = 255; + gc->argb[1] = 0; + gc->argb[2] = 0; + gc->argb[3] = 0; + gc->argb[4] = 0; + gc->argb[5] = 0; + gc->argb[6] = 0; + gc->flag = 0; + + *gcp = gc; + return fz_okay; + +cleanup: + if (gc->model) fz_dropcolorspace(gc->model); + if (gc->cache) fz_dropglyphcache(gc->cache); + if (gc->gel) fz_dropgel(gc->gel); + if (gc->ael) fz_dropael(gc->ael); + fz_free(gc); + return error; +} + +void +fz_droprenderer(fz_renderer *gc) +{ + if (gc->dest) fz_droppixmap(gc->dest); + if (gc->over) fz_droppixmap(gc->over); + + if (gc->model) fz_dropcolorspace(gc->model); + if (gc->cache) fz_dropglyphcache(gc->cache); + if (gc->gel) fz_dropgel(gc->gel); + if (gc->ael) fz_dropael(gc->ael); + fz_free(gc); +} + +/* + * Transform + */ + +static fz_error * +rendertransform(fz_renderer *gc, fz_transformnode *transform, fz_matrix ctm) +{ + fz_error *error; +DEBUG("transform [%g %g %g %g %g %g]\n", +transform->m.a, transform->m.b, +transform->m.c, transform->m.d, +transform->m.e, transform->m.f); +DEBUG("{\n"); + ctm = fz_concat(transform->m, ctm); + error = rendernode(gc, transform->super.first, ctm); +DEBUG("}\n"); + return error; +} + +/* + * Color + */ + +static fz_error * +rendersolid(fz_renderer *gc, fz_solidnode *solid, fz_matrix ctm) +{ + fz_error *error; + float rgb[3]; + unsigned char a, r, g, b; + unsigned char *p; + int n; + + if (gc->maskonly) + return fz_throw("assert: mask only renderer"); + if (gc->model->n != 3) + return fz_throw("assert: non-rgb renderer"); + + fz_convertcolor(solid->cs, solid->samples, gc->model, rgb); + gc->argb[0] = solid->a * 255; + gc->argb[1] = rgb[0] * solid->a * 255; + gc->argb[2] = rgb[1] * solid->a * 255; + gc->argb[3] = rgb[2] * solid->a * 255; + gc->argb[4] = rgb[0] * 255; + gc->argb[5] = rgb[1] * 255; + gc->argb[6] = rgb[2] * 255; + +DEBUG("solid %s [%d %d %d %d];\n", solid->cs->name, gc->argb[0], gc->argb[1], gc->argb[2], gc->argb[3]); + + if (gc->flag == FOVER) + { + p = gc->over->samples; + n = gc->over->w * gc->over->h; + } + else + { + error = fz_newpixmapwithrect(&gc->dest, gc->clip, 4); + if (error) + return error; + p = gc->dest->samples; + n = gc->dest->w * gc->dest->h; + } + + a = gc->argb[0]; + r = gc->argb[1]; + g = gc->argb[2]; + b = gc->argb[3]; + if (((unsigned)p & 3)) { + while (n--) + { + p[0] = a; + p[1] = r; + p[2] = g; + p[3] = b; + p += 4; + } + } + else + { + unsigned *pw = (unsigned *)p; +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned argb = a | (r << 8) | (g << 16) | (b << 24); +#else + unsigned argb = (a << 24) | (r << 16) | (g << 8) | b; +#endif + while (n--) + { + *pw++ = argb; + } + } + + return fz_okay; +} + +/* + * Path + */ + +static fz_error * +renderpath(fz_renderer *gc, fz_pathnode *path, fz_matrix ctm) +{ + fz_error *error; + float flatness; + fz_irect gbox; + fz_irect clip; + float expansion = fz_matrixexpansion(ctm); + + flatness = 0.3 / expansion; + if (flatness < 0.1) + flatness = 0.1; + + fz_resetgel(gc->gel, gc->clip); + + if (path->paint == FZ_STROKE) + { + float lw = path->linewidth; + /* Check for hairline */ + if (lw * expansion < 0.1) { + lw = 1.0f / expansion; + } + if (path->dash) + error = fz_dashpath(gc->gel, path, ctm, flatness, lw); + else + error = fz_strokepath(gc->gel, path, ctm, flatness, lw); + } + else + error = fz_fillpath(gc->gel, path, ctm, flatness); + if (error) + return error; + + fz_sortgel(gc->gel); + + gbox = fz_boundgel(gc->gel); + clip = fz_intersectirects(gc->clip, gbox); + + if (fz_isemptyrect(clip)) + return fz_okay; + +DEBUG("path %s;\n", path->paint == FZ_STROKE ? "stroke" : "fill"); + + if (gc->flag & FRGB) + { +DEBUG(" path rgb %d %d %d %d, %d %d %d\n", gc->argb[0], gc->argb[1], gc->argb[2], gc->argb[3], gc->argb[4], gc->argb[5], gc->argb[6]); + return fz_scanconvert(gc->gel, gc->ael, path->paint == FZ_EOFILL, + clip, gc->over, gc->argb, 1); + } + else if (gc->flag & FOVER) + { + return fz_scanconvert(gc->gel, gc->ael, path->paint == FZ_EOFILL, + clip, gc->over, nil, 1); + } + else + { + error = fz_newpixmapwithrect(&gc->dest, clip, 1); + if (error) + return error; + fz_clearpixmap(gc->dest); + return fz_scanconvert(gc->gel, gc->ael, path->paint == FZ_EOFILL, + clip, gc->dest, nil, 0); + } +} + +/* + * Text + */ + +static void drawglyph(fz_renderer *gc, fz_pixmap *dst, fz_glyph *src, int xorig, int yorig) +{ + unsigned char *dp, *sp; + int w, h; + + int dx0 = dst->x; + int dy0 = dst->y; + int dx1 = dst->x + dst->w; + int dy1 = dst->y + dst->h; + + int x0 = xorig + src->x; + int y0 = yorig + src->y; + int x1 = x0 + src->w; + int y1 = y0 + src->h; + + int sx0 = 0; + int sy0 = 0; + int sx1 = src->w; + int sy1 = src->h; + + if (x1 <= dx0 || x0 >= dx1) return; + if (y1 <= dy0 || y0 >= dy1) return; + if (x0 < dx0) { sx0 += dx0 - x0; x0 = dx0; } + if (y0 < dy0) { sy0 += dy0 - y0; y0 = dy0; } + if (x1 > dx1) { sx1 += dx1 - x1; x1 = dx1; } + if (y1 > dy1) { sy1 += dy1 - y1; y1 = dy1; } + + sp = src->samples + (sy0 * src->w + sx0); + dp = dst->samples + ((y0 - dst->y) * dst->w + (x0 - dst->x)) * dst->n; + + w = sx1 - sx0; + h = sy1 - sy0; + + switch (gc->flag) + { + case FNONE: + assert(dst->n == 1); + fz_text_1o1(sp, src->w, dp, dst->w, w, h); + break; + + case FOVER: + assert(dst->n == 1); + fz_text_1o1(sp, src->w, dp, dst->w, w, h); + break; + + case FOVER | FRGB: + assert(dst->n == 4); + fz_text_w4i1o4(gc->argb, sp, src->w, dp, dst->w * 4, w, h); + break; + + default: + assert(!"impossible flag in text span function"); + } +} + +static fz_error * +rendertext(fz_renderer *gc, fz_textnode *text, fz_matrix ctm) +{ + fz_error *error; + fz_irect tbox; + fz_irect clip; + fz_matrix tm, trm; + fz_glyph glyph; + int i, x, y, gid; + + tbox = fz_roundrect(fz_boundnode((fz_node*)text, ctm)); + clip = fz_intersectirects(gc->clip, tbox); + +DEBUG("text %s n=%d [%g %g %g %g];\n", +text->font->name, text->len, +text->trm.a, text->trm.b, text->trm.c, text->trm.d); + + if (fz_isemptyrect(clip)) + return fz_okay; + + if (!(gc->flag & FOVER)) + { + error = fz_newpixmapwithrect(&gc->dest, clip, 1); + if (error) + return error; + fz_clearpixmap(gc->dest); + } + + tm = text->trm; + + for (i = 0; i < text->len; i++) + { + gid = text->els[i].gid; + tm.e = text->els[i].x; + tm.f = text->els[i].y; + trm = fz_concat(tm, ctm); + x = fz_floor(trm.e); + y = fz_floor(trm.f); + trm.e = QUANT(trm.e - fz_floor(trm.e), HSUBPIX); + trm.f = QUANT(trm.f - fz_floor(trm.f), VSUBPIX); + + error = fz_renderglyph(gc->cache, &glyph, text->font, gid, trm); + if (error) + return error; + + if (!(gc->flag & FOVER)) + drawglyph(gc, gc->dest, &glyph, x, y); + else + drawglyph(gc, gc->over, &glyph, x, y); + } + + return fz_okay; +} + +/* + * Image + */ + +static inline void +calcimagescale(fz_matrix ctm, int w, int h, int *odx, int *ody) +{ + float sx, sy; + int dx, dy; + + sx = sqrt(ctm.a * ctm.a + ctm.b * ctm.b); + dx = 1; + while (((w+dx-1)/dx)/sx > 2.0 && (w+dx-1)/dx > 1) + dx++; + + sy = sqrt(ctm.c * ctm.c + ctm.d * ctm.d); + dy = 1; + while (((h+dy-1)/dy)/sy > 2.0 && (h+dy-1)/dy > 1) + dy++; + + *odx = dx; + *ody = dy; +} + +static fz_error * +renderimage(fz_renderer *gc, fz_imagenode *node, fz_matrix ctm) +{ + fz_error *error; + fz_image *image = node->image; + fz_irect bbox; + fz_irect clip; + int dx, dy; + fz_pixmap *tile; + fz_pixmap *temp; + fz_matrix imgmat; + fz_matrix invmat; + int fa, fb, fc, fd; + int u0, v0; + int x0, y0; + int w, h; + int tileheight; + +DEBUG("image %dx%d %d+%d %s\n{\n", image->w, image->h, image->n, image->a, image->cs?image->cs->name:"(nil)"); + + bbox = fz_roundrect(fz_boundnode((fz_node*)node, ctm)); + clip = fz_intersectirects(gc->clip, bbox); + + if (fz_isemptyrect(clip)) + return fz_okay; + if (image->w == 0 || image->h == 0) + return fz_okay; + + calcimagescale(ctm, image->w, image->h, &dx, &dy); + + /* try to fit tile into a typical L2 cachce */ + tileheight = 512 * 1024 / (image->w * (image->n + image->a)); + /* tileheight must be an even multiple of dy, except for last band */ + tileheight = (tileheight + dy - 1) / dy * dy; + + if ((dx != 1 || dy != 1) && image->h > tileheight) { + int y = 0; + + DEBUG(" load image tile size = %dx%d\n", image->w, tileheight); + error = fz_newpixmap(&tile, 0, 0, image->w, + tileheight, image->n + 1); + if (error) + return error; + + error = fz_newscaledpixmap(&temp, image->w, image->h, image->n + 1, dx, dy); + if (error) + goto cleanup; + + do { + if (y + tileheight > image->h) + tileheight = image->h - y; + tile->y = y; + tile->h = tileheight; + DEBUG(" tile xywh=%d %d %d %d sxsy=1/%d 1/%d\n", + 0, y, image->w, tileheight, dx, dy); + error = image->loadtile(image, tile); + if (error) + goto cleanup1; + + error = fz_scalepixmaptile(temp, 0, y, tile, dx, dy); + if (error) + goto cleanup1; + + y += tileheight; + } while (y < image->h); + + fz_droppixmap(tile); + tile = temp; + } + else { + + +DEBUG(" load image\n"); + error = fz_newpixmap(&tile, 0, 0, image->w, image->h, image->n + 1); + if (error) + return error; + + error = image->loadtile(image, tile); + if (error) + goto cleanup; + + if (dx != 1 || dy != 1) + { +DEBUG(" scale image 1/%d 1/%d\n", dx, dy); + error = fz_scalepixmap(&temp, tile, dx, dy); + if (error) + goto cleanup; + fz_droppixmap(tile); + tile = temp; + } + } + + if (image->cs && image->cs != gc->model) + { +DEBUG(" convert from %s to %s\n", image->cs->name, gc->model->name); + error = fz_newpixmap(&temp, tile->x, tile->y, tile->w, tile->h, gc->model->n + 1); + if (error) + goto cleanup; + fz_convertpixmap(image->cs, tile, gc->model, temp); + fz_droppixmap(tile); + tile = temp; + } + + imgmat.a = 1.0 / tile->w; + imgmat.b = 0.0; + imgmat.c = 0.0; + imgmat.d = -1.0 / tile->h; + imgmat.e = 0.0; + imgmat.f = 1.0; + invmat = fz_invertmatrix(fz_concat(imgmat, ctm)); + + w = clip.x1 - clip.x0; + h = clip.y1 - clip.y0; + x0 = clip.x0; + y0 = clip.y0; + u0 = (invmat.a * (x0+0.5) + invmat.c * (y0+0.5) + invmat.e) * 65536; + v0 = (invmat.b * (x0+0.5) + invmat.d * (y0+0.5) + invmat.f) * 65536; + fa = invmat.a * 65536; + fb = invmat.b * 65536; + fc = invmat.c * 65536; + fd = invmat.d * 65536; + +#define PSRC tile->samples, tile->w, tile->h +#define PDST(p) p->samples + ((y0-p->y) * p->w + (x0-p->x)) * p->n, p->w * p->n +#define PCTM u0, v0, fa, fb, fc, fd, w, h + + switch (gc->flag) + { + case FNONE: + { +DEBUG(" fnone %d x %d\n", w, h); + if (image->cs) + error = fz_newpixmapwithrect(&gc->dest, clip, gc->model->n + 1); + else + error = fz_newpixmapwithrect(&gc->dest, clip, 1); + if (error) + goto cleanup; + + if (image->cs) + fz_img_4c4(PSRC, PDST(gc->dest), PCTM); + else + fz_img_1c1(PSRC, PDST(gc->dest), PCTM); + } + break; + + case FOVER: + { +DEBUG(" fover %d x %d\n", w, h); + if (image->cs) + fz_img_4o4(PSRC, PDST(gc->over), PCTM); + else + fz_img_1o1(PSRC, PDST(gc->over), PCTM); + } + break; + + case FOVER | FRGB: +DEBUG(" fover+rgb %d x %d\n", w, h); + fz_img_w4i1o4(gc->argb, PSRC, PDST(gc->over), PCTM); + break; + + default: + assert(!"impossible flag in image span function"); + } + +DEBUG("}\n"); + + fz_droppixmap(tile); + return fz_okay; + +cleanup1: + fz_droppixmap(temp); +cleanup: + fz_droppixmap(tile); + return error; +} + +/* + * Shade + */ + +static fz_error * +rendershade(fz_renderer *gc, fz_shadenode *node, fz_matrix ctm) +{ + fz_error *error; + fz_irect bbox; + + assert(!gc->maskonly); + + DEBUG("shade;\n"); + + bbox = fz_roundrect(fz_boundnode((fz_node*)node, ctm)); + bbox = fz_intersectirects(gc->clip, bbox); + + error = fz_newpixmapwithrect(&gc->dest, bbox, gc->model->n + 1); + if (error) + return error; + + return fz_rendershade(node->shade, ctm, gc->model, gc->dest); +} + +/* + * Over, Mask and Blend + */ + +static void +blendover(fz_renderer *gc, fz_pixmap *src, fz_pixmap *dst) +{ + unsigned char *sp, *dp; + fz_irect sr, dr; + int x, y, w, h; + + sr.x0 = src->x; + sr.y0 = src->y; + sr.x1 = src->x + src->w; + sr.y1 = src->y + src->h; + + dr.x0 = dst->x; + dr.y0 = dst->y; + dr.x1 = dst->x + dst->w; + dr.y1 = dst->y + dst->h; + + dr = fz_intersectirects(sr, dr); + x = dr.x0; + y = dr.y0; + w = dr.x1 - dr.x0; + h = dr.y1 - dr.y0; + + 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; + + if (src->n == 1 && dst->n == 1) + fz_duff_1o1(sp, src->w, dp, dst->w, w, h); + else if (src->n == 4 && dst->n == 4) + fz_duff_4o4(sp, src->w * 4, dp, dst->w * 4, w, h); + else if (src->n == dst->n) + fz_duff_non(sp, src->w * src->n, src->n, dp, dst->w * dst->n, w, h); + else + assert(!"blendover src and dst mismatch"); +} + +static void +blendmask(fz_renderer *gc, fz_pixmap *src, fz_pixmap *msk, fz_pixmap *dst, int over) +{ + unsigned char *sp, *dp, *mp; + fz_irect sr, dr, mr; + int x, y, w, h; + + sr.x0 = src->x; + sr.y0 = src->y; + sr.x1 = src->x + src->w; + sr.y1 = src->y + src->h; + + dr.x0 = dst->x; + dr.y0 = dst->y; + dr.x1 = dst->x + dst->w; + dr.y1 = dst->y + dst->h; + + mr.x0 = msk->x; + mr.y0 = msk->y; + mr.x1 = msk->x + msk->w; + mr.y1 = msk->y + msk->h; + + dr = fz_intersectirects(sr, dr); + dr = fz_intersectirects(dr, mr); + x = dr.x0; + y = dr.y0; + w = dr.x1 - dr.x0; + h = dr.y1 - dr.y0; + + 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; + + if (over) + { + if (src->n == 1 && msk->n == 1 && dst->n == 1) + fz_duff_1i1o1(sp, src->w, mp, msk->w, dp, dst->w, w, h); + else if (src->n == 4 && msk->n == 1 && dst->n == 4) + fz_duff_4i1o4(sp, src->w * 4, mp, msk->w, dp, dst->w * 4, w, h); + else if (src->n == dst->n) + fz_duff_nimon(sp, src->w * src->n, src->n, mp, msk->w * msk->n, msk->n, dp, dst->w * dst->n, w, h); + else + assert(!"blendmaskover src and msk and dst mismatch"); + } + else + { + if (src->n == 1 && msk->n == 1 && dst->n == 1) + fz_duff_1i1c1(sp, src->w, mp, msk->w, dp, dst->w, w, h); + else if (src->n == 4 && msk->n == 1 && dst->n == 4) + fz_duff_4i1c4(sp, src->w * 4, mp, msk->w, dp, dst->w * 4, w, h); + else if (src->n == dst->n) + fz_duff_nimcn(sp, src->w * src->n, src->n, mp, msk->w * msk->n, msk->n, dp, dst->w * dst->n, w, h); + else + assert(!"blendmask src and msk and dst mismatch"); + } +} + +static fz_error * +renderover(fz_renderer *gc, fz_overnode *over, fz_matrix ctm) +{ + fz_error *error; + fz_node *child; + int cluster = 0; + + if (!gc->over) + { +DEBUG("over cluster %d\n{\n", gc->maskonly ? 1 : 4); + cluster = 1; + if (gc->maskonly) + error = fz_newpixmapwithrect(&gc->over, gc->clip, 1); + else + error = fz_newpixmapwithrect(&gc->over, gc->clip, 4); + if (error) + return error; + fz_clearpixmap(gc->over); + } +else DEBUG("over\n{\n"); + + for (child = over->super.first; child; child = child->next) + { + error = rendernode(gc, child, ctm); + if (error) + return error; + if (gc->dest) + { + blendover(gc, gc->dest, gc->over); + fz_droppixmap(gc->dest); + gc->dest = nil; + } + } + + if (cluster) + { + gc->dest = gc->over; + gc->over = nil; + } + +DEBUG("}\n"); + + return fz_okay; +} + +static fz_error * +rendermask(fz_renderer *gc, fz_masknode *mask, fz_matrix ctm) +{ + fz_error *error; + int oldmaskonly; + fz_pixmap *oldover; + fz_irect oldclip; + fz_irect bbox; + fz_irect clip; + fz_pixmap *shapepix = nil; + fz_pixmap *colorpix = nil; + fz_node *shape; + fz_node *color; + float rgb[3]; + + shape = mask->super.first; + color = shape->next; + + /* special case black voodo */ + if (gc->flag & FOVER) + { + if (fz_issolidnode(color)) + { + fz_solidnode *solid = (fz_solidnode*)color; + + fz_convertcolor(solid->cs, solid->samples, gc->model, rgb); + gc->argb[0] = solid->a * 255; + gc->argb[1] = rgb[0] * solid->a * 255; + gc->argb[2] = rgb[1] * solid->a * 255; + gc->argb[3] = rgb[2] * solid->a * 255; + gc->argb[4] = rgb[0] * 255; + gc->argb[5] = rgb[1] * 255; + gc->argb[6] = rgb[2] * 255; + gc->flag |= FRGB; + + /* we know these can handle the FRGB shortcut */ + if (fz_ispathnode(shape)) + return renderpath(gc, (fz_pathnode*)shape, ctm); + if (fz_istextnode(shape)) + return rendertext(gc, (fz_textnode*)shape, ctm); + if (fz_isimagenode(shape)) + return renderimage(gc, (fz_imagenode*)shape, ctm); + } + } + + oldclip = gc->clip; + oldover = gc->over; + + bbox = fz_roundrect(fz_boundnode(shape, ctm)); + clip = fz_intersectirects(bbox, gc->clip); + bbox = fz_roundrect(fz_boundnode(color, ctm)); + clip = fz_intersectirects(bbox, clip); + + if (fz_isemptyrect(clip)) + return fz_okay; + +DEBUG("mask [%d %d %d %d]\n{\n", clip.x0, clip.y0, clip.x1, clip.y1); + +{ +fz_irect sbox = fz_roundrect(fz_boundnode(shape, ctm)); +fz_irect cbox = fz_roundrect(fz_boundnode(color, ctm)); +if (cbox.x0 >= sbox.x0 && cbox.x1 <= sbox.x1) +if (cbox.y0 >= sbox.y0 && cbox.y1 <= sbox.y1) +DEBUG("potentially useless mask\n"); +} + + gc->clip = clip; + gc->over = nil; + + oldmaskonly = gc->maskonly; + gc->maskonly = 1; + + error = rendernode(gc, shape, ctm); + if (error) + goto cleanup; + shapepix = gc->dest; + gc->dest = nil; + + gc->maskonly = oldmaskonly; + + error = rendernode(gc, color, ctm); + if (error) + goto cleanup; + colorpix = gc->dest; + gc->dest = nil; + + gc->clip = oldclip; + gc->over = oldover; + + if (shapepix && colorpix) + { + if (gc->over) + { + blendmask(gc, colorpix, shapepix, gc->over, 1); + } + else + { + clip.x0 = MAX(colorpix->x, shapepix->x); + clip.y0 = MAX(colorpix->y, shapepix->y); + clip.x1 = MIN(colorpix->x+colorpix->w, shapepix->x+shapepix->w); + clip.y1 = MIN(colorpix->y+colorpix->h, shapepix->y+shapepix->h); + error = fz_newpixmapwithrect(&gc->dest, clip, colorpix->n); + if (error) + goto cleanup; + blendmask(gc, colorpix, shapepix, gc->dest, 0); + } + } + +DEBUG("}\n"); + + if (shapepix) fz_droppixmap(shapepix); + if (colorpix) fz_droppixmap(colorpix); + return fz_okay; + +cleanup: + if (shapepix) fz_droppixmap(shapepix); + if (colorpix) fz_droppixmap(colorpix); + return error; +} + +/* + * Dispatch + */ + +static fz_error * +rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm) +{ + if (!node) + return fz_okay; + + gc->flag = FNONE; + if (gc->over) + gc->flag |= FOVER; + + switch (node->kind) + { + case FZ_NOVER: + return renderover(gc, (fz_overnode*)node, ctm); + case FZ_NMASK: + return rendermask(gc, (fz_masknode*)node, ctm); + case FZ_NTRANSFORM: + return rendertransform(gc, (fz_transformnode*)node, ctm); + case FZ_NCOLOR: + return rendersolid(gc, (fz_solidnode*)node, ctm); + case FZ_NPATH: + return renderpath(gc, (fz_pathnode*)node, ctm); + case FZ_NTEXT: + return rendertext(gc, (fz_textnode*)node, ctm); + case FZ_NIMAGE: + return renderimage(gc, (fz_imagenode*)node, ctm); + case FZ_NSHADE: + return rendershade(gc, (fz_shadenode*)node, ctm); + case FZ_NLINK: + return rendernode(gc, ((fz_linknode*)node)->tree->root, ctm); + case FZ_NBLEND: + return fz_okay; + } + + return fz_okay; +} + +fz_error * +fz_rendertree(fz_pixmap **outp, + fz_renderer *gc, fz_tree *tree, fz_matrix ctm, + fz_irect bbox, int white) +{ + fz_error *error; + + gc->clip = bbox; + gc->over = nil; + + if (gc->maskonly) + error = fz_newpixmapwithrect(&gc->over, bbox, 1); + else + error = fz_newpixmapwithrect(&gc->over, bbox, 4); + if (error) + return error; + + if (white) + memset(gc->over->samples, 0xff, gc->over->w * gc->over->h * gc->over->n); + else + memset(gc->over->samples, 0x00, gc->over->w * gc->over->h * gc->over->n); + +DEBUG("tree %d [%d %d %d %d]\n{\n", +gc->maskonly ? 1 : 4, +bbox.x0, bbox.y0, bbox.x1, bbox.y1); + + error = rendernode(gc, tree->root, ctm); + if (error) + return error; + +DEBUG("}\n"); + + if (gc->dest) + { + blendover(gc, gc->dest, gc->over); + fz_droppixmap(gc->dest); + gc->dest = nil; + } + + *outp = gc->over; + gc->over = nil; + + return fz_okay; +} + +fz_error * +fz_rendertreeover(fz_renderer *gc, fz_pixmap *dest, fz_tree *tree, fz_matrix ctm) +{ + fz_error *error; + + assert(!gc->maskonly); + assert(dest->n == 4); + + gc->clip.x0 = dest->x; + gc->clip.y0 = dest->y; + gc->clip.x1 = dest->x + dest->w; + gc->clip.y1 = dest->y + dest->h; + + gc->over = dest; + + error = rendernode(gc, tree->root, ctm); + if (error) + { + gc->over = nil; + return error; + } + + if (gc->dest) + { + blendover(gc, gc->dest, gc->over); + fz_droppixmap(gc->dest); + gc->dest = nil; + } + + gc->over = nil; + + return fz_okay; +} + |