ImageExtensions.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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. /// Crops an image by removing whitespace and transparency from the edges
  64. /// </summary>
  65. /// <param name="bmp">The BMP.</param>
  66. /// <returns>Bitmap.</returns>
  67. /// <exception cref="System.Exception"></exception>
  68. public static Bitmap CropWhitespace(this Bitmap bmp)
  69. {
  70. var width = bmp.Width;
  71. var height = bmp.Height;
  72. var topmost = 0;
  73. for (int row = 0; row < height; ++row)
  74. {
  75. if (IsAllWhiteRow(bmp, row, width))
  76. topmost = row;
  77. else break;
  78. }
  79. int bottommost = 0;
  80. for (int row = height - 1; row >= 0; --row)
  81. {
  82. if (IsAllWhiteRow(bmp, row, width))
  83. bottommost = row;
  84. else break;
  85. }
  86. int leftmost = 0, rightmost = 0;
  87. for (int col = 0; col < width; ++col)
  88. {
  89. if (IsAllWhiteColumn(bmp, col, height))
  90. leftmost = col;
  91. else
  92. break;
  93. }
  94. for (int col = width - 1; col >= 0; --col)
  95. {
  96. if (IsAllWhiteColumn(bmp, col, height))
  97. rightmost = col;
  98. else
  99. break;
  100. }
  101. if (rightmost == 0) rightmost = width; // As reached left
  102. if (bottommost == 0) bottommost = height; // As reached top.
  103. var croppedWidth = rightmost - leftmost;
  104. var croppedHeight = bottommost - topmost;
  105. if (croppedWidth == 0) // No border on left or right
  106. {
  107. leftmost = 0;
  108. croppedWidth = width;
  109. }
  110. if (croppedHeight == 0) // No border on top or bottom
  111. {
  112. topmost = 0;
  113. croppedHeight = height;
  114. }
  115. // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
  116. var thumbnail = bmp.PixelFormat.HasFlag(PixelFormat.Indexed) ? new Bitmap(croppedWidth, croppedHeight) : new Bitmap(croppedWidth, croppedHeight, bmp.PixelFormat);
  117. // Preserve the original resolution
  118. thumbnail.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
  119. using (var thumbnailGraph = Graphics.FromImage(thumbnail))
  120. {
  121. thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality;
  122. thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
  123. thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
  124. thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
  125. thumbnailGraph.CompositingMode = CompositingMode.SourceOver;
  126. thumbnailGraph.DrawImage(bmp,
  127. new RectangleF(0, 0, croppedWidth, croppedHeight),
  128. new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
  129. GraphicsUnit.Pixel);
  130. }
  131. return thumbnail;
  132. }
  133. /// <summary>
  134. /// Determines whether or not a row of pixels is all whitespace
  135. /// </summary>
  136. /// <param name="bmp">The BMP.</param>
  137. /// <param name="row">The row.</param>
  138. /// <param name="width">The width.</param>
  139. /// <returns><c>true</c> if [is all white row] [the specified BMP]; otherwise, <c>false</c>.</returns>
  140. private static bool IsAllWhiteRow(Bitmap bmp, int row, int width)
  141. {
  142. for (var i = 0; i < width; ++i)
  143. {
  144. if (!IsWhiteSpace(bmp.GetPixel(i, row)))
  145. {
  146. return false;
  147. }
  148. }
  149. return true;
  150. }
  151. /// <summary>
  152. /// Determines whether or not a column of pixels is all whitespace
  153. /// </summary>
  154. /// <param name="bmp">The BMP.</param>
  155. /// <param name="col">The col.</param>
  156. /// <param name="height">The height.</param>
  157. /// <returns><c>true</c> if [is all white column] [the specified BMP]; otherwise, <c>false</c>.</returns>
  158. private static bool IsAllWhiteColumn(Bitmap bmp, int col, int height)
  159. {
  160. for (var i = 0; i < height; ++i)
  161. {
  162. if (!IsWhiteSpace(bmp.GetPixel(col, i)))
  163. {
  164. return false;
  165. }
  166. }
  167. return true;
  168. }
  169. /// <summary>
  170. /// Determines if a color is whitespace
  171. /// </summary>
  172. /// <param name="color">The color.</param>
  173. /// <returns><c>true</c> if [is white space] [the specified color]; otherwise, <c>false</c>.</returns>
  174. private static bool IsWhiteSpace(Color color)
  175. {
  176. return (color.R == 255 && color.G == 255 && color.B == 255) || color.A == 0;
  177. }
  178. }
  179. }