[gs-commits] mupdf/master - 0_6-189-g81fedd6 - Support SMasks for general graphics (incomplete).

Tor Andersson tor at ghostscript.com
Sat Jul 17 01:47:52 UTC 2010


commit 81fedd67062acfe802c0e5541d31e294df9607c9
Author: Tor Andersson <tor at ghostscript.com>
Date:   Sat Jul 17 01:41:20 2010 +0000

    Support SMasks for general graphics (incomplete).
    
    Ignore-this: 67f56d1a8b654ba609ddb7b168f29a42
    
    darcs-hash:20100717014120-f546f-2af4690a72960c5eba62f01fc5b43c96279e9648.gz

 9 files changed, 333 insertions(+), 223 deletions(-)

diff --git a/draw/blendmodes.c b/draw/blendmodes.c
index 11cb627..713b6cf 100644
--- a/draw/blendmodes.c
+++ b/draw/blendmodes.c
@@ -2,6 +2,27 @@
 
 typedef unsigned char byte;
 
+const char *fz_blendnames[] =
+{
+	"Normal",
+	"Multiply",
+	"Screen",
+	"Overlay",
+	"Darken",
+	"Lighten",
+	"ColorDodge",
+	"ColorBurn",
+	"HardLight",
+	"SoftLight",
+	"Difference",
+	"Exclusion",
+	"Hue",
+	"Saturation",
+	"Color",
+	"Luminosity",
+	nil
+};
+
 /*
 PDF 1.4 blend modes.
 Only the actual blend routines are here, not the rendering logic.
diff --git a/fitz/dev_draw.c b/fitz/dev_draw.c
index 3148360..5c2445a 100644
--- a/fitz/dev_draw.c
+++ b/fitz/dev_draw.c
@@ -4,32 +4,27 @@
 #define HSUBPIX 5.0
 #define VSUBPIX 5.0
 
-#define MAXCLIP 64
+#define STACKSIZE 96
 
 typedef struct fz_drawdevice_s fz_drawdevice;
 
 struct fz_drawdevice_s
 {
-	fz_colorspace *model;
 	fz_glyphcache *cache;
 	fz_gel *gel;
 	fz_ael *ael;
-	fz_pixmap *dest;
 
+	fz_pixmap *dest;
 	fz_bbox scissor;
-	struct {
-		fz_pixmap *dest;
-		fz_pixmap *mask;
-		fz_bbox scissor;
-	} clipstack[MAXCLIP];
-	int cliptop;
 
 	struct {
+		fz_bbox scissor;
 		fz_pixmap *dest;
+		fz_pixmap *mask;
 		fz_blendmode blendmode;
-		fz_bbox scissor;
-	} groupstack[MAXCLIP];
-	int grouptop;
+		int luminosity;
+	} stack[STACKSIZE];
+	int top;
 };
 
 static void
@@ -84,6 +79,7 @@ fz_drawfillpath(void *user, fz_path *path, int evenodd, fz_matrix ctm,
 	fz_colorspace *colorspace, float *color, float alpha)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	float expansion = fz_matrixexpansion(ctm);
 	float flatness = 0.3f / expansion;
 	unsigned char colorbv[FZ_MAXCOLORS + 1];
@@ -101,10 +97,10 @@ fz_drawfillpath(void *user, fz_path *path, int evenodd, fz_matrix ctm,
 	if (fz_isemptyrect(bbox))
 		return;
 
-	if (dev->model)
+	if (model)
 	{
-		fz_convertcolor(colorspace, color, dev->model, colorfv);
-		for (i = 0; i < dev->model->n; i++)
+		fz_convertcolor(colorspace, color, model, colorfv);
+		for (i = 0; i < model->n; i++)
 			colorbv[i] = colorfv[i] * 255;
 		colorbv[i] = alpha * 255;
 		fz_scanconvert(dev->gel, dev->ael, evenodd, bbox, dev->dest, colorbv, nil, nil);
@@ -120,6 +116,7 @@ fz_drawstrokepath(void *user, fz_path *path, fz_strokestate *stroke, fz_matrix c
 	fz_colorspace *colorspace, float *color, float alpha)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	float expansion = fz_matrixexpansion(ctm);
 	float flatness = 0.3f / expansion;
 	float linewidth = stroke->linewidth;
@@ -144,10 +141,10 @@ fz_drawstrokepath(void *user, fz_path *path, fz_strokestate *stroke, fz_matrix c
 	if (fz_isemptyrect(bbox))
 		return;
 
-	if (dev->model)
+	if (model)
 	{
-		fz_convertcolor(colorspace, color, dev->model, colorfv);
-		for (i = 0; i < dev->model->n; i++)
+		fz_convertcolor(colorspace, color, model, colorfv);
+		for (i = 0; i < model->n; i++)
 			colorbv[i] = colorfv[i] * 255;
 		colorbv[i] = alpha * 255;
 		fz_scanconvert(dev->gel, dev->ael, 0, bbox, dev->dest, colorbv, nil, nil);
@@ -162,14 +159,15 @@ static void
 fz_drawclippath(void *user, fz_path *path, int evenodd, fz_matrix ctm)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	float expansion = fz_matrixexpansion(ctm);
 	float flatness = 0.3f / expansion;
 	fz_pixmap *mask, *dest;
 	fz_bbox bbox;
 
-	if (dev->cliptop == MAXCLIP)
+	if (dev->top == STACKSIZE)
 	{
-		fz_warn("assert: too many clip masks on stack");
+		fz_warn("assert: too many buffers on stack");
 		return;
 	}
 
@@ -182,43 +180,44 @@ fz_drawclippath(void *user, fz_path *path, int evenodd, fz_matrix ctm)
 
 	if (fz_isemptyrect(bbox) || fz_isrectgel(dev->gel))
 	{
-		dev->clipstack[dev->cliptop].scissor = dev->scissor;
-		dev->clipstack[dev->cliptop].mask = nil;
-		dev->clipstack[dev->cliptop].dest = nil;
+		dev->stack[dev->top].scissor = dev->scissor;
+		dev->stack[dev->top].mask = nil;
+		dev->stack[dev->top].dest = nil;
 		dev->scissor = bbox;
-		dev->cliptop++;
+		dev->top++;
 		return;
 	}
 
 	mask = fz_newpixmapwithrect(nil, bbox);
-	dest = fz_newpixmapwithrect(dev->model, bbox);
+	dest = fz_newpixmapwithrect(model, bbox);
 
 	fz_clearpixmap(mask, 0);
 	fz_clearpixmap(dest, 0);
 
 	fz_scanconvert(dev->gel, dev->ael, evenodd, bbox, mask, nil, nil, nil);
 
-	dev->clipstack[dev->cliptop].scissor = dev->scissor;
-	dev->clipstack[dev->cliptop].mask = mask;
-	dev->clipstack[dev->cliptop].dest = dev->dest;
+	dev->stack[dev->top].scissor = dev->scissor;
+	dev->stack[dev->top].mask = mask;
+	dev->stack[dev->top].dest = dev->dest;
 	dev->scissor = bbox;
 	dev->dest = dest;
-	dev->cliptop++;
+	dev->top++;
 }
 
 static void
 fz_drawclipstrokepath(void *user, fz_path *path, fz_strokestate *stroke, fz_matrix ctm)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	float expansion = fz_matrixexpansion(ctm);
 	float flatness = 0.3f / expansion;
 	float linewidth = stroke->linewidth;
 	fz_pixmap *mask, *dest;
 	fz_bbox bbox;
 
-	if (dev->cliptop == MAXCLIP)
+	if (dev->top == STACKSIZE)
 	{
-		fz_warn("assert: too many clip masks on stack");
+		fz_warn("assert: too many buffers on stack");
 		return;
 	}
 
@@ -236,7 +235,7 @@ fz_drawclipstrokepath(void *user, fz_path *path, fz_strokestate *stroke, fz_matr
 	bbox = fz_intersectbbox(bbox, dev->scissor);
 
 	mask = fz_newpixmapwithrect(nil, bbox);
-	dest = fz_newpixmapwithrect(dev->model, bbox);
+	dest = fz_newpixmapwithrect(model, bbox);
 
 	fz_clearpixmap(mask, 0);
 	fz_clearpixmap(dest, 0);
@@ -244,12 +243,12 @@ fz_drawclipstrokepath(void *user, fz_path *path, fz_strokestate *stroke, fz_matr
 	if (!fz_isemptyrect(bbox))
 		fz_scanconvert(dev->gel, dev->ael, 0, bbox, mask, nil, nil, nil);
 
-	dev->clipstack[dev->cliptop].scissor = dev->scissor;
-	dev->clipstack[dev->cliptop].mask = mask;
-	dev->clipstack[dev->cliptop].dest = dev->dest;
+	dev->stack[dev->top].scissor = dev->scissor;
+	dev->stack[dev->top].mask = mask;
+	dev->stack[dev->top].dest = dev->dest;
 	dev->scissor = bbox;
 	dev->dest = dest;
-	dev->cliptop++;
+	dev->top++;
 }
 
 static void
@@ -311,16 +310,17 @@ fz_drawfilltext(void *user, fz_text *text, fz_matrix ctm,
 	fz_colorspace *colorspace, float *color, float alpha)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	unsigned char colorbv[FZ_MAXCOLORS + 1];
 	float colorfv[FZ_MAXCOLORS];
 	fz_matrix tm, trm;
 	fz_pixmap *glyph;
 	int i, x, y, gid;
 
-	if (dev->model)
+	if (model)
 	{
-		fz_convertcolor(colorspace, color, dev->model, colorfv);
-		for (i = 0; i < dev->model->n; i++)
+		fz_convertcolor(colorspace, color, model, colorfv);
+		for (i = 0; i < model->n; i++)
 			colorbv[i] = colorfv[i] * 255;
 		colorbv[i] = alpha * 255;
 	}
@@ -344,7 +344,7 @@ fz_drawfilltext(void *user, fz_text *text, fz_matrix ctm,
 		glyph = fz_renderglyph(dev->cache, text->font, gid, trm);
 		if (glyph)
 		{
-			if (dev->model)
+			if (model)
 				drawglyph(colorbv, dev->dest, glyph, x, y, dev->scissor);
 			else
 				drawglyph(nil, dev->dest, glyph, x, y, dev->scissor);
@@ -358,16 +358,17 @@ fz_drawstroketext(void *user, fz_text *text, fz_strokestate *stroke, fz_matrix c
 	fz_colorspace *colorspace, float *color, float alpha)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	unsigned char colorbv[FZ_MAXCOLORS + 1];
 	float colorfv[FZ_MAXCOLORS];
 	fz_matrix tm, trm;
 	fz_pixmap *glyph;
 	int i, x, y, gid;
 
-	if (dev->model)
+	if (model)
 	{
-		fz_convertcolor(colorspace, color, dev->model, colorfv);
-		for (i = 0; i < dev->model->n; i++)
+		fz_convertcolor(colorspace, color, model, colorfv);
+		for (i = 0; i < model->n; i++)
 			colorbv[i] = colorfv[i] * 255;
 		colorbv[i] = alpha * 255;
 	}
@@ -391,7 +392,7 @@ fz_drawstroketext(void *user, fz_text *text, fz_strokestate *stroke, fz_matrix c
 		glyph = fz_renderstrokedglyph(dev->cache, text->font, gid, trm, ctm, stroke);
 		if (glyph)
 		{
-			if (dev->model)
+			if (model)
 				drawglyph(colorbv, dev->dest, glyph, x, y, dev->scissor);
 			else
 				drawglyph(nil, dev->dest, glyph, x, y, dev->scissor);
@@ -404,6 +405,7 @@ static void
 fz_drawcliptext(void *user, fz_text *text, fz_matrix ctm, int accumulate)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	fz_bbox bbox;
 	fz_pixmap *mask, *dest;
 	fz_matrix tm, trm;
@@ -414,9 +416,9 @@ fz_drawcliptext(void *user, fz_text *text, fz_matrix ctm, int accumulate)
 	/* If accumulate == 1 then this text object is the first (or only) in a sequence */
 	/* If accumulate == 2 then this text object is a continuation */
 
-	if (dev->cliptop == MAXCLIP)
+	if (dev->top == STACKSIZE)
 	{
-		fz_warn("assert: too many clip masks on stack");
+		fz_warn("assert: too many buffers on stack");
 		return;
 	}
 
@@ -435,21 +437,21 @@ fz_drawcliptext(void *user, fz_text *text, fz_matrix ctm, int accumulate)
 	if (accumulate == 0 || accumulate == 1)
 	{
 		mask = fz_newpixmapwithrect(nil, bbox);
-		dest = fz_newpixmapwithrect(dev->model, bbox);
+		dest = fz_newpixmapwithrect(model, bbox);
 
 		fz_clearpixmap(mask, 0);
 		fz_clearpixmap(dest, 0);
 
-		dev->clipstack[dev->cliptop].scissor = dev->scissor;
-		dev->clipstack[dev->cliptop].mask = mask;
-		dev->clipstack[dev->cliptop].dest = dev->dest;
+		dev->stack[dev->top].scissor = dev->scissor;
+		dev->stack[dev->top].mask = mask;
+		dev->stack[dev->top].dest = dev->dest;
 		dev->scissor = bbox;
 		dev->dest = dest;
-		dev->cliptop++;
+		dev->top++;
 	}
 	else
 	{
-		mask = dev->clipstack[dev->cliptop-1].mask;
+		mask = dev->stack[dev->top-1].mask;
 	}
 
 	if (!fz_isemptyrect(bbox))
@@ -484,15 +486,16 @@ static void
 fz_drawclipstroketext(void *user, fz_text *text, fz_strokestate *stroke, fz_matrix ctm)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	fz_bbox bbox;
 	fz_pixmap *mask, *dest;
 	fz_matrix tm, trm;
 	fz_pixmap *glyph;
 	int i, x, y, gid;
 
-	if (dev->cliptop == MAXCLIP)
+	if (dev->top == STACKSIZE)
 	{
-		fz_warn("assert: too many clip masks on stack");
+		fz_warn("assert: too many buffers on stack");
 		return;
 	}
 
@@ -501,17 +504,17 @@ fz_drawclipstroketext(void *user, fz_text *text, fz_strokestate *stroke, fz_matr
 	bbox = fz_intersectbbox(bbox, dev->scissor);
 
 	mask = fz_newpixmapwithrect(nil, bbox);
-	dest = fz_newpixmapwithrect(dev->model, bbox);
+	dest = fz_newpixmapwithrect(model, bbox);
 
 	fz_clearpixmap(mask, 0);
 	fz_clearpixmap(dest, 0);
 
-	dev->clipstack[dev->cliptop].scissor = dev->scissor;
-	dev->clipstack[dev->cliptop].mask = mask;
-	dev->clipstack[dev->cliptop].dest = dev->dest;
+	dev->stack[dev->top].scissor = dev->scissor;
+	dev->stack[dev->top].mask = mask;
+	dev->stack[dev->top].dest = dev->dest;
 	dev->scissor = bbox;
 	dev->dest = dest;
-	dev->cliptop++;
+	dev->top++;
 
 	if (!fz_isemptyrect(bbox))
 	{
@@ -550,6 +553,7 @@ static void
 fz_drawfillshade(void *user, fz_shade *shade, fz_matrix ctm)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	fz_pixmap *dest = dev->dest;
 	fz_rect bounds;
 	fz_bbox bbox;
@@ -569,7 +573,7 @@ fz_drawfillshade(void *user, fz_shade *shade, fz_matrix ctm)
 	if (fz_isemptyrect(bbox))
 		return;
 
-	if (!dev->model)
+	if (!model)
 	{
 		fz_warn("cannot render shading directly to an alpha mask");
 		return;
@@ -579,8 +583,8 @@ fz_drawfillshade(void *user, fz_shade *shade, fz_matrix ctm)
 	{
 		unsigned char *s;
 		int x, y, n, i;
-		fz_convertcolor(shade->cs, shade->background, dev->model, colorfv);
-		for (i = 0; i < dev->model->n; i++)
+		fz_convertcolor(shade->cs, shade->background, model, colorfv);
+		for (i = 0; i < model->n; i++)
 			colorbv[i] = colorfv[i] * 255;
 		colorbv[i] = 255;
 
@@ -655,13 +659,14 @@ static void
 fz_drawfillimage(void *user, fz_pixmap *image, fz_matrix ctm)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	fz_bbox bbox;
 	int dx, dy;
 	fz_pixmap *scaled = nil;
 	fz_pixmap *converted = nil;
 	fz_matrix invmat;
 
-	if (!dev->model)
+	if (!model)
 	{
 		fz_warn("cannot render image directly to an alpha mask");
 		return;
@@ -678,9 +683,9 @@ fz_drawfillimage(void *user, fz_pixmap *image, fz_matrix ctm)
 		image = scaled;
 	}
 
-	if (image->colorspace != dev->model)
+	if (image->colorspace != model)
 	{
-		converted = fz_newpixmap(dev->model, image->x, image->y, image->w, image->h);
+		converted = fz_newpixmap(model, image->x, image->y, image->w, image->h);
 		fz_convertpixmap(image, converted);
 		image = converted;
 	}
@@ -698,6 +703,7 @@ fz_drawfillimagemask(void *user, fz_pixmap *image, fz_matrix ctm,
 	fz_colorspace *colorspace, float *color, float alpha)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	unsigned char colorbv[FZ_MAXCOLORS + 1];
 	float colorfv[FZ_MAXCOLORS];
 	fz_bbox bbox;
@@ -719,8 +725,8 @@ fz_drawfillimagemask(void *user, fz_pixmap *image, fz_matrix ctm,
 
 	if (dev->dest->colorspace)
 	{
-		fz_convertcolor(colorspace, color, dev->model, colorfv);
-		for (i = 0; i < dev->model->n; i++)
+		fz_convertcolor(colorspace, color, model, colorfv);
+		for (i = 0; i < model->n; i++)
 			colorbv[i] = colorfv[i] * 255;
 		colorbv[i] = alpha * 255;
 		fz_scanconvert(dev->gel, dev->ael, 0, bbox, dev->dest, colorbv, image, &invmat);
@@ -738,15 +744,16 @@ static void
 fz_drawclipimagemask(void *user, fz_pixmap *image, fz_matrix ctm)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	fz_bbox bbox;
 	fz_pixmap *mask, *dest;
 	int dx, dy;
 	fz_pixmap *scaled = nil;
 	fz_matrix invmat;
 
-	if (dev->cliptop == MAXCLIP)
+	if (dev->top == STACKSIZE)
 	{
-		fz_warn("assert: too many clip masks on stack");
+		fz_warn("assert: too many buffers on stack");
 		return;
 	}
 
@@ -754,11 +761,11 @@ fz_drawclipimagemask(void *user, fz_pixmap *image, fz_matrix ctm)
 
 	if (fz_isemptyrect(bbox) || image->w == 0 || image->h == 0)
 	{
-		dev->clipstack[dev->cliptop].scissor = dev->scissor;
-		dev->clipstack[dev->cliptop].mask = nil;
-		dev->clipstack[dev->cliptop].dest = nil;
+		dev->stack[dev->top].scissor = dev->scissor;
+		dev->stack[dev->top].mask = nil;
+		dev->stack[dev->top].dest = nil;
 		dev->scissor = bbox;
-		dev->cliptop++;
+		dev->top++;
 		return;
 	}
 
@@ -769,19 +776,19 @@ fz_drawclipimagemask(void *user, fz_pixmap *image, fz_matrix ctm)
 	}
 
 	mask = fz_newpixmapwithrect(nil, bbox);
-	dest = fz_newpixmapwithrect(dev->model, bbox);
+	dest = fz_newpixmapwithrect(model, bbox);
 
 	fz_clearpixmap(mask, 0);
 	fz_clearpixmap(dest, 0);
 
 	fz_scanconvert(dev->gel, dev->ael, 0, bbox, mask, nil, image, &invmat);
 
-	dev->clipstack[dev->cliptop].scissor = dev->scissor;
-	dev->clipstack[dev->cliptop].mask = mask;
-	dev->clipstack[dev->cliptop].dest = dev->dest;
+	dev->stack[dev->top].scissor = dev->scissor;
+	dev->stack[dev->top].mask = mask;
+	dev->stack[dev->top].dest = dev->dest;
 	dev->scissor = bbox;
 	dev->dest = dest;
-	dev->cliptop++;
+	dev->top++;
 
 	if (scaled)
 		fz_droppixmap(scaled);
@@ -792,12 +799,12 @@ fz_drawpopclip(void *user)
 {
 	fz_drawdevice *dev = user;
 	fz_pixmap *mask, *dest;
-	if (dev->cliptop > 0)
+	if (dev->top > 0)
 	{
-		dev->cliptop--;
-		dev->scissor = dev->clipstack[dev->cliptop].scissor;
-		mask = dev->clipstack[dev->cliptop].mask;
-		dest = dev->clipstack[dev->cliptop].dest;
+		dev->top--;
+		dev->scissor = dev->stack[dev->top].scissor;
+		mask = dev->stack[dev->top].mask;
+		dest = dev->stack[dev->top].dest;
 		if (mask && dest)
 		{
 			fz_pixmap *scratch = dev->dest;
@@ -812,38 +819,106 @@ fz_drawpopclip(void *user)
 static void
 fz_drawbeginmask(void *user, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *colorfv)
 {
+	fz_drawdevice *dev = user;
+	fz_pixmap *dest;
+	fz_bbox bbox;
+
 	fz_warn("fz_drawbeginmask");
+
+	if (dev->top == STACKSIZE)
+	{
+		fz_warn("assert: too many buffers on stack");
+		return;
+	}
+
+	bbox = fz_roundrect(rect);
+	bbox = fz_intersectbbox(bbox, dev->scissor);
+	dest = fz_newpixmapwithrect(pdf_devicegray, bbox);
+
+	if (luminosity)
+		fz_clearpixmap(dest, 255);
+	else
+		fz_clearpixmap(dest, 0);
+	
+	dev->stack[dev->top].scissor = dev->scissor;
+	dev->stack[dev->top].dest = dev->dest;
+	dev->stack[dev->top].luminosity = luminosity;
+	dev->top++;
+
+	dev->scissor = bbox;
+	dev->dest = dest;
 }
 
 static void
 fz_drawendmask(void *user)
 {
+	fz_drawdevice *dev = user;
+	fz_pixmap *mask = dev->dest;
+	fz_pixmap *temp, *dest;
+	fz_bbox bbox;
+	int luminosity;
+
 	fz_warn("fz_drawendmask");
+
+	if (dev->top == STACKSIZE)
+	{
+		fz_warn("assert: too many buffers on stack");
+		return;
+	}
+
+	if (dev->top > 0)
+	{
+		/* pop soft mask buffer */
+		dev->top--;
+		luminosity = dev->stack[dev->top].luminosity;
+		dev->scissor = dev->stack[dev->top].scissor;
+		dev->dest = dev->stack[dev->top].dest;
+
+		/* convert to alpha mask */
+		temp = fz_alphafromgray(mask, luminosity);
+fz_writepng(mask, "softmask-1.png", 1);
+fz_writepng(temp, "softmask-2.png", 1);
+		fz_droppixmap(mask);
+
+		/* create new dest scratch buffer */
+		bbox = fz_boundpixmap(temp);
+		dest = fz_newpixmapwithrect(dev->dest->colorspace, bbox);
+		fz_clearpixmap(dest, 0);
+
+		/* push soft mask as clip mask */
+		dev->stack[dev->top].scissor = dev->scissor;
+		dev->stack[dev->top].mask = temp;
+		dev->stack[dev->top].dest = dev->dest;
+		dev->scissor = bbox;
+		dev->dest = dest;
+		dev->top++;
+	}
 }
 
 static void
 fz_drawbegingroup(void *user, fz_rect rect, int isolated, int knockout, fz_blendmode blendmode)
 {
 	fz_drawdevice *dev = user;
+	fz_colorspace *model = dev->dest->colorspace;
 	fz_bbox bbox;
 	fz_pixmap *dest;
 
-	if (dev->cliptop == MAXCLIP)
+	if (dev->top == STACKSIZE)
 	{
-		fz_warn("assert: too many clip masks on stack");
+		fz_warn("assert: too many buffers on stack");
 		return;
 	}
 
 	bbox = fz_roundrect(rect);
 	bbox = fz_intersectbbox(bbox, dev->scissor);
-	dest = fz_newpixmapwithrect(dev->model, bbox);
+	dest = fz_newpixmapwithrect(model, bbox);
 
 	fz_clearpixmap(dest, 0);
 
-	dev->groupstack[dev->grouptop].blendmode = blendmode;
-	dev->groupstack[dev->grouptop].scissor = dev->scissor;
-	dev->groupstack[dev->grouptop].dest = dev->dest;
-	dev->grouptop++;
+	dev->stack[dev->top].blendmode = blendmode;
+	dev->stack[dev->top].scissor = dev->scissor;
+	dev->stack[dev->top].dest = dev->dest;
+	dev->top++;
 
 	dev->scissor = bbox;
 	dev->dest = dest;
@@ -856,24 +931,22 @@ fz_drawendgroup(void *user)
 	fz_pixmap *group = dev->dest;
 	fz_blendmode blendmode;
 
-	if (dev->grouptop > 0)
+	if (dev->top > 0)
 	{
-		dev->grouptop--;
-		blendmode = dev->groupstack[dev->grouptop].blendmode;
-		dev->dest = dev->groupstack[dev->grouptop].dest;
-		dev->scissor = dev->groupstack[dev->grouptop].scissor;
+		dev->top--;
+		blendmode = dev->stack[dev->top].blendmode;
+		dev->dest = dev->stack[dev->top].dest;
+		dev->scissor = dev->stack[dev->top].scissor;
 		fz_blendpixmaps(group, dev->dest, blendmode);
+		fz_droppixmap(group);
 	}
-
-	fz_droppixmap(group);
 }
 
 static void
 fz_drawfreeuser(void *user)
 {
 	fz_drawdevice *dev = user;
-	if (dev->model)
-		fz_dropcolorspace(dev->model);
+	/* TODO: pop and free the stacks */
 	fz_freegel(dev->gel);
 	fz_freeael(dev->ael);
 	fz_free(dev);
@@ -884,16 +957,11 @@ fz_newdrawdevice(fz_glyphcache *cache, fz_pixmap *dest)
 {
 	fz_device *dev;
 	fz_drawdevice *ddev = fz_malloc(sizeof(fz_drawdevice));
-	if (dest->colorspace)
-		ddev->model = fz_keepcolorspace(dest->colorspace);
-	else
-		ddev->model = nil;
 	ddev->cache = cache;
 	ddev->gel = fz_newgel();
 	ddev->ael = fz_newael();
 	ddev->dest = dest;
-	ddev->cliptop = 0;
-	ddev->grouptop = 0;
+	ddev->top = 0;
 
 	ddev->scissor.x0 = dest->x;
 	ddev->scissor.y0 = dest->y;
diff --git a/fitz/dev_trace.c b/fitz/dev_trace.c
index 6941ec4..84aaff5 100644
--- a/fitz/dev_trace.c
+++ b/fitz/dev_trace.c
@@ -230,10 +230,11 @@ fz_tracepopclip(void *user)
 static void
 fz_tracebeginmask(void *user, fz_rect bbox, int luminosity, fz_colorspace *colorspace, float *color)
 {
+	printf("<gsave>\n");
 	printf("<mask bbox=\"%g %g %g %g\" s=\"%s\" ",
 		bbox.x0, bbox.y0, bbox.x1, bbox.y1,
 		luminosity ? "luminosity" : "alpha");
-	fz_tracecolor(colorspace, color, 1);
+//	fz_tracecolor(colorspace, color, 1);
 	printf(">\n");
 }
 
@@ -246,9 +247,9 @@ fz_traceendmask(void *user)
 static void
 fz_tracebegingroup(void *user, fz_rect bbox, int isolated, int knockout, fz_blendmode blendmode)
 {
-	printf("<group bbox=\"%g %g %g %g\" isolated=\"%d\" knockout=\"%d\" blendmode=\"%d\">\n",
+	printf("<group bbox=\"%g %g %g %g\" isolated=\"%d\" knockout=\"%d\" blendmode=\"%s\">\n",
 		bbox.x0, bbox.y0, bbox.x1, bbox.y1,
-		isolated, knockout, blendmode);
+		isolated, knockout, fz_blendnames[blendmode]);
 }
 
 static void
diff --git a/fitz/fitz.h b/fitz/fitz.h
index 3d7c5cb..215b943 100644
--- a/fitz/fitz.h
+++ b/fitz/fitz.h
@@ -676,6 +676,8 @@ typedef enum fz_blendmode_e
 	FZ_BLUMINOSITY,
 } fz_blendmode;
 
+extern const char *fz_blendnames[];
+
 /*
  * Pixmaps have n components per pixel. the last is always alpha.
  * premultiplied alpha when rendering, but non-premultiplied for colorspace
@@ -695,10 +697,12 @@ struct fz_pixmap_s
 
 fz_pixmap * fz_newpixmapwithrect(fz_colorspace *, fz_bbox bbox);
 fz_pixmap * fz_newpixmap(fz_colorspace *, int x, int y, int w, int h);
-fz_pixmap *fz_keeppixmap(fz_pixmap *map);
-void fz_droppixmap(fz_pixmap *map);
-void fz_clearpixmap(fz_pixmap *map, unsigned char value);
+fz_pixmap *fz_keeppixmap(fz_pixmap *pix);
+void fz_droppixmap(fz_pixmap *pix);
+void fz_clearpixmap(fz_pixmap *pix, int value);
 void fz_gammapixmap(fz_pixmap *pix, float gamma);
+fz_pixmap *fz_alphafromgray(fz_pixmap *gray, int luminosity);
+fz_bbox fz_boundpixmap(fz_pixmap *pix);
 
 fz_pixmap * fz_scalepixmap(fz_pixmap *src, int xdenom, int ydenom);
 
diff --git a/fitz/res_font.c b/fitz/res_font.c
index 368c491..8dc0690 100644
--- a/fitz/res_font.c
+++ b/fitz/res_font.c
@@ -472,7 +472,7 @@ fz_rendert3glyph(fz_font *font, int gid, fz_matrix trm)
 	fz_freedevice(dev);
 
 	glyph = fz_newpixmap(nil, bbox.x0-1, bbox.y0-1, bbox.x1 - bbox.x0 + 1, bbox.y1 - bbox.y0 + 1);
-	fz_clearpixmap(glyph, 0x00);
+	fz_clearpixmap(glyph, 0);
 
 	cache = fz_newglyphcache();
 	dev = fz_newdrawdevice(cache, glyph);
diff --git a/fitz/res_pixmap.c b/fitz/res_pixmap.c
index e1184ff..4599fb0 100644
--- a/fitz/res_pixmap.c
+++ b/fitz/res_pixmap.c
@@ -51,11 +51,22 @@ fz_droppixmap(fz_pixmap *pix)
 }
 
 void
-fz_clearpixmap(fz_pixmap *pix, unsigned char value)
+fz_clearpixmap(fz_pixmap *pix, int value)
 {
 	memset(pix->samples, value, pix->w * pix->h * pix->n);
 }
 
+fz_bbox
+fz_boundpixmap(fz_pixmap *pix)
+{
+	fz_bbox bbox;
+	bbox.x0 = pix->x;
+	bbox.y0 = pix->y;
+	bbox.x1 = pix->x + pix->w;
+	bbox.y1 = pix->y + pix->h;
+	return bbox;
+}
+
 void
 fz_gammapixmap(fz_pixmap *pix, float gamma)
 {
@@ -72,6 +83,31 @@ fz_gammapixmap(fz_pixmap *pix, float gamma)
 	}
 }
 
+fz_pixmap *
+fz_alphafromgray(fz_pixmap *gray, int luminosity)
+{
+	fz_pixmap *alpha;
+	unsigned char *sp, *dp;
+	int len;
+
+	assert(gray->n == 2);
+
+	alpha = fz_newpixmap(nil, gray->x, gray->y, gray->w, gray->h);
+	dp = alpha->samples;
+	sp = gray->samples;
+	if (!luminosity)
+		sp ++;
+
+	len = gray->w * gray->h;
+	while (len--)
+	{
+		*dp++ = sp[0];
+		sp += 2;
+	}
+
+	return alpha;
+}
+
 /*
  * Write pixmap to PNM file (without alpha channel)
  */
diff --git a/mupdf/mupdf.h b/mupdf/mupdf.h
index faf7740..8c9acdd 100644
--- a/mupdf/mupdf.h
+++ b/mupdf/mupdf.h
@@ -570,7 +570,6 @@ struct pdf_gstate_s
 	/* materials */
 	pdf_material stroke;
 	pdf_material fill;
-	fz_blendmode blendmode;
 
 	/* text state */
 	float charspace;
@@ -581,6 +580,12 @@ struct pdf_gstate_s
 	float size;
 	int render;
 	float rise;
+
+	/* transparency */
+	fz_blendmode blendmode;
+	pdf_xobject *softmask;
+	fz_matrix softmaskctm;
+	int luminosity;
 };
 
 struct pdf_csi_s
@@ -627,6 +632,7 @@ void pdf_showshade(pdf_csi*, fz_shade *shd);
 void pdf_gsave(pdf_csi *csi);
 void pdf_grestore(pdf_csi *csi);
 fz_error pdf_runcsibuffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents);
+fz_error pdf_runxobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj);
 fz_error pdf_runcontents(pdf_xref *xref, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm);
 fz_error pdf_runpage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm);
 
diff --git a/mupdf/pdf_build.c b/mupdf/pdf_build.c
index fe6cc30..ff57d0f 100644
--- a/mupdf/pdf_build.c
+++ b/mupdf/pdf_build.c
@@ -31,8 +31,6 @@ pdf_initgstate(pdf_gstate *gs, fz_matrix ctm)
 	gs->fill.parentalpha = 1;
 	gs->fill.alpha = 1;
 
-	gs->blendmode = FZ_BNORMAL;
-
 	gs->charspace = 0;
 	gs->wordspace = 0;
 	gs->scale = 1;
@@ -41,6 +39,11 @@ pdf_initgstate(pdf_gstate *gs, fz_matrix ctm)
 	gs->size = -1;
 	gs->render = 0;
 	gs->rise = 0;
+
+	gs->blendmode = FZ_BNORMAL;
+	gs->softmask = nil;
+	gs->softmaskctm = fz_identity;
+	gs->luminosity = 0;
 }
 
 void
@@ -221,12 +224,12 @@ void
 pdf_showshade(pdf_csi *csi, fz_shade *shd)
 {
 	pdf_gstate *gstate = csi->gstate + csi->gtop;
+	fz_rect bbox;
+
+	bbox = fz_boundshade(shd, gstate->ctm);
 
 	if (gstate->blendmode != FZ_BNORMAL)
-	{
-		fz_rect bbox = fz_boundshade(shd, gstate->ctm);
 		csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode);
-	}
 
 	csi->dev->fillshade(csi->dev->user, shd, gstate->ctm);
 
@@ -239,6 +242,12 @@ pdf_showimage(pdf_csi *csi, pdf_image *image)
 {
 	pdf_gstate *gstate = csi->gstate + csi->gtop;
 	fz_pixmap *tile, *mask;
+	fz_rect bbox;
+
+	bbox = fz_transformrect(gstate->ctm, fz_unitrect);
+
+	if (gstate->blendmode != FZ_BNORMAL)
+		csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode);
 
 	if (image->mask)
 	{
@@ -247,17 +256,10 @@ pdf_showimage(pdf_csi *csi, pdf_image *image)
 		fz_droppixmap(mask);
 	}
 
-	if (gstate->blendmode != FZ_BNORMAL)
-	{
-		fz_rect bbox = fz_transformrect(gstate->ctm, fz_unitrect);
-		csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode);
-	}
-
 	tile = pdf_loadtile(image);
 
 	if (image->imagemask)
 	{
-		fz_rect bbox;
 
 		switch (gstate->fill.kind)
 		{
@@ -270,7 +272,6 @@ pdf_showimage(pdf_csi *csi, pdf_image *image)
 		case PDF_MPATTERN:
 			if (gstate->fill.pattern)
 			{
-				bbox = fz_transformrect(gstate->ctm, fz_unitrect);
 				csi->dev->clipimagemask(csi->dev->user, tile, gstate->ctm);
 				pdf_showpattern(csi, gstate->fill.pattern, bbox, PDF_MFILL);
 				csi->dev->popclip(csi->dev->user);
@@ -291,12 +292,12 @@ pdf_showimage(pdf_csi *csi, pdf_image *image)
 		csi->dev->fillimage(csi->dev->user, tile, gstate->ctm);
 	}
 
-	if (gstate->blendmode != FZ_BNORMAL)
-		csi->dev->endgroup(csi->dev->user);
-
 	if (image->mask)
 		csi->dev->popclip(csi->dev->user);
 
+	if (gstate->blendmode != FZ_BNORMAL)
+		csi->dev->endgroup(csi->dev->user);
+
 	fz_droppixmap(tile);
 }
 
@@ -304,8 +305,8 @@ void
 pdf_showpath(pdf_csi *csi, int doclose, int dofill, int dostroke, int evenodd)
 {
 	pdf_gstate *gstate = csi->gstate + csi->gtop;
-	fz_rect bbox;
 	fz_path *path;
+	fz_rect bbox;
 
 	path = csi->path;
 	csi->path = fz_newpath();
@@ -320,15 +321,13 @@ pdf_showpath(pdf_csi *csi, int doclose, int dofill, int dostroke, int evenodd)
 		csi->clip = 0;
 	}
 
+	if (dostroke)
+		bbox = fz_boundpath(path, &gstate->strokestate, gstate->ctm);
+	else
+		bbox = fz_boundpath(path, nil, gstate->ctm);
+
 	if (gstate->blendmode != FZ_BNORMAL)
-	{
-		fz_rect bbox;
-		if (dostroke)
-			bbox = fz_boundpath(path, &gstate->strokestate, gstate->ctm);
-		else
-			bbox = fz_boundpath(path, nil, gstate->ctm);
 		csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode);
-	}
 
 	if (dofill)
 	{
@@ -343,7 +342,6 @@ pdf_showpath(pdf_csi *csi, int doclose, int dofill, int dostroke, int evenodd)
 		case PDF_MPATTERN:
 			if (gstate->fill.pattern)
 			{
-				bbox = fz_boundpath(path, nil, gstate->ctm);
 				csi->dev->clippath(csi->dev->user, path, evenodd, gstate->ctm);
 				pdf_showpattern(csi, gstate->fill.pattern, bbox, PDF_MFILL);
 				csi->dev->popclip(csi->dev->user);
@@ -373,7 +371,6 @@ pdf_showpath(pdf_csi *csi, int doclose, int dofill, int dostroke, int evenodd)
 		case PDF_MPATTERN:
 			if (gstate->stroke.pattern)
 			{
-				bbox = fz_boundpath(path, &gstate->strokestate, gstate->ctm);
 				csi->dev->clipstrokepath(csi->dev->user, path, &gstate->strokestate, gstate->ctm);
 				pdf_showpattern(csi, gstate->stroke.pattern, bbox, PDF_MFILL);
 				csi->dev->popclip(csi->dev->user);
@@ -416,39 +413,24 @@ pdf_flushtext(pdf_csi *csi)
 	text = csi->text;
 	csi->text = nil;
 
+	dofill = dostroke = doclip = doinvisible = 0;
 	switch (csi->textmode)
 	{
-	case 0:
-		dofill = 1;
-		break;
-	case 1:
-		dostroke = 1;
-		break;
-	case 2:
-		dofill = 1;
-		dostroke = 1;
-		break;
-	case 3:
-		doinvisible = 1;
-		break;
-	case 4:
-		dofill = 1;
-		doclip = 1;
-		break;
-	case 5:
-		dostroke = 1;
-		doclip = 1;
-		break;
-	case 6:
-		dofill = 1;
-		dostroke = 1;
-		doclip = 1;
-		break;
-	case 7:
-		doclip = 1;
-		break;
+	case 0: dofill = 1; break;
+	case 1: dostroke = 1; break;
+	case 2: dofill = dostroke = 1; break;
+	case 3: doinvisible = 1; break;
+	case 4: dofill = doclip = 1; break;
+	case 5: dostroke = doclip = 1; break;
+	case 6: dofill = dostroke = doclip = 1; break;
+	case 7: doclip = 1; break;
 	}
 
+	bbox = fz_boundtext(text, gstate->ctm);
+
+	if (gstate->blendmode != FZ_BNORMAL)
+		csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode);
+
 	if (doinvisible)
 		csi->dev->ignoretext(csi->dev->user, text, gstate->ctm);
 
@@ -459,12 +441,6 @@ pdf_flushtext(pdf_csi *csi)
 		csi->accumulate = 2;
 	}
 
-	if (gstate->blendmode != FZ_BNORMAL)
-	{
-		fz_rect bbox = fz_boundtext(text, gstate->ctm);
-		csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode);
-	}
-
 	if (dofill)
 	{
 		switch (gstate->fill.kind)
@@ -478,7 +454,6 @@ pdf_flushtext(pdf_csi *csi)
 		case PDF_MPATTERN:
 			if (gstate->fill.pattern)
 			{
-				bbox = fz_boundtext(text, gstate->ctm);
 				csi->dev->cliptext(csi->dev->user, text, gstate->ctm, 0);
 				pdf_showpattern(csi, gstate->fill.pattern, bbox, PDF_MFILL);
 				csi->dev->popclip(csi->dev->user);
@@ -508,7 +483,6 @@ pdf_flushtext(pdf_csi *csi)
 		case PDF_MPATTERN:
 			if (gstate->stroke.pattern)
 			{
-				bbox = fz_boundtext(text, gstate->ctm);
 				csi->dev->clipstroketext(csi->dev->user, text, &gstate->strokestate, gstate->ctm);
 				pdf_showpattern(csi, gstate->stroke.pattern, bbox, PDF_MFILL);
 				csi->dev->popclip(csi->dev->user);
diff --git a/mupdf/pdf_interpret.c b/mupdf/pdf_interpret.c
index 710b52d..c8a2d0f 100644
--- a/mupdf/pdf_interpret.c
+++ b/mupdf/pdf_interpret.c
@@ -83,6 +83,8 @@ pdf_gsave(pdf_csi *csi)
 	pdf_keepmaterial(&gs->fill);
 	if (gs->font)
 		pdf_keepfont(gs->font);
+	if (gs->softmask)
+		pdf_keepxobject(gs->softmask);
 }
 
 void
@@ -101,6 +103,8 @@ pdf_grestore(pdf_csi *csi)
 	pdf_dropmaterial(&gs->fill);
 	if (gs->font)
 		pdf_dropfont(gs->font);
+	if (gs->softmask)
+		pdf_dropxobject(gs->softmask);
 
 	csi->gtop --;
 
@@ -140,13 +144,14 @@ pdf_freecsi(pdf_csi *csi)
  * Push gstate, set transform, clip, run, pop gstate.
  */
 
-static fz_error
+fz_error
 pdf_runxobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj)
 {
 	fz_error error;
 	pdf_gstate *gstate;
 	fz_matrix oldtopctm;
 	int oldtop;
+	pdf_xobject *softmask = nil;
 
 	pdf_gsave(csi);
 
@@ -162,6 +167,21 @@ pdf_runxobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj)
 	/* reset alpha to 1.0 when starting a new Transparency group */
 	if (xobj->transparency)
 	{
+		if (gstate->softmask)
+		{
+			softmask = gstate->softmask;
+			gstate->softmask = nil;
+
+			fz_rect bbox = fz_transformrect(gstate->ctm, xobj->bbox);
+			fz_matrix oldctm = gstate->ctm;
+			csi->dev->beginmask(csi->dev->user, bbox, gstate->luminosity, nil, nil);
+			pdf_runxobject(csi, nil, softmask);
+			csi->dev->endmask(csi->dev->user);
+			gstate->ctm = oldctm;
+
+			pdf_dropxobject(softmask);
+		}
+
 		csi->dev->begingroup(csi->dev->user,
 			fz_transformrect(gstate->ctm, xobj->bbox),
 			xobj->isolated, xobj->knockout, gstate->blendmode);
@@ -201,8 +221,13 @@ pdf_runxobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj)
 	pdf_grestore(csi);
 
 	if (xobj->transparency)
+	{
 		csi->dev->endgroup(csi->dev->user);
 
+		if (softmask)
+			csi->dev->popclip(csi->dev->user);
+	}
+
 	return fz_okay;
 }
 
@@ -311,38 +336,13 @@ pdf_runextgstate(pdf_csi *csi, pdf_gstate *gstate, fz_obj *rdb, fz_obj *extgstat
 
 		else if (!strcmp(s, "BM"))
 		{
-			static const struct { const char *name; fz_blendmode mode; } bm[] = {
-				{ "Normal", FZ_BNORMAL },
-				{ "Multiply", FZ_BMULTIPLY },
-				{ "Screen", FZ_BSCREEN },
-				{ "Overlay", FZ_BOVERLAY },
-				{ "SoftLight", FZ_BSOFTLIGHT },
-				{ "HardLight", FZ_BHARDLIGHT },
-				{ "ColorDodge", FZ_BCOLORDODGE },
-				{ "ColorBurn", FZ_BCOLORBURN },
-				{ "Darken", FZ_BDARKEN },
-				{ "Lighten", FZ_BLIGHTEN },
-				{ "Difference", FZ_BDIFFERENCE },
-				{ "Exclusion", FZ_BEXCLUSION },
-				{ "Hue", FZ_BHUE },
-				{ "Saturation", FZ_BSATURATION },
-				{ "Color", FZ_BCOLOR },
-				{ "Luminosity", FZ_BLUMINOSITY }
-			};
-
-			char *n = fz_toname(val);
 			if (!fz_isname(val))
 				return fz_throw("malformed BM");
 
 			gstate->blendmode = FZ_BNORMAL;
-			for (k = 0; k < nelem(bm); k++)
-			{
-				if (!strcmp(bm[k].name, n))
-				{
-					gstate->blendmode = bm[k].mode;
-					break;
-				}
-			}
+			for (k = 0; fz_blendnames[k]; k++)
+				if (!strcmp(fz_blendnames[k], fz_toname(val)))
+					gstate->blendmode = k;
 		}
 
 		else if (!strcmp(s, "SMask"))
@@ -350,30 +350,30 @@ pdf_runextgstate(pdf_csi *csi, pdf_gstate *gstate, fz_obj *rdb, fz_obj *extgstat
 			if (fz_isdict(val))
 			{
 				fz_error error;
-				fz_obj *g;
-				fz_obj *subtype;
 				pdf_xobject *xobj;
-				pdf_image *img;
+				fz_obj *group, *luminosity;
 
-				fz_warn("ignoring soft mask");
-#if 0
-				g = fz_dictgets(val, "G");
-				subtype = fz_dictgets(g, "Subtype");
+				fz_warn("not ignoring soft mask");
 
-				if (!strcmp(fz_toname(subtype), "Form"))
+				if (gstate->softmask)
 				{
-				error = pdf_loadxobject(&xobj, csi->xref, g);
-				if (error)
-				return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_tonum(val), fz_togen(val));
+					pdf_dropxobject(gstate->softmask);
+					gstate->softmask = nil;
 				}
 
-				else if (!strcmp(fz_toname(subtype), "Image"))
-				{
-				error = pdf_loadimage(&img, csi->xref, g);
+				group = fz_dictgets(val, "G");
+				error = pdf_loadxobject(&xobj, csi->xref, group);
 				if (error)
-				return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_tonum(val), fz_togen(val));
-				}
-#endif
+					return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_tonum(val), fz_togen(val));
+
+				gstate->softmaskctm = fz_concat(xobj->matrix, gstate->ctm);
+				gstate->softmask = xobj;
+
+				luminosity = fz_dictgets(val, "S");
+				if (fz_isname(luminosity) && !strcmp(fz_toname(luminosity), "Luminosity"))
+					gstate->luminosity = 1;
+				else
+					gstate->luminosity = 0;
 			}
 		}
 

--
git/hooks/post-receive


More information about the gs-commits mailing list