Esempio n. 1
0
void pdfmoz_onmouse(pdfmoz_t *moz, int x, int y, int click)
{
    char buf[512];
    pdf_link *link;
    fz_matrix ctm;
    fz_point p;
    int pi;
    int py;

    if (!moz->pages)
	return;

    pi = moz->scrollpage;
    py = -moz->scrollyofs;
    while (pi < moz->pagecount)
    {
	if (!moz->pages[pi].image)
	    return;
	if (y > py && y < moz->pages[pi].px)
	    break;
	py += moz->pages[pi].px;
	pi ++;
    }
    if (pi == moz->pagecount)
	return;

    p.x = x + moz->pages[pi].image->x;
    p.y = y + moz->pages[pi].image->y - py;

    ctm = pdfmoz_pagectm(moz, pi);
    ctm = fz_invertmatrix(ctm);

    p = fz_transformpoint(ctm, p);

    for (link = moz->pages[pi].page->links; link; link = link->next)
    {
	if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
	    if (p.y >= link->rect.y0 && p.y <= link->rect.y1)
		break;
    }

    if (link)
    {
	SetCursor(moz->hand);
	if (click)
	{
	    if (link->kind == PDF_LURI)
		pdfmoz_gotouri(moz, link->dest);
	    else if (link->kind == PDF_LGOTO)
		pdfmoz_gotopage(moz, link->dest);
	    return;
	}
	else
	{
	    if (fz_isstring(link->dest))
	    {
		memcpy(buf, fz_tostrbuf(link->dest), fz_tostrlen(link->dest));
		buf[fz_tostrlen(link->dest)] = 0;
		NPN_Status(moz->inst, buf);
	    }
	    else if (fz_isindirect(link->dest))
	    {
		sprintf(buf, "Go to page %d",
			pdfmoz_getpagenum(moz, link->dest) + 1);
		NPN_Status(moz->inst, buf);
	    }
	    else
		NPN_Status(moz->inst, "Say what?");
	}
    }
    else
    {
	sprintf(buf, "Page %d of %d", moz->scrollpage + 1, moz->pagecount);
	NPN_Status(moz->inst, buf);
	SetCursor(moz->arrow);
    }
}
Esempio n. 2
0
void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int state)
{
	pdf_link *link;
	fz_matrix ctm;
	fz_point p;

	p.x = x - app->panx + app->image->x;
	p.y = y - app->pany + app->image->y;

	ctm = pdfapp_viewctm(app);
	ctm = fz_invertmatrix(ctm);

	p = fz_transformpoint(ctm, p);

	for (link = app->page->links; link; link = link->next)
	{
		if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
			if (p.y >= link->rect.y0 && p.y <= link->rect.y1)
				break;
	}

	if (link)
	{
		wincursor(app, HAND);
		if (btn == 1 && state == 1)
		{
			if (fz_isstring(link->dest))
				pdfapp_gotouri(app, link->dest);
			if (fz_isindirect(link->dest))
				pdfapp_gotopage(app, link->dest);
			return;
		}
	}
	else
	{
		wincursor(app, ARROW);
	}

	if (state == 1)
	{
		if (btn == 1 && !app->iscopying)
		{
			app->ispanning = 1;
			app->selx = x;
			app->sely = y;
		}
		if (btn == 3 && !app->ispanning)
		{
			app->iscopying = 1;
			app->selx = x;
			app->sely = y;
			app->selr.x0 = x;
			app->selr.x1 = x;
			app->selr.y0 = y;
			app->selr.y1 = y;
		}
		if (btn == 4 || btn == 5) /* scroll wheel */
		{
			int dir = btn == 4 ? 1 : -1;
			app->ispanning = app->iscopying = 0;
			if (modifiers & (1<<2))
			{
				/* zoom in/out if ctrl is pressed */
				app->zoom += 0.1 * dir;
				if (app->zoom > 3.0)
					app->zoom = 3.0;
				if (app->zoom < 0.1)
					app->zoom = 0.1;
				pdfapp_showpage(app, 0, 1);
			}
			else
			{
				/* scroll up/down, or left/right if
				   shift is pressed */
				int isx = (modifiers & (1<<0));
				int xstep = isx ? 20 * dir : 0;
				int ystep = !isx ? 20 * dir : 0;
				pdfapp_panview(app, app->panx + xstep, app->pany + ystep);
			}
		}
	}

	else if (state == -1)
	{
		if (app->iscopying)
		{
			app->iscopying = 0;
			app->selr.x0 = MIN(app->selx, x);
			app->selr.x1 = MAX(app->selx, x);
			app->selr.y0 = MIN(app->sely, y);
			app->selr.y1 = MAX(app->sely, y);
			winrepaint(app);
			if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1)
				windocopy(app);
		}
		if (app->ispanning)
			app->ispanning = 0;
	}

	else if (app->ispanning)
	{
		int newx = app->panx + x - app->selx;
		int newy = app->pany + y - app->sely;
		pdfapp_panview(app, newx, newy);
		app->selx = x;
		app->sely = y;
	}

	else if (app->iscopying)
	{
		app->selr.x0 = MIN(app->selx, x);
		app->selr.x1 = MAX(app->selx, x);
		app->selr.y0 = MIN(app->sely, y);
		app->selr.y1 = MAX(app->sely, y);
		winrepaint(app);
	}

}
Esempio n. 3
0
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;

	if (image->n + image->a == 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;
}
Esempio n. 4
0
static void
pdf_showpattern(pdf_csi *csi, pdf_pattern *pat, fz_rect bbox, int what)
{
	pdf_gstate *gstate;
	fz_matrix ptm, invptm;
	fz_matrix oldtopctm;
	fz_error error;
	int x, y, x0, y0, x1, y1;
	int oldtop;

	pdf_gsave(csi);
	gstate = csi->gstate + csi->gtop;

	if (pat->ismask)
	{
		pdf_unsetpattern(csi, PDF_MFILL);
		pdf_unsetpattern(csi, PDF_MSTROKE);
		if (what == PDF_MFILL)
		{
			pdf_dropmaterial(&gstate->stroke);
			pdf_keepmaterial(&gstate->fill);
			gstate->stroke = gstate->fill;
		}
		if (what == PDF_MSTROKE)
		{
			pdf_dropmaterial(&gstate->fill);
			pdf_keepmaterial(&gstate->stroke);
			gstate->fill = gstate->stroke;
		}
	}
	else
	{
		// TODO: unset only the current fill/stroke or both?
		pdf_unsetpattern(csi, what);
	}

	/* don't apply softmasks to objects in the pattern as well */
	if (gstate->softmask)
	{
		pdf_dropxobject(gstate->softmask);
		gstate->softmask = nil;
	}

	ptm = fz_concat(pat->matrix, csi->topctm);
	invptm = fz_invertmatrix(ptm);

	/* patterns are painted using the ctm in effect at the beginning of the content stream */
	/* get bbox of shape in pattern space for stamping */
	bbox = fz_transformrect(invptm, bbox);
	x0 = floorf(bbox.x0 / pat->xstep);
	y0 = floorf(bbox.y0 / pat->ystep);
	x1 = ceilf(bbox.x1 / pat->xstep);
	y1 = ceilf(bbox.y1 / pat->ystep);

	oldtopctm = csi->topctm;
	oldtop = csi->gtop;

	for (y = y0; y < y1; y++)
	{
		for (x = x0; x < x1; x++)
		{
			gstate->ctm = fz_concat(fz_translate(x * pat->xstep, y * pat->ystep), ptm);
			csi->topctm = gstate->ctm;
			error = pdf_runcsibuffer(csi, pat->resources, pat->contents);
			while (oldtop < csi->gtop)
				pdf_grestore(csi);
			if (error)
			{
				fz_catch(error, "cannot render pattern tile");
				goto cleanup;
			}
		}
	}

cleanup:
	csi->topctm = oldtopctm;

	pdf_grestore(csi);
}
Esempio n. 5
0
static fz_error *
addshadeshape(pdf_gstate *gs, fz_node *shape, fz_shade *shade)
{
	fz_error *error;
	fz_node *mask;
	fz_node *color;
	fz_node *xform;
	fz_node *over;
	fz_node *bgnd;
	fz_matrix ctm;
	fz_matrix inv;

	ctm = getmatrix(gs->head);
	inv = fz_invertmatrix(ctm);

	error = fz_newtransformnode(&xform, inv);
	if (error)
		return fz_rethrow(error, "cannot create transform node");

	error = fz_newmasknode(&mask);
	if (error)
	{
		fz_dropnode(xform);
		return fz_rethrow(error, "cannot create mask node");
	}

	error = fz_newshadenode(&color, shade);
	if (error)
	{
		fz_dropnode(mask);
		fz_dropnode(xform);
		return fz_rethrow(error, "cannot create shade node");
	}

	if (shade->usebackground)
	{
		error = pdf_newovernode(&over, gs);
		if (error)
		{
			fz_dropnode(color);
			fz_dropnode(mask);
			fz_dropnode(xform);
			return fz_rethrow(error, "cannot create over node for background color");
		}

		error = fz_newsolidnode(&bgnd, 1.0f, shade->cs, shade->cs->n, shade->background);
		if (error)
		{
			fz_dropnode(over);
			fz_dropnode(color);
			fz_dropnode(mask);
			fz_dropnode(xform);
			return fz_rethrow(error, "cannot create solid node for background color");;
		}

		fz_insertnodelast(mask, shape);
		fz_insertnodelast(over, bgnd);
		fz_insertnodelast(over, color);
		fz_insertnodelast(xform, over);
		fz_insertnodelast(mask, xform);
		fz_insertnodelast(gs->head, mask);
	}
	else
	{
		fz_insertnodelast(mask, shape);
		fz_insertnodelast(xform, color);
		fz_insertnodelast(mask, xform);
		fz_insertnodelast(gs->head, mask);
	}

	return fz_okay;
}
Esempio n. 6
0
static fz_error *
addpatternshape(pdf_gstate *gs, fz_node *shape,
		pdf_pattern *pat, fz_colorspace *cs, float *v)
{
	fz_error *error;
	fz_node *xform;
	fz_node *over;
	fz_node *mask;
	fz_node *link;
	fz_matrix ctm;
	fz_matrix inv;
	fz_matrix ptm;
	fz_rect bbox;
	int x, y, x0, y0, x1, y1;

	/* patterns are painted in user space */
	ctm = getmatrix(gs->head);
	inv = fz_invertmatrix(ctm);

	error = fz_newmasknode(&mask);
	if (error)
		return fz_rethrow(error, "cannot create mask node");

	ptm = fz_concat(pat->matrix, fz_invertmatrix(ctm));
	error = fz_newtransformnode(&xform, ptm);
	if (error)
	{
		fz_dropnode(mask);
		return fz_rethrow(error, "cannot create transform node");
	}

	error = pdf_newovernode(&over, gs);
	if (error)
	{
		fz_dropnode(xform);
		fz_dropnode(mask);
		return fz_rethrow(error, "cannot create over node");
	}

	fz_insertnodelast(mask, shape);
	fz_insertnodelast(mask, xform);
	fz_insertnodelast(xform, over);
	xform = nil;

	/* over, xform, mask are now owned by the tree */

	/* get bbox of shape in pattern space for stamping */
	ptm = fz_concat(ctm, fz_invertmatrix(pat->matrix));
	bbox = fz_boundnode(shape, ptm);

	/* expand bbox by pattern bbox */
	bbox.x0 += pat->bbox.x0;
	bbox.y0 += pat->bbox.y0;
	bbox.x1 += pat->bbox.x1;
	bbox.y1 += pat->bbox.y1;

	x0 = fz_floor(bbox.x0 / pat->xstep);
	y0 = fz_floor(bbox.y0 / pat->ystep);
	x1 = fz_ceil(bbox.x1 / pat->xstep);
	y1 = fz_ceil(bbox.y1 / pat->ystep);

	for (y = y0; y <= y1; y++)
	{
		for (x = x0; x <= x1; x++)
		{
			ptm = fz_translate(x * pat->xstep, y * pat->ystep);
			error = fz_newtransformnode(&xform, ptm);
			if (error)
				return fz_rethrow(error, "cannot create transform node for stamp");
			error = fz_newlinknode(&link, pat->tree);
			if (error)
			{
				fz_dropnode(xform);
				return fz_rethrow(error, "cannot create link node for stamp");
			}
			fz_insertnodelast(xform, link);
			fz_insertnodelast(over, xform);
		}
	}

	if (pat->ismask)
	{
		error = addcolorshape(gs, mask, 1.0, cs, v);
		if (error)
			return fz_rethrow(error, "cannot add colored shape");
		return fz_okay;
	}

	fz_insertnodelast(gs->head, mask);
	return fz_okay;
}
Esempio n. 7
0
void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int state)
{
    pdf_link *link;
    fz_matrix ctm;
    fz_point p;

    p.x = x - app->panx + app->image->x;
    p.y = y - app->pany + app->image->y;

    ctm = pdfapp_viewctm(app);
    ctm = fz_invertmatrix(ctm);

    p = fz_transformpoint(ctm, p);

    for (link = app->page->links; link; link = link->next)
    {
        if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
            if (p.y >= link->rect.y0 && p.y <= link->rect.y1)
                break;
    }

    if (link)
    {
        wincursor(app, HAND);
        if (btn == 1 && state == 1)
        {
            if (link->kind == PDF_LURI)
                pdfapp_gotouri(app, link->dest);
            else if (link->kind == PDF_LGOTO)
                pdfapp_gotopage(app, link->dest);
            return;
        }
    }
    else
    {
        wincursor(app, ARROW);
    }

    if (state == 1)
    {
        if (btn == 1 && !app->iscopying)
        {
            app->ispanning = 1;
            app->selx = x;
            app->sely = y;
        }
        if (btn == 3 && !app->ispanning)
        {
            kno_clearselect(app); //code change by kakai
            app->iscopying = 1;
            app->selx = x;
            app->sely = y;
            app->selr.x0 = x;
            app->selr.x1 = x;
            app->selr.y0 = y;
            app->selr.y1 = y;
        }
        if (btn == 4 || btn == 5) /* scroll wheel */
        {
            int dir = btn == 4 ? 1 : -1;
            app->ispanning = app->iscopying = 0;
            if (modifiers & (1<<2))
            {
                /* zoom in/out if ctrl is pressed */
                app->zoom += 0.1 * dir;
                if (app->zoom > 3.0)
                    app->zoom = 3.0;
                if (app->zoom < 0.1)
                    app->zoom = 0.1;
                pdfapp_showpage(app, 0, 1);
            }
            else
            {
                /* scroll up/down, or left/right if
                shift is pressed */
                int isx = (modifiers & (1<<0));
                int xstep = isx ? 20 * dir : 0;
                int ystep = !isx ? 20 * dir : 0;
                pdfapp_panview(app, app->panx + xstep, app->pany + ystep);
            }
        }
    }

    else if (state == -1)
    {
        //Code change by Kakai
        //Hit testing
        kno_hitdata *hitdata = kno_gethitdata(app, x, y);
        printf("hit test char is: %c\n", hitdata->ucs);
        //Code change by Kakai
        if (app->iscopying)
        {
            app->iscopying = 0;
            app->selr.x0 = MIN(app->selx, x);
            app->selr.x1 = MAX(app->selx, x);
            app->selr.y0 = MIN(app->sely, y);
            app->selr.y1 = MAX(app->sely, y);
            winrepaint(app);
            if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1)
                windocopy(app);
        }
        if (app->ispanning)
            app->ispanning = 0;
    }

    else if (app->ispanning)
    {
        int newx = app->panx + x - app->selx;
        int newy = app->pany + y - app->sely;
        pdfapp_panview(app, newx, newy);
        app->selx = x;
        app->sely = y;
    }

    else if (app->iscopying)
    {
        app->selr.x0 = MIN(app->selx, x);
        app->selr.x1 = MAX(app->selx, x);
        app->selr.y0 = MIN(app->sely, y);
        app->selr.y1 = MAX(app->sely, y);
        //code change by kakai
        //IsHighlightable and selection testing
        int closestx, closesty;
        closestx = closesty = 0;
        if (kno_ishighlightable(app, x, y, &closestx, &closesty) == 1)
            kno_onselect(app); //code change by kakai
        else
        {
            printf("x is %d\n", closestx);
            printf("y is %d\n", closesty);
        }
        //code change by kakai
        winrepaint(app);
    }

}
Esempio n. 8
0
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;

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 nil;

	calcimagescale(ctm, image->w, image->h, &dx, &dy);

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_w3i1o4(gc->rgb, PSRC, PDST(gc->over), PCTM);
		break;

	default:
		assert(!"impossible flag in image span function");
	}

DEBUG("}\n");

	fz_droppixmap(tile);
	return nil;

cleanup:
	fz_droppixmap(tile);
	return error;
}