Understanding Image Processing Approaches in Graphics Mill

Graphics Mill provides two approaches for raster image processing - bitmaps and pipelines. For processing vector graphics, there are graphics containers. Let us consider them on a common imaging task - loading an image from a file, resizing it, and saving it to another file. All the examples in this topic implement the same functionality.

Bitmap

Using bitmaps is a straightforward and conventional image processing approach. It provides the best fit for relatively small images because the bigger image you load, the more memory your program occupies. Additionally, this approach is less suitable for complex tasks, due to the necessity of handling temporary results manually.

This approach implies using the Bitmap class, which encapsulates a raster image and stores a bitmap as well as other related data. To load Bitmap, you can use any of its constructors, and to save it, use Save methods. Image transformation operations in this approach can be performed in two ways: in-place and out-of-place. The main difference is that in-place operations change the source bitmap, while out-of-place operations create a new one. All in-place transformations are realized through the TransformsProvider class and can be implemented via the Bitmap.Transforms property. To implement an out-of-place operation, you should utilize the related class from the Aurigma.GraphicsMill.Transforms namespace, for example, the Transforms.Resize class.

The following code samples demonstrate both in-place and out-of-place transformations.

C#
using (var bitmap = new Aurigma.GraphicsMill.Bitmap(@"Images\in.jpg"))
{
    bitmap.Transforms.Resize(100, 0);
    bitmap.Save(@"Images\Output\out.jpg");
}
C#
using (var bitmap = new Aurigma.GraphicsMill.Bitmap(@"Images\in.jpg"))
using (var resize = new Aurigma.GraphicsMill.Transforms.Resize(100, 0))
{
    using (var result = resize.Apply(bitmap))
    {
        result.Save(@"Images\Output\out.jpg");
    }
}

To reduce coding, you can forego creating a Resize instance and use static Apply methods.

C#
using (var bitmap = new Aurigma.GraphicsMill.Bitmap(@"Images\in.jpg"))
{
    using (var result = Aurigma.GraphicsMill.Transforms.Resize.Apply(bitmap, 100, 0))
    {
        result.Save(@"Images\Output\out.jpg");
    }
}

Pipeline

In distinction from a bitmap, pipelines solve image processing tasks without loading a whole image into memory. Additionally, the pipeline has built-in branching support and the ability to dump temporary bitmap data. Therefore, this approach is preferable for large images and complex imaging tasks.

The essential class of this approach is Pipeline, which represents an ordered list of PipelineElements. Each pipeline element performs one of the following imaging tasks - reading/writing, transforming, filtering, and drawing on the image surface. Moreover, elements can pass their output to several pipelines via the PipelineElement.Receivers property; this behavior is what is meant by the term "branching". This functionality is useful when you want to process a single image's data in different ways, for instance, to save an image in different file formats or color spaces.

To load and save images within a pipeline, you can use ImageReader and ImageWriter classes as well as their descendants. Pipelines support out-of-place image operations only, so to transform an image, you should create and configure the desired Transform descendant and add it to the Pipeline. When the pipeline is ready, just run it by calling the Run() method.

C#
using (var reader = new Aurigma.GraphicsMill.Codecs.JpegReader(@"Images\in.jpg"))
using (var resize = new Aurigma.GraphicsMill.Transforms.Resize(100, 0))
using (var writer = new Aurigma.GraphicsMill.Codecs.JpegWriter(@"Images\Output\out.jpg"))
{
    Aurigma.GraphicsMill.Pipeline.Run(reader + resize + writer);
}

To make your code shorter and clearer, you can pass file names to a pipeline instead of readers and writers.

C#
Aurigma.GraphicsMill.Pipeline.Run(@"Images\in.jpg" + new Aurigma.GraphicsMill.Transforms.Resize(100, 0) + @"Images\out.jpg");

Implementing branched pipelines is quite difficult, but it allows you to solve a lot of complex and non-trivial tasks. A detailed example of how to produce multiple thumbnails of different sizes without overhead is provided in the Processing Large Images Using Pipelines topic.

GraphicsContainer

The GraphicsContainer class provides an easy way to manipulate vector graphics. It presents a storage that can include vector primitives, bitmaps, texts, and other graphics containers. You can draw such containers on bitmaps or combine them with each other without rasterization. When a graphics container is disposed of, all elements that it contains are automatically disposed as well.

To demonstrate how you can work with such containers, let us read a PDF file with vector graphics and resize it in the following ways:

Note

If you want only to rasterize vector graphics, you can use the PdfFrame.GetBitmap() method.

Using Bitmaps

So, to read vector graphics from a file, you can use the PdfReader or SvgReader classes, depending on the file types. You can get document pages from the PdfFrame collection, get vector objects through PdfFrame.GetContent(), and then rasterize them into a bitmap through the Graphics.DrawContainer() method.

C#
float dpi = 150f;

using (var reader = new Aurigma.GraphicsMill.Codecs.PdfReader(@"Images\in.pdf", dpi, dpi))
// Get the first page of the PDF file, for example:
using (var frame = reader.Frames[0])
using (var container = frame.GetContent())
using (var bitmap = new Aurigma.GraphicsMill.Bitmap(container.Width, container.Height,
        Aurigma.GraphicsMill.PixelFormat.Format24bppRgb, Aurigma.GraphicsMill.RgbColor.White)
        { DpiX = dpi, DpiY = dpi })
using (var graphics = bitmap.GetAdvancedGraphics())
{
    graphics.DrawContainer(container, 0, 0);

    bitmap.Transforms.Resize(100, 0);

    bitmap.Save(@"Images\Output\out.png");
}

Using Pipelines

Alternatively, you can transform rasterized graphics through pipelines. Here, the ImageGenerator class prepares a bitmap for pipelines.

C#
float dpi = 150f;

using (var reader = new Aurigma.GraphicsMill.Codecs.PdfReader(@"Images\in.pdf", dpi, dpi))
using (var frame = reader.Frames[0])
using (var container = frame.GetContent())
using (var imageGenerator = new Aurigma.GraphicsMill.ImageGenerator(container,
        Aurigma.GraphicsMill.PixelFormat.Format24bppRgb, Aurigma.GraphicsMill.RgbColor.White))
using (var resize = new Aurigma.GraphicsMill.Transforms.Resize(100, 0))
using (var writer = Aurigma.GraphicsMill.Codecs.ImageWriter.Create(@"Images\Output\out.png"))

{
    Aurigma.GraphicsMill.Pipeline.Run(imageGenerator + resize + writer);
}

Processing Vector Images Without Rasterization

If you want to load vector graphics and work without rasterization, you can use this method.

C#
float dpi = 150f;

using (var reader = new Aurigma.GraphicsMill.Codecs.PdfReader(@"Images\in.pdf", dpi, dpi))
using (var frame = reader.Frames[0])
using (var container = frame.GetContent())
using (var writer = new Aurigma.GraphicsMill.Codecs.PdfWriter(@"Images\Output\out.pdf"))
{
    var resizeK = 0.5f;

    writer.AddPage((int)(frame.Height * resizeK), (int)(frame.Width * resizeK),
        dpi, dpi, Aurigma.GraphicsMill.RgbColor.White, null);

    using (var graphics = writer.GetGraphics())
    {
        graphics.Transform.Scale(resizeK, resizeK);
        graphics.DrawContainer(container, 0, 0);
    }
}

See Also

Reference

Manual