Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Image Masking: Now with Vector Paths

0.00/5 (No votes)
2 Apr 2017 1  
Masked Images and Specifying Soft Masks

Introduction

I’m going to discuss the other ways that you can mask an image before revealing the solution and discussing it, because there are lots of ways to mask an image in PDF, so it’s a bit more interesting a topic than you might think. If you search the PDF Specification (as I did just now) you will find that there are essentially two sections that discuss Masking: Section 8.9.6 Masked Images and Section 11.6.5 Specifying Soft Masks, and neither discusses masking with vector paths.

Background

Soft Masks are part of the chapter on transparency and come in two flavors: Images and Dictionaries. If you are familiar with RGBA images, an image soft mask is essentially the Alpha (transparency) channel separated from the RGB image and made to stand-alone within the parent image XObject. Which is essentially what DLE does with PNG files that have RGBA images. SoftMask dictionaries, on the other hand, are part of the extended graphic state, and while they contain a Transparency Group Form XObject, which could contain a vector path, it’s not the mechanism that comes to mind as the ideal way to mask an image with a vector path.

Masked images come in three flavors; Stencil Masking, Explicit Masking, and Colour Key Masking. Explicit Masking is like an image soft mask except that the alpha channel pixel can only be all on or all off, so there’s no degree of transparency. Colour key masking is a bit like how transparency works in GIF files, where one of the indexed colors is designated as the transparency color, but with colour key masking you can specify a range for each color component of the image; if all the components values fall within those ranges, then that pixel is masked (or not depending on the Decode parameter, but that’s the idea). Stencil Masking is like Explicit Masking, except that you have thrown away the primary image and are masking whatever is already in the background.

Using the code

None of these variants allow for vector paths, so how do you mask with a vector path? Masking is the right concept, but the wrong term in this case because to mask an image with a vector path, you simply apply a clipping path to the image. Or at least, that was the theory, and I needed some code to prove it. I decided to clip this image: with a 17 point star created with the following code:

            var center = new Point(pageRect.Left+pageRect.Width/2,pageRect.Bottom+pageRect.Height/2);

            var smallDim = Math.Min(img2.BoundingBox.Width,img2.BoundingBox.Height);

            var cPath = createStar(center,smallDim/2,smallDim/3, 17);

static Path createStar(Point Center, double r1, double r2, int rays)

{

    var starPath = new Path();

    Point start = new Point(Center.H+ r1 *Math.Cos(0), Center.V+r1*Math.Sin(0));

    starPath.MoveTo(start);

    double angle =2*Math.PI/rays;

    var halfAngle = angle /2.0;

    for(int i=0; i < rays; i++)

    {

        var theta1 = (i * angle) + halfAngle;

        var theta2 = theta1 + halfAngle;

        var Point1 = new Point(Center.H + r2 * Math.Cos(theta1), Center.V + r2 * Math.Sin(theta1));

        var Point2 = new Point(Center.H + r1 * Math.Cos(theta2), Center.V + r1 * Math.Sin(theta2));

        starPath.AddLine(Point1);

        starPath.AddLine(Point2);

    }

    starPath.ClosePath();



    return starPath;

}

The result was:

However, I was then challenged by my neighbor at the table to turn the clip inside out, or effectively Outside In(!).

// Challenge #2: use the inverse of the Path as the mask for an image

             var page3 = doc.CreatePage(Document.LastPage, pageRect);

             var img3 = newimage.Clone();



             // Solved by surrounding the Path with a Rect around the image.

             var clipPath2 = new Path();

             clipPath2.AddRect(new Point(72,72), img3.BoundingBox.Width, img3.BoundingBox.Height);

             AppendStar(ref clipPath2, center, smallDim / 2, smallDim / 3, 17);

             Clip clip2 = new Clip();

             clip2.AddElement(clipPath2);

             img3.Clip = clip2;

             page3.Content.AddElement(img3);

             page3.UpdateContent();

Appending the star to the exist clip path, which contains a rectangle around the entire image, required a slight alteration to how I created the star:

static void AppendStar(ref Path existingPath, Point Center, double r1, double r2, int rays)

        {

            

            Point start = new Point(Center.H + r1 * Math.Cos(0), Center.V + r1 * Math.Sin(0));

            existingPath.MoveTo(start);

            double angle = 2 * Math.PI / rays;

            var halfAngle = angle / 2.0;

            for (int i = 0; i < rays; i++)

            {

                var theta1 = (i * angle) + halfAngle;

                var theta2 = theta1 + halfAngle;

                var Point1 = new Point(Center.H + r2 * Math.Cos(theta1), Center.V + r2 * Math.Sin(theta1));

                var Point2 = new Point(Center.H + r1 * Math.Cos(theta2), Center.V + r1 * Math.Sin(theta2));

                existingPath.AddLine(Point1);

                existingPath.AddLine(Point2);

            }

            existingPath.ClosePath();

        }

And this time the result was:

Mission accomplished: An image masked with a vector path.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here