ImageExtensions.cs 8.3 KB

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