2
0

ImageExtensions.cs 7.6 KB

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