Processing JPEG files may result in image quality loss. The reason for this is the lossy nature of the JPEG compression algorithm. If you perform open/save operations on a JPEG file several times, then the image quality degrades because of JPEG artifacts superposition.
Graphics Mill allows skipping the decoding-encoding step for certain JPEG-processing operations and applies them directly on compressed JPEG data. This topic describes which JPEG operations can be performed without quality loss and how to perform them.
The LosslessJpeg class encapsulates all functionality related to lossless JPEG transformations in Graphics Mill. To use this class you should perform at least two steps:
The simplest lossless operation on a JPEG image is a 90-degree rotation. To rotate a JPEG image, use the LosslessJpeg.WriteRotated method as shown in the following snippet:
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg")) { losslessJpeg.WriteRotated(@"Images\Output\out.jpg", System.Drawing.RotateFlipType.Rotate90FlipNone); }
The LosslessJpeg.WriteRotated method allows not only rotating a JPEG image, but also flipping it horizontally or vertically. To flip an image pass the appropriate RotateFlipType value as a method parameter, like follows:
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg")) { losslessJpeg.WriteRotated(@"Images\out.jpg", System.Drawing.RotateFlipType.RotateNoneFlipX); }
According to the JPEG specification any JPEG image is built of blocks of pixels called MCUs (Minimum Coded Units). Typically MCU size is a number divisible by 8. Lossless rotation or flip rearranges MCU blocks and pixels of each MCU in a new order. If an image width or height is non-divisible by MCU size, then lossless rotation/flip will crop the image. You can use the LosslessJpeg.IsTrimmingRequired(RotateFlipType) method to check if the image will be cropped. The method returns true
if the image will be cropped in the result of a specified lossless operation.
Lossless crop offers benefits not only in terms of the resulting image quality, but also in terms of CPU and memory usage. You save CPU resources by skipping JPEG decompression and do not waste memory on pixels which will be cropped out.
The following code demonstrates how to use the LosslessJpeg.WriteCropped method:
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg")) { losslessJpeg.WriteCropped(@"Images\Output\out.jpg", new System.Drawing.Rectangle(64, 40, 157, 117)); }
The LosslessJpeg.WritePatched method allows applying any Graphics Mill effect on a part of a JPEG image without recompression of the entire image. It is useful when changing only a small part of an image, for example, adding a watermark, removing red eyes, etc.
One of the widely used tasks where patching can be applied is hiding license plate numbers when publishing car photos in the web. The idea is as follows:
The patched car photo will look like the following one:
The following code replaces a part of an image with its mosaic-transformed version:
var rect = new System.Drawing.Rectangle(152, 136, 72, 32); using (var patchBitmap = new Bitmap()) { //Apply crop and mosaic transfroms using (var input = new Bitmap(@"Images\in.jpg")) using (var crop = new Crop(rect)) using (var mosaic = new Mosaic(4, 4)) { Pipeline.Run(input + crop + mosaic + patchBitmap); } //Patch JPEG using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg")) { rect = losslessJpeg.AlignToMCUSize(rect, JpegAlignToSampleSizeMode.Patch); losslessJpeg.WritePatched(@"Images\Output\out.jpg", rect.Location, patchBitmap); } }
The WritePatched method cannot draw a bitmap with arbitrary coordinates. The coordinates must be aligned to MCU size. Alignment can be easily performed by using the LosslessJpeg.AlignToMCUSize method, which takes given coordinates and returns them aligned to the JPEG sample size.
Processing JPEG files is not always meant to change JPEG image, sometimes you only need to modify the metadata. To perform this task without JPEG recompression you can use the following properties of the LosslessJpeg class:
After you update metadata, call the LosslessJpeg.Write method to apply the changes.
The following code updates fields of all four metadata types:
using (var losslessJpeg = new LosslessJpeg(@"Images\in.jpg")) { // IPTC if (losslessJpeg.Iptc == null) losslessJpeg.Iptc = new IptcDictionary(); losslessJpeg.Iptc[IptcDictionary.Caption] = "Mountain"; // EXIF if (losslessJpeg.Exif == null) losslessJpeg.Exif = new ExifDictionary(); losslessJpeg.Exif[ExifDictionary.Software] = "Aurigma Graphics Mill"; // XMP var xmp = new XmpData(); if (losslessJpeg.Xmp != null) xmp.Load(losslessJpeg.Xmp); var node = new XmpValueNode(XmpNodeType.SimpleProperty, "John Wood", XmpTagNames.DCCreator); xmp.AddNode(node); losslessJpeg.Xmp = xmp.Save(); // Adobe image resource blocks if (losslessJpeg.AdobeResources == null) losslessJpeg.AdobeResources = new AdobeResourceDictionary(); var arBlock = new AdobeResourceBlock("Copyright", new byte[] { 1 }); losslessJpeg.AdobeResources[0x040A] = arBlock; losslessJpeg.Write(@"Images\Output\out.jpg"); }