mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 19:41:57 +00:00
435a566751
* sumatrapdf - vendor import * everything compiles (libjpeg, poppler, fitz, sumatrapdf) * does NOT link (remove the comment tags in the parent directory.rbuild file (rosapps dir) to build it) svn path=/trunk/; revision=29295
487 lines
7.9 KiB
C
487 lines
7.9 KiB
C
#include "fitz-base.h"
|
|
#include "fitz-world.h"
|
|
#include "fitz-draw.h"
|
|
|
|
/*
|
|
* 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->xmin = gel->ymin = INT_MAX;
|
|
gel->xmax = gel->ymax = INT_MIN;
|
|
gel->hs = 1;
|
|
gel->vs = 1;
|
|
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
fz_resetgel(fz_gel *gel, int hs, int vs)
|
|
{
|
|
gel->xmin = gel->ymin = INT_MAX;
|
|
gel->xmax = gel->ymax = INT_MIN;
|
|
gel->hs = hs;
|
|
gel->vs = vs;
|
|
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->xmin, gel->hs);
|
|
bbox.y0 = fz_idiv(gel->ymin, gel->vs);
|
|
bbox.x1 = fz_idiv(gel->xmax, gel->hs) + 1;
|
|
bbox.y1 = fz_idiv(gel->ymax, gel->vs) + 1;
|
|
return bbox;
|
|
}
|
|
|
|
fz_error *
|
|
fz_insertgel(fz_gel *gel, float fx0, float fy0, float fx1, float fy1)
|
|
{
|
|
fz_edge *edge;
|
|
int x0, y0, x1, y1;
|
|
int dx, dy;
|
|
int winding;
|
|
int width;
|
|
int tmp;
|
|
|
|
fx0 *= gel->hs;
|
|
fy0 *= gel->vs;
|
|
fx1 *= gel->hs;
|
|
fy1 *= gel->vs;
|
|
|
|
/* TODO: should we round or truncate? */
|
|
x0 = fx0 < 0 ? fx0 - 0.5 : fx0 + 0.5;
|
|
y0 = fy0 < 0 ? fy0 - 0.5 : fy0 + 0.5;
|
|
x1 = fx1 < 0 ? fx1 - 0.5 : fx1 + 0.5;
|
|
y1 = fy1 < 0 ? fy1 - 0.5 : fy1 + 0.5;
|
|
|
|
if (y0 == y1)
|
|
return nil;
|
|
|
|
if (y0 > y1) {
|
|
winding = -1;
|
|
tmp = x0; x0 = x1; x1 = tmp;
|
|
tmp = y0; y0 = y1; y1 = tmp;
|
|
}
|
|
else
|
|
winding = 1;
|
|
|
|
if (x0 < gel->xmin) gel->xmin = x0;
|
|
if (x0 > gel->xmax) gel->xmax = x0;
|
|
if (x1 < gel->xmin) gel->xmin = x1;
|
|
if (x1 > gel->xmax) gel->xmax = x1;
|
|
|
|
if (y0 < gel->ymin) gel->ymin = y0;
|
|
if (y1 > gel->ymax) gel->ymax = 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 nil;
|
|
}
|
|
|
|
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 nil;
|
|
}
|
|
|
|
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 */
|
|
int e2 = *e;
|
|
while (e2 < gel->len && gel->edges[e2].y == y) {
|
|
if (ael->len + 1 == ael->cap) {
|
|
int newcap = ael->cap + 256;
|
|
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[e2++];
|
|
}
|
|
*e = e2;
|
|
/* shell-sort the edges by increasing x */
|
|
sortael(ael->edges, ael->len);
|
|
|
|
return nil;
|
|
}
|
|
|
|
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 hs)
|
|
{
|
|
int x0pix, x0sub;
|
|
int x1pix, x1sub;
|
|
|
|
if (x0 == x1)
|
|
return;
|
|
|
|
/* x between 0 and width of bbox */
|
|
x0 -= xofs;
|
|
x1 -= xofs;
|
|
|
|
x0pix = x0 / hs;
|
|
x0sub = x0 % hs;
|
|
x1pix = x1 / hs;
|
|
x1sub = x1 % hs;
|
|
|
|
if (x0pix == x1pix)
|
|
{
|
|
list[x0pix] += x1sub - x0sub;
|
|
list[x0pix+1] += x0sub - x1sub;
|
|
}
|
|
|
|
else
|
|
{
|
|
list[x0pix] += hs - x0sub;
|
|
list[x0pix+1] += x0sub;
|
|
list[x1pix] += x1sub - hs;
|
|
list[x1pix+1] += -x1sub;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
nonzerowinding(fz_ael *ael, unsigned char *list, int xofs, int hs)
|
|
{
|
|
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, hs);
|
|
winding += ael->edges[i]->ydir;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
evenodd(fz_ael *ael, unsigned char *list, int xofs, int hs)
|
|
{
|
|
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, hs);
|
|
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 *rgb, int over)
|
|
{
|
|
unsigned char *dst;
|
|
int cov;
|
|
|
|
dst = pix->samples + ( (y - pix->y) * pix->w + (x - pix->x) ) * pix->n;
|
|
cov = 0;
|
|
|
|
while (skipx--)
|
|
{
|
|
cov += *list;
|
|
*list = 0;
|
|
++list;
|
|
}
|
|
|
|
if (rgb)
|
|
fz_path_w3i1o4(rgb, 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 *rgb, int over)
|
|
{
|
|
fz_error *error;
|
|
unsigned char *deltas;
|
|
int y, e;
|
|
int yd, yc;
|
|
|
|
int xmin = fz_idiv(gel->xmin, gel->hs);
|
|
int xmax = fz_idiv(gel->xmax, gel->hs) + 1;
|
|
|
|
int xofs = xmin * gel->hs;
|
|
int hs = gel->hs;
|
|
int vs = gel->vs;
|
|
|
|
int skipx = clip.x0 - xmin;
|
|
int clipn = clip.x1 - clip.x0;
|
|
|
|
assert(clip.x0 >= xmin);
|
|
assert(clip.x1 <= xmax);
|
|
|
|
if (gel->len == 0)
|
|
return nil;
|
|
|
|
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, vs);
|
|
yd = yc;
|
|
|
|
while (ael->len > 0 || e < gel->len)
|
|
{
|
|
yc = fz_idiv(y, vs);
|
|
if (yc != yd)
|
|
{
|
|
if (yd >= clip.y0 && yd < clip.y1)
|
|
{
|
|
blit(pix, xmin + skipx, yd, deltas, skipx, clipn, rgb, 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, hs);
|
|
else
|
|
nonzerowinding(ael, deltas, xofs, hs);
|
|
}
|
|
|
|
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, rgb, over);
|
|
}
|
|
|
|
fz_free(deltas);
|
|
return nil;
|
|
}
|
|
|