imageWhen you are creating a marketing site with a lot of visuals on it very likely that you will need to process images that you need to display. Fortunately, Sitecore provides you with some basic operations that you would need like resize or grayscale image. Moreover, it will think about caching images on the server-side after processing, so that processing of the image for a 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.

Implementation

How to create round images

To add this functionality to Sitecore we need to intercept the MediaStream pipeline and make our changes. We must add our processor before the image would be resized, as our processor will not be able to get the original dimensions of the image.

1
2
3
4
5
6
7
<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 the correct place to add a handler we need to add some code for processing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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 parameters (in my case ‘rnd’) in args.Options.CustomOptions to define whether we should apply our transformations and finally to check if the content of the stream is an image. When all conditions are satisfied we could run our transformation functions and assign the 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.


Follow me on twitter @true_shoorik. Share if the post was useful for you.