ImageHelper.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Drawing.Imaging;
  6. using System.IO;
  7. using System.Net;
  8. namespace FastReport.Utils
  9. {
  10. /// <summary>
  11. /// Interface allows to load images with custom format or custom type
  12. /// </summary>
  13. [EditorBrowsable(EditorBrowsableState.Never)]
  14. public interface IImageHelperLoader
  15. {
  16. /// <summary>
  17. /// Returns true if image can be loaded
  18. /// </summary>
  19. /// <param name="imageData"></param>
  20. /// <returns></returns>
  21. bool CanLoad(byte[] imageData);
  22. /// <summary>
  23. /// Returns true if image can be loaded
  24. /// </summary>
  25. /// <param name="fileName"></param>
  26. /// <returns></returns>
  27. bool CanLoad(string fileName);
  28. /// <summary>
  29. /// Try to load the image, must not throw exception!
  30. /// </summary>
  31. /// <param name="imageData"></param>
  32. /// <param name="result"></param>
  33. /// <returns></returns>
  34. bool TryLoad(byte[] imageData, out Image result);
  35. /// <summary>
  36. /// Try to load the image, must not throw exception!
  37. /// </summary>
  38. /// <param name="fileName"></param>
  39. /// <param name="result"></param>
  40. /// <returns></returns>
  41. bool TryLoad(string fileName, out Image result);
  42. }
  43. /// <summary>
  44. /// Internal calss for image processing
  45. /// </summary>
  46. [EditorBrowsable(EditorBrowsableState.Never)]
  47. public static class ImageHelper
  48. {
  49. private readonly static object _customLoadersLocker = new object();
  50. private readonly static List<IImageHelperLoader> _customLoaders = new List<IImageHelperLoader>();
  51. /// <summary>
  52. /// Register a new custom loader
  53. /// </summary>
  54. /// <param name="imageHelperLoader"></param>
  55. public static void Register(IImageHelperLoader imageHelperLoader)
  56. {
  57. lock (_customLoadersLocker)
  58. {
  59. foreach (var loader in _customLoaders)
  60. if (loader == imageHelperLoader)
  61. return;
  62. _customLoaders.Add(imageHelperLoader);
  63. }
  64. }
  65. internal static Bitmap CloneBitmap(Image source)
  66. {
  67. if (source == null)
  68. return null;
  69. Bitmap image = new Bitmap(source.Width, source.Height);
  70. if (!Config.IsRunningOnMono) // mono fw bug workaround
  71. image.SetResolution(source.HorizontalResolution, source.VerticalResolution);
  72. using (Graphics g = Graphics.FromImage(image))
  73. {
  74. g.DrawImageUnscaled(source, 0, 0);
  75. }
  76. return image;
  77. // this can throw OutOfMemory when creating a grayscale image from a cloned bitmap
  78. // return source.Clone() as Bitmap;
  79. }
  80. internal static void Save(Image image, Stream stream)
  81. {
  82. Save(image, stream, image.GetImageFormat());
  83. }
  84. internal static void Save(Image image, string fileName, ImageFormat format)
  85. {
  86. using (FileStream stream = new FileStream(fileName, FileMode.Create))
  87. {
  88. Save(image, stream, format);
  89. }
  90. }
  91. internal static void Save(Image image, Stream stream, ImageFormat format)
  92. {
  93. if (image == null)
  94. return;
  95. if (image is Bitmap)
  96. {
  97. if (format == ImageFormat.Icon)
  98. SaveAsIcon(image, stream, true);
  99. else
  100. image.Save(stream, format);
  101. }
  102. else if (image is Metafile)
  103. {
  104. Metafile emf = null;
  105. using (Bitmap bmp = new Bitmap(1, 1))
  106. using (Graphics g = Graphics.FromImage(bmp))
  107. {
  108. IntPtr hdc = g.GetHdc();
  109. emf = new Metafile(stream, hdc);
  110. g.ReleaseHdc(hdc);
  111. }
  112. using (Graphics g = Graphics.FromImage(emf))
  113. {
  114. g.DrawImage(image, 0, 0);
  115. }
  116. }
  117. }
  118. internal static bool SaveAndConvert(Image image, Stream stream, ImageFormat format)
  119. {
  120. if (image == null)
  121. return false;
  122. if (format == ImageFormat.Jpeg || format == ImageFormat.Gif
  123. || format == ImageFormat.Tiff || format == ImageFormat.Bmp
  124. || format == ImageFormat.Png
  125. || format == ImageFormat.MemoryBmp)
  126. {
  127. if (image is Bitmap)
  128. {
  129. if (format == ImageFormat.MemoryBmp)
  130. throw new Exception(Res.Get("Export,Image,ImageParceFormatException"));
  131. image.Save(stream, format);
  132. return true;
  133. }
  134. //from mf to bitmap
  135. using (Metafile metafile = image as Metafile)
  136. using (Bitmap bitmap = new Bitmap(image.Width, image.Height))
  137. {
  138. bitmap.SetResolution(96F, 96F);
  139. using (Graphics g = Graphics.FromImage(bitmap))
  140. {
  141. g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
  142. g.DrawImage(metafile, 0, 0, (float)image.Width, (float)image.Height);
  143. g.Dispose();
  144. }
  145. bitmap.Save(stream, format);
  146. }
  147. return true;
  148. }
  149. else if (format == ImageFormat.Icon)
  150. {
  151. return SaveAsIcon(image, stream, true);
  152. }
  153. else if (format == ImageFormat.Wmf || format == ImageFormat.Emf)
  154. {
  155. if (image is Metafile)
  156. {
  157. Metafile emf = null;
  158. using (Bitmap bmp = new Bitmap(1, 1))
  159. using (Graphics g = Graphics.FromImage(bmp))
  160. {
  161. IntPtr hdc = g.GetHdc();
  162. emf = new Metafile(stream, hdc);
  163. g.ReleaseHdc(hdc);
  164. }
  165. using (Graphics g = Graphics.FromImage(emf))
  166. {
  167. g.DrawImage(image, 0, 0);
  168. }
  169. return true;
  170. }
  171. }
  172. //throw new Exception(Res.Get("Export,Image,ImageParceFormatException")); // we cant convert image to exif or from bitmap to mf
  173. return false;
  174. }
  175. internal static byte[] Load(string fileName)
  176. {
  177. if (!String.IsNullOrEmpty(fileName))
  178. return File.ReadAllBytes(fileName);
  179. return null;
  180. }
  181. /// <summary>
  182. /// Load the image from bytes, Internal only method
  183. /// </summary>
  184. /// <param name="bytes"></param>
  185. /// <returns></returns>
  186. public static Image Load(byte[] bytes)
  187. {
  188. if (bytes != null && bytes.Length > 0)
  189. {
  190. try
  191. {
  192. #if CROSSPLATFORM
  193. // TODO memory leaks image converter
  194. return Image.FromStream(new MemoryStream(bytes));
  195. #else
  196. return new ImageConverter().ConvertFrom(bytes) as Image;
  197. #endif
  198. }
  199. catch
  200. {
  201. if (_customLoaders.Count > 0)
  202. {
  203. lock (_customLoadersLocker)
  204. {
  205. foreach (var loader in _customLoaders)
  206. {
  207. if (loader.CanLoad(bytes) && loader.TryLoad(bytes, out Image result))
  208. return result;
  209. }
  210. }
  211. }
  212. Bitmap errorBmp = new Bitmap(10, 10);
  213. using (Graphics g = Graphics.FromImage(errorBmp))
  214. {
  215. g.DrawLine(Pens.Red, 0, 0, 10, 10);
  216. g.DrawLine(Pens.Red, 0, 10, 10, 0);
  217. }
  218. return errorBmp;
  219. }
  220. }
  221. return null;
  222. }
  223. internal static byte[] LoadURL(string url)
  224. {
  225. if (!String.IsNullOrEmpty(url))
  226. {
  227. System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
  228. using (WebClient web = new WebClient())
  229. {
  230. return web.DownloadData(url);
  231. }
  232. }
  233. return null;
  234. }
  235. internal static Bitmap GetTransparentBitmap(Image source, float transparency)
  236. {
  237. if (source == null)
  238. return null;
  239. ColorMatrix colorMatrix = new ColorMatrix();
  240. colorMatrix.Matrix33 = 1 - transparency;
  241. ImageAttributes imageAttributes = new ImageAttributes();
  242. imageAttributes.SetColorMatrix(
  243. colorMatrix,
  244. ColorMatrixFlag.Default,
  245. ColorAdjustType.Bitmap);
  246. int width = source.Width;
  247. int height = source.Height;
  248. Bitmap image = new Bitmap(width, height);
  249. image.SetResolution(source.HorizontalResolution, source.VerticalResolution);
  250. using (Graphics g = Graphics.FromImage(image))
  251. {
  252. g.Clear(Color.Transparent);
  253. g.DrawImage(
  254. source,
  255. new Rectangle(0, 0, width, height),
  256. 0, 0, width, height,
  257. GraphicsUnit.Pixel,
  258. imageAttributes);
  259. }
  260. return image;
  261. }
  262. internal static Bitmap GetGrayscaleBitmap(Image source)
  263. {
  264. Bitmap grayscaleBitmap = new Bitmap(source.Width, source.Height, source.PixelFormat);
  265. // Red should be converted to (R*.299)+(G*.587)+(B*.114)
  266. // Green should be converted to (R*.299)+(G*.587)+(B*.114)
  267. // Blue should be converted to (R*.299)+(G*.587)+(B*.114)
  268. // Alpha should stay the same.
  269. ColorMatrix grayscaleMatrix = new ColorMatrix(new float[][]{
  270. new float[] {0.299f, 0.299f, 0.299f, 0, 0},
  271. new float[] {0.587f, 0.587f, 0.587f, 0, 0},
  272. new float[] {0.114f, 0.114f, 0.114f, 0, 0},
  273. new float[] { 0, 0, 0, 1, 0},
  274. new float[] { 0, 0, 0, 0, 1}});
  275. ImageAttributes attributes = new ImageAttributes();
  276. attributes.SetColorMatrix(grayscaleMatrix);
  277. // Use a Graphics object from the new image
  278. using (Graphics graphics = Graphics.FromImage(grayscaleBitmap))
  279. {
  280. // Draw the original image using the ImageAttributes we created
  281. graphics.DrawImage(source,
  282. new Rectangle(0, 0, grayscaleBitmap.Width, grayscaleBitmap.Height),
  283. 0, 0, grayscaleBitmap.Width, grayscaleBitmap.Height,
  284. GraphicsUnit.Pixel, attributes);
  285. }
  286. return grayscaleBitmap;
  287. }
  288. /// <summary>
  289. /// Converts a PNG image to a icon (ico)
  290. /// </summary>
  291. /// <param name="image">The input image</param>
  292. /// <param name="output">The output stream</param>
  293. /// <param name="preserveAspectRatio">Preserve the aspect ratio</param>
  294. /// <returns>Wether or not the icon was succesfully generated</returns>
  295. internal static bool SaveAsIcon(Image image, Stream output, bool preserveAspectRatio = false)
  296. {
  297. int size = 256;
  298. float width = size, height = size;
  299. if (preserveAspectRatio)
  300. {
  301. if (image.Width > image.Height)
  302. height = ((float)image.Height / image.Width) * size;
  303. else
  304. width = ((float)image.Width / image.Height) * size;
  305. }
  306. var newBitmap = new Bitmap(image, new Size((int)width, (int)height));
  307. if (newBitmap == null)
  308. return false;
  309. // save the resized png into a memory stream for future use
  310. using (MemoryStream memoryStream = new MemoryStream())
  311. {
  312. newBitmap.Save(memoryStream, ImageFormat.Png);
  313. var iconWriter = new BinaryWriter(output);
  314. if (output == null || iconWriter == null)
  315. return false;
  316. // 0-1 reserved, 0
  317. iconWriter.Write((byte)0);
  318. iconWriter.Write((byte)0);
  319. // 2-3 image type, 1 = icon, 2 = cursor
  320. iconWriter.Write((short)1);
  321. // 4-5 number of images
  322. iconWriter.Write((short)1);
  323. // image entry 1
  324. // 0 image width
  325. iconWriter.Write((byte)width);
  326. // 1 image height
  327. iconWriter.Write((byte)height);
  328. // 2 number of colors
  329. iconWriter.Write((byte)0);
  330. // 3 reserved
  331. iconWriter.Write((byte)0);
  332. // 4-5 color planes
  333. iconWriter.Write((short)0);
  334. // 6-7 bits per pixel
  335. iconWriter.Write((short)32);
  336. // 8-11 size of image data
  337. iconWriter.Write((int)memoryStream.Length);
  338. // 12-15 offset of image data
  339. iconWriter.Write((int)(6 + 16));
  340. // write image data
  341. // png data must contain the whole png data file
  342. iconWriter.Write(memoryStream.ToArray());
  343. iconWriter.Flush();
  344. }
  345. return true;
  346. }
  347. internal static Image LoadFromFile(string fileName)
  348. {
  349. try
  350. {
  351. return Image.FromFile(fileName);
  352. }
  353. catch (Exception ex)
  354. {
  355. if (_customLoaders.Count > 0)
  356. {
  357. lock (_customLoadersLocker)
  358. {
  359. foreach (var loader in _customLoaders)
  360. {
  361. if (loader.CanLoad(fileName) && loader.TryLoad(fileName, out Image result))
  362. return result;
  363. }
  364. }
  365. }
  366. throw new ImageLoadException(ex);
  367. }
  368. }
  369. }
  370. public static class ImageExtension
  371. {
  372. /// <summary>
  373. /// Returns an Image format.
  374. /// </summary>
  375. public static ImageFormat GetImageFormat(this Image bitmap)
  376. {
  377. if (bitmap == null || bitmap.RawFormat == null)
  378. return null;
  379. ImageFormat format = null;
  380. if (ImageFormat.Jpeg.Equals(bitmap.RawFormat))
  381. {
  382. format = ImageFormat.Jpeg;
  383. }
  384. else if (ImageFormat.Gif.Equals(bitmap.RawFormat))
  385. {
  386. format = ImageFormat.Gif;
  387. }
  388. else if (ImageFormat.Png.Equals(bitmap.RawFormat))
  389. {
  390. format = ImageFormat.Png;
  391. }
  392. else if (ImageFormat.Emf.Equals(bitmap.RawFormat))
  393. {
  394. format = ImageFormat.Emf;
  395. }
  396. else if (ImageFormat.Icon.Equals(bitmap.RawFormat))
  397. {
  398. format = ImageFormat.Icon;
  399. }
  400. else if (ImageFormat.Tiff.Equals(bitmap.RawFormat))
  401. {
  402. format = ImageFormat.Tiff;
  403. }
  404. else if (ImageFormat.Bmp.Equals(bitmap.RawFormat) || ImageFormat.MemoryBmp.Equals(bitmap.RawFormat)) // MemoryBmp format raises a GDI exception
  405. {
  406. format = ImageFormat.Bmp;
  407. }
  408. else if (ImageFormat.Wmf.Equals(bitmap.RawFormat))
  409. {
  410. format = ImageFormat.Wmf;
  411. }
  412. if (format != null)
  413. return format;
  414. return ImageFormat.Bmp;
  415. }
  416. }
  417. }