aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Drawing/ImageExtensions.cs
blob: 9dc58b3d2fe2d4827935749cac907a23bfa288bf (plain)
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace MediaBrowser.Controller.Drawing
{
    /// <summary>
    /// Class ImageExtensions
    /// </summary>
    public static class ImageExtensions
    {
        /// <summary>
        /// Saves the image.
        /// </summary>
        /// <param name="outputFormat">The output format.</param>
        /// <param name="image">The image.</param>
        /// <param name="toStream">To stream.</param>
        /// <param name="quality">The quality.</param>
        public static void Save(this Image image, ImageFormat outputFormat, Stream toStream, int quality)
        {
            // Use special save methods for jpeg and png that will result in a much higher quality image
            // All other formats use the generic Image.Save
            if (ImageFormat.Jpeg.Equals(outputFormat))
            {
                SaveAsJpeg(image, toStream, quality);
            }
            else if (ImageFormat.Png.Equals(outputFormat))
            {
                image.Save(toStream, ImageFormat.Png);
            }
            else
            {
                image.Save(toStream, outputFormat);
            }
        }

        /// <summary>
        /// Saves the JPEG.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="target">The target.</param>
        /// <param name="quality">The quality.</param>
        public static void SaveAsJpeg(this Image image, Stream target, int quality)
        {
            using (var encoderParameters = new EncoderParameters(1))
            {
                encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
                image.Save(target, GetImageCodecInfo("image/jpeg"), encoderParameters);
            }
        }

        private static readonly ImageCodecInfo[] Encoders = ImageCodecInfo.GetImageEncoders();

        /// <summary>
        /// Gets the image codec info.
        /// </summary>
        /// <param name="mimeType">Type of the MIME.</param>
        /// <returns>ImageCodecInfo.</returns>
        private static ImageCodecInfo GetImageCodecInfo(string mimeType)
        {
            foreach (var encoder in Encoders)
            {
                if (string.Equals(encoder.MimeType, mimeType, StringComparison.OrdinalIgnoreCase))
                {
                    return encoder;
                }
            }

            return Encoders.Length == 0 ? null : Encoders[0];
        }

        /// <summary>
        /// Determines whether [is pixel format supported by graphics object] [the specified format].
        /// </summary>
        /// <param name="format">The format.</param>
        /// <returns><c>true</c> if [is pixel format supported by graphics object] [the specified format]; otherwise, <c>false</c>.</returns>
        public static bool IsPixelFormatSupportedByGraphicsObject(PixelFormat format)
        {
            // http://msdn.microsoft.com/en-us/library/system.drawing.graphics.fromimage.aspx

            if ((format & PixelFormat.Indexed) == PixelFormat.Indexed)
            {
                return false;
            }
            if ((format & PixelFormat.Undefined) == PixelFormat.Undefined)
            {
                return false;
            }
            if ((format & PixelFormat.DontCare) == PixelFormat.DontCare)
            {
                return false;
            }
            if ((format & PixelFormat.Format16bppArgb1555) == PixelFormat.Format16bppArgb1555)
            {
                return false;
            }
            if ((format & PixelFormat.Format16bppGrayScale) == PixelFormat.Format16bppGrayScale)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Crops an image by removing whitespace and transparency from the edges
        /// </summary>
        /// <param name="bmp">The BMP.</param>
        /// <returns>Bitmap.</returns>
        /// <exception cref="System.Exception"></exception>
        public static Bitmap CropWhitespace(this Bitmap bmp)
        {
            var width = bmp.Width;
            var height = bmp.Height;

            var topmost = 0;
            for (int row = 0; row < height; ++row)
            {
                if (IsAllWhiteRow(bmp, row, width))
                    topmost = row;
                else break;
            }

            int bottommost = 0;
            for (int row = height - 1; row >= 0; --row)
            {
                if (IsAllWhiteRow(bmp, row, width))
                    bottommost = row;
                else break;
            }

            int leftmost = 0, rightmost = 0;
            for (int col = 0; col < width; ++col)
            {
                if (IsAllWhiteColumn(bmp, col, height))
                    leftmost = col;
                else
                    break;
            }

            for (int col = width - 1; col >= 0; --col)
            {
                if (IsAllWhiteColumn(bmp, col, height))
                    rightmost = col;
                else
                    break;
            }

            if (rightmost == 0) rightmost = width; // As reached left
            if (bottommost == 0) bottommost = height; // As reached top.

            var croppedWidth = rightmost - leftmost;
            var croppedHeight = bottommost - topmost;

            if (croppedWidth == 0) // No border on left or right
            {
                leftmost = 0;
                croppedWidth = width;
            }

            if (croppedHeight == 0) // No border on top or bottom
            {
                topmost = 0;
                croppedHeight = height;
            }

            // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
            var thumbnail = new Bitmap(croppedWidth, croppedHeight, PixelFormat.Format32bppPArgb);

            // Preserve the original resolution
            TrySetResolution(thumbnail, bmp.HorizontalResolution, bmp.VerticalResolution);

            using (var thumbnailGraph = Graphics.FromImage(thumbnail))
            {
                thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality;
                thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
                thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
                thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
                thumbnailGraph.CompositingMode = CompositingMode.SourceCopy;

                thumbnailGraph.DrawImage(bmp,
                  new RectangleF(0, 0, croppedWidth, croppedHeight),
                  new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
                  GraphicsUnit.Pixel);
            }
            return thumbnail;
        }

        /// <summary>
        /// Tries the set resolution.
        /// </summary>
        /// <param name="bmp">The BMP.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        private static void TrySetResolution(Bitmap bmp, float x, float y)
        {
            if (x > 0 && y > 0)
            {
                bmp.SetResolution(x, y);
            }
        }

        /// <summary>
        /// Determines whether or not a row of pixels is all whitespace
        /// </summary>
        /// <param name="bmp">The BMP.</param>
        /// <param name="row">The row.</param>
        /// <param name="width">The width.</param>
        /// <returns><c>true</c> if [is all white row] [the specified BMP]; otherwise, <c>false</c>.</returns>
        private static bool IsAllWhiteRow(Bitmap bmp, int row, int width)
        {
            for (var i = 0; i < width; ++i)
            {
                if (!IsWhiteSpace(bmp.GetPixel(i, row)))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Determines whether or not a column of pixels is all whitespace
        /// </summary>
        /// <param name="bmp">The BMP.</param>
        /// <param name="col">The col.</param>
        /// <param name="height">The height.</param>
        /// <returns><c>true</c> if [is all white column] [the specified BMP]; otherwise, <c>false</c>.</returns>
        private static bool IsAllWhiteColumn(Bitmap bmp, int col, int height)
        {
            for (var i = 0; i < height; ++i)
            {
                if (!IsWhiteSpace(bmp.GetPixel(col, i)))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Determines if a color is whitespace
        /// </summary>
        /// <param name="color">The color.</param>
        /// <returns><c>true</c> if [is white space] [the specified color]; otherwise, <c>false</c>.</returns>
        private static bool IsWhiteSpace(Color color)
        {
            return (color.R == 255 && color.G == 255 && color.B == 255) || color.A == 0;
        }
    }
}