Sitecore: Round Image Processor

How to create round images

imageWhen you are creating a marketing site with a lot of visual on it very likely that you will need to process images that you need to display.
Fortunately, Sitecore provide you with some basic operation that you would need like resize or grayscale image. Moreover it will think for about caching images on server side after processing, so that processing of the image for specific page will happen only once (for more information about images parameters see this link).

In my case there was a specific request to get round images.
To add this functionality to Sitecore we need to intercept MediaStream pipeline and make our changes. It is important that we add our processor before the image would be resized, as it our processor will not be able to get original dimensions of image.

<getmediastream>
    <processor type="Sitecore.Resources.Media.ThumbnailProcessor, Sitecore.Kernel" />
    <!-- Processor for rounded images should be installed before resize processor in order to get right propostions of image -->
    <processor type="Example.ImageProcessor, Example" />
    <processor type="Sitecore.Resources.Media.ResizeProcessor, Sitecore.Kernel" />
    <processor type="Sitecore.Resources.Media.GrayscaleProcessor, Sitecore.Kernel" />
</getMediaStream>

When we identify correct place to add handler we need to add some code for processing.

namespace Examples
{
    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
 
    using Sitecore.Data.Items;
    using Sitecore.Diagnostics;
    using Sitecore.Resources.Media;
 
    public class ImageProcessor
    {
        private static readonly string[] ImageExtentions = { "bmp", "jpeg", "jpg", "png", "gif" };
 
        public void Process(GetMediaStreamPipelineArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if (args.Options.Thumbnail) // should not apply for thumbnails
            {
                return;
            }
 
            var outputStream = args.OutputStream;
            if (outputStream == null)
            {
                return;
            }
 
            var functionsArg = args.Options.CustomOptions["rnd"]; // searching for parameter in options
            if (string.IsNullOrEmpty(functionsArg) || !functionsArg.Equals("1"))
            {
                return;
            }
 
            var extension = args.MediaData.Extension;
            // need to check extension, in order not to apply handler on PDF
            if (!ImageExtentions.Any(x => x.Equals(args.MediaData.Extension, StringComparison.InvariantCultureIgnoreCase)))
            {
                return;
            }
 
            args.OutputStream = this.GetRoundedImage(outputStream, extension, outputStream.MediaItem, args.Options.BackgroundColor);
        }
 
        private MediaStream GetRoundedImage(MediaStream outputStream, string extension, MediaItem mediaItem, Color backgroundColor)
        {
            var sourceImage = (Bitmap)Image.FromStream(outputStream.Stream);
 
            // image processing
            var diameter = sourceImage.Width > sourceImage.Height ? sourceImage.Height : sourceImage.Width;
            var cropRectangle = new Rectangle((sourceImage.Width - diameter) / 2, (sourceImage.Height - diameter) / 2, diameter, diameter);
 
            var croppedImage = sourceImage.Clone(cropRectangle, sourceImage.PixelFormat);
            var textureBrush = new TextureBrush(croppedImage);
            var finalImage = new Bitmap(diameter, diameter);
            var graphics = Graphics.FromImage(finalImage);
 
            var solidBrush = new SolidBrush(extension == "png" ? Color.Transparent : backgroundColor);
            graphics.FillRectangle(solidBrush, 0, 0, finalImage.Width, finalImage.Height);
            graphics.FillEllipse(textureBrush, 0, 0, finalImage.Width, finalImage.Height);
            graphics.Flush();
 
            var memoryStream = new MemoryStream();
            finalImage.Save(memoryStream, ImageFormat.Png);
            memoryStream.Flush();
            memoryStream.Seek(0, SeekOrigin.Begin);
            // image processing end
 
            return new MediaStream(memoryStream, extension, mediaItem);
        }
    }
}

First of all we should skip processing if flag args.Options.Thumbnail is set to true or OutputStream is not set previously.
Then we need to check some parameter (in my case rnd) in args.Options.CustomOptions to define whether we should apply our transformations and finally to check if a content of the stream is an image.
When all conditions are satisfied we could run our transformation functions and assign returned stream to args.OutputStream, so that resize or grayscale processors will use our rounded image.
Image transformation is quite straightforward: we read image data from the stream and fill ellipse with it. Image background we should fill with default background color or transparent in case of png-files.

Sitecore: Round Image Processor

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s