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.
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.
using (var bitmap = new Bitmap(@"Images\in.jpg")) { bitmap.Transforms.Resize(100, 0); bitmap.Save(@"Images\Output\out.jpg"); }
using (var bitmap = new Bitmap(@"Images\in.jpg")) using (var resize = new 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.
using (var bitmap = new Bitmap(@"Images\in.jpg")) { using (var result = Resize.Apply(bitmap, 100, 0)) { result.Save(@"Images\Output\out.jpg"); } }
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.
using (var reader = new JpegReader(@"Images\in.jpg")) using (var resize = new Resize(100, 0)) using (var writer = new JpegWriter(@"Images\Output\out.jpg")) { 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.
Pipeline.Run(@"Images\in.jpg" + new 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.
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:
If you want only to rasterize vector graphics, you can use the Frame.GetBitmap() method.
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.
float dpi = 150f; using (var reader = new 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 Bitmap((int)container.Width, (int)container.Height, PixelFormat.Format24bppRgb, 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"); }
Alternatively, you can transform rasterized graphics through pipelines. Here, the ImageGenerator class prepares a bitmap for pipelines.
float dpi = 150f; using (var reader = new PdfReader(@"Images\in.pdf", dpi, dpi)) using (var frame = reader.Frames[0]) using (var container = frame.GetContent()) using (var imageGenerator = new ImageGenerator(container, PixelFormat.Format24bppRgb, RgbColor.White)) using (var resize = new Resize(100, 0)) using (var writer = ImageWriter.Create(@"Images\Output\out.png")) { Pipeline.Run(imageGenerator + resize + writer); }
If you want to load vector graphics and work without rasterization, you can use this method.
float dpi = 150f; using (var reader = new PdfReader(@"Images\in.pdf", dpi, dpi)) using (var frame = reader.Frames[0]) using (var container = frame.GetContent()) using (var writer = new PdfWriter(@"Images\Output\out.pdf")) { var resizeK = 0.5f; writer.AddPage((int)(frame.Height * resizeK), (int)(frame.Width * resizeK), dpi, dpi, RgbColor.White, null); using (var graphics = writer.GetGraphics()) { graphics.Transform.Scale(resizeK, resizeK); graphics.DrawContainer(container, 0, 0); } }