ImageExtensions.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System;
  2. using System.Drawing;
  3. using System.Drawing.Drawing2D;
  4. using System.Drawing.Imaging;
  5. using System.IO;
  6. namespace MediaBrowser.Server.Implementations.Drawing
  7. {
  8. /// <summary>
  9. /// Class ImageExtensions
  10. /// </summary>
  11. public static class ImageExtensions
  12. {
  13. /// <summary>
  14. /// Saves the image.
  15. /// </summary>
  16. /// <param name="outputFormat">The output format.</param>
  17. /// <param name="image">The image.</param>
  18. /// <param name="toStream">To stream.</param>
  19. /// <param name="quality">The quality.</param>
  20. public static void Save(this Image image, ImageFormat outputFormat, Stream toStream, int quality)
  21. {
  22. // Use special save methods for jpeg and png that will result in a much higher quality image
  23. // All other formats use the generic Image.Save
  24. if (ImageFormat.Jpeg.Equals(outputFormat))
  25. {
  26. SaveAsJpeg(image, toStream, quality);
  27. }
  28. else if (ImageFormat.Png.Equals(outputFormat))
  29. {
  30. image.Save(toStream, ImageFormat.Png);
  31. }
  32. else
  33. {
  34. image.Save(toStream, outputFormat);
  35. }
  36. }
  37. /// <summary>
  38. /// Saves the JPEG.
  39. /// </summary>
  40. /// <param name="image">The image.</param>
  41. /// <param name="target">The target.</param>
  42. /// <param name="quality">The quality.</param>
  43. public static void SaveAsJpeg(this Image image, Stream target, int quality)
  44. {
  45. using (var encoderParameters = new EncoderParameters(1))
  46. {
  47. encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
  48. image.Save(target, GetImageCodecInfo("image/jpeg"), encoderParameters);
  49. }
  50. }
  51. private static readonly ImageCodecInfo[] Encoders = ImageCodecInfo.GetImageEncoders();
  52. /// <summary>
  53. /// Gets the image codec info.
  54. /// </summary>
  55. /// <param name="mimeType">Type of the MIME.</param>
  56. /// <returns>ImageCodecInfo.</returns>
  57. private static ImageCodecInfo GetImageCodecInfo(string mimeType)
  58. {
  59. foreach (var encoder in Encoders)
  60. {
  61. if (string.Equals(encoder.MimeType, mimeType, StringComparison.OrdinalIgnoreCase))
  62. {
  63. return encoder;
  64. }
  65. }
  66. return Encoders.Length == 0 ? null : Encoders[0];
  67. }
  68. /// <summary>
  69. /// Crops an image by removing whitespace and transparency from the edges
  70. /// </summary>
  71. /// <param name="bmp">The BMP.</param>
  72. /// <returns>Bitmap.</returns>
  73. /// <exception cref="System.Exception"></exception>
  74. public static Bitmap CropWhitespace(this Bitmap bmp)
  75. {
  76. var width = bmp.Width;
  77. var height = bmp.Height;
  78. var topmost = 0;
  79. for (int row = 0; row < height; ++row)
  80. {
  81. if (IsAllWhiteRow(bmp, row, width))
  82. topmost = row;
  83. else break;
  84. }
  85. int bottommost = 0;
  86. for (int row = height - 1; row >= 0; --row)
  87. {
  88. if (IsAllWhiteRow(bmp, row, width))
  89. bottommost = row;
  90. else break;
  91. }
  92. int leftmost = 0, rightmost = 0;
  93. for (int col = 0; col < width; ++col)
  94. {
  95. if (IsAllWhiteColumn(bmp, col, height))
  96. leftmost = col;
  97. else
  98. break;
  99. }
  100. for (int col = width - 1; col >= 0; --col)
  101. {
  102. if (IsAllWhiteColumn(bmp, col, height))
  103. rightmost = col;
  104. else
  105. break;
  106. }
  107. if (rightmost == 0) rightmost = width; // As reached left
  108. if (bottommost == 0) bottommost = height; // As reached top.
  109. var croppedWidth = rightmost - leftmost;
  110. var croppedHeight = bottommost - topmost;
  111. if (croppedWidth == 0) // No border on left or right
  112. {
  113. leftmost = 0;
  114. croppedWidth = width;
  115. }
  116. if (croppedHeight == 0) // No border on top or bottom
  117. {
  118. topmost = 0;
  119. croppedHeight = height;
  120. }
  121. // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
  122. var thumbnail = new Bitmap(croppedWidth, croppedHeight, PixelFormat.Format32bppPArgb);
  123. // Preserve the original resolution
  124. TrySetResolution(thumbnail, bmp.HorizontalResolution, bmp.VerticalResolution);
  125. using (var thumbnailGraph = Graphics.FromImage(thumbnail))
  126. {
  127. thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality;
  128. thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
  129. thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
  130. thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
  131. thumbnailGraph.CompositingMode = CompositingMode.SourceCopy;
  132. thumbnailGraph.DrawImage(bmp,
  133. new RectangleF(0, 0, croppedWidth, croppedHeight),
  134. new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
  135. GraphicsUnit.Pixel);
  136. }
  137. return thumbnail;
  138. }
  139. /// <summary>
  140. /// Tries the set resolution.
  141. /// </summary>
  142. /// <param name="bmp">The BMP.</param>
  143. /// <param name="x">The x.</param>
  144. /// <param name="y">The y.</param>
  145. private static void TrySetResolution(Bitmap bmp, float x, float y)
  146. {
  147. if (x > 0 && y > 0)
  148. {
  149. bmp.SetResolution(x, y);
  150. }
  151. }
  152. /// <summary>
  153. /// Determines whether or not a row of pixels is all whitespace
  154. /// </summary>
  155. /// <param name="bmp">The BMP.</param>
  156. /// <param name="row">The row.</param>
  157. /// <param name="width">The width.</param>
  158. /// <returns><c>true</c> if [is all white row] [the specified BMP]; otherwise, <c>false</c>.</returns>
  159. private static bool IsAllWhiteRow(Bitmap bmp, int row, int width)
  160. {
  161. for (var i = 0; i < width; ++i)
  162. {
  163. if (!IsWhiteSpace(bmp.GetPixel(i, row)))
  164. {
  165. return false;
  166. }
  167. }
  168. return true;
  169. }
  170. /// <summary>
  171. /// Determines whether or not a column of pixels is all whitespace
  172. /// </summary>
  173. /// <param name="bmp">The BMP.</param>
  174. /// <param name="col">The col.</param>
  175. /// <param name="height">The height.</param>
  176. /// <returns><c>true</c> if [is all white column] [the specified BMP]; otherwise, <c>false</c>.</returns>
  177. private static bool IsAllWhiteColumn(Bitmap bmp, int col, int height)
  178. {
  179. for (var i = 0; i < height; ++i)
  180. {
  181. if (!IsWhiteSpace(bmp.GetPixel(col, i)))
  182. {
  183. return false;
  184. }
  185. }
  186. return true;
  187. }
  188. /// <summary>
  189. /// Determines if a color is whitespace
  190. /// </summary>
  191. /// <param name="color">The color.</param>
  192. /// <returns><c>true</c> if [is white space] [the specified color]; otherwise, <c>false</c>.</returns>
  193. private static bool IsWhiteSpace(Color color)
  194. {
  195. return (color.R == 255 && color.G == 255 && color.B == 255) || color.A == 0;
  196. }
  197. }
  198. }