PageRenderTime 65ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Eto.Mac/Drawing/BitmapHandler.cs

https://github.com/picoe/Eto
C# | 380 lines | 314 code | 45 blank | 21 comment | 34 complexity | 5c7064b3c39500595b2f60a004e53998 MD5 | raw file
  1. using System;
  2. using System.Globalization;
  3. using System.IO;
  4. using System.Linq;
  5. using Eto.Drawing;
  6. using Eto.Mac.Forms;
  7. using Eto.Shared.Drawing;
  8. using System.Collections.Generic;
  9. using Eto.Forms;
  10. namespace Eto.Mac.Drawing
  11. {
  12. /// <summary>
  13. /// Bitmap data handler.
  14. /// </summary>
  15. /// <copyright>(c) 2012-2013 by Curtis Wensley</copyright>
  16. /// <license type="BSD-3">See LICENSE for full terms</license>
  17. public class BitmapDataHandler : BaseBitmapData
  18. {
  19. public BitmapDataHandler(Bitmap bitmap, IntPtr data, int scanWidth, int bitsPerPixel, object controlObject, bool isPremultiplied)
  20. : base(bitmap, data, scanWidth, bitsPerPixel, controlObject, isPremultiplied)
  21. {
  22. }
  23. public static int ArgbToData(int argb)
  24. {
  25. return unchecked((int)(((uint)argb & 0xFF00FF00) | (((uint)argb & 0xFF) << 16) | (((uint)argb & 0xFF0000) >> 16)));
  26. }
  27. public static int DataToArgb(int bitmapData)
  28. {
  29. return unchecked((int)(((uint)bitmapData & 0xFF00FF00) | (((uint)bitmapData & 0xFF) << 16) | (((uint)bitmapData & 0xFF0000) >> 16)));
  30. }
  31. public override int TranslateArgbToData(int argb)
  32. {
  33. var a = (uint)(byte)(argb >> 24);
  34. var r = (uint)(byte)(argb >> 16);
  35. var g = (uint)(byte)(argb >> 8);
  36. var b = (uint)(byte)(argb);
  37. if (PremultipliedAlpha)
  38. {
  39. r = r * a / 255;
  40. g = g * a / 255;
  41. b = b * a / 255;
  42. }
  43. return unchecked((int)((a << 24) | (b << 16) | (g << 8) | (r)));
  44. }
  45. public override int TranslateDataToArgb(int bitmapData)
  46. {
  47. var a = (uint)(byte)(bitmapData >> 24);
  48. var b = (uint)(byte)(bitmapData >> 16);
  49. var g = (uint)(byte)(bitmapData >> 8);
  50. var r = (uint)(byte)(bitmapData);
  51. if (a > 0 && PremultipliedAlpha)
  52. {
  53. b = b * 255 / a;
  54. g = g * 255 / a;
  55. r = r * 255 / a;
  56. }
  57. return unchecked((int)((a << 24) | (r << 16) | (g << 8) | (b)));
  58. }
  59. public override bool Flipped { get { return false; } }
  60. }
  61. /// <summary>
  62. /// Bitmap handler.
  63. /// </summary>
  64. /// <copyright>(c) 2012-2013 by Curtis Wensley</copyright>
  65. /// <license type="BSD-3">See LICENSE for full terms</license>
  66. public class BitmapHandler : ImageHandler<NSImage, Bitmap>, Bitmap.IHandler
  67. {
  68. NSImageRep rep;
  69. protected NSBitmapImageRep bmprep;
  70. bool alpha = true;
  71. public BitmapHandler()
  72. {
  73. }
  74. public BitmapHandler(NSImage image)
  75. {
  76. Control = image;
  77. rep = GetBestRepresentation();
  78. bmprep = rep as NSBitmapImageRep;
  79. alpha = rep.HasAlpha;
  80. }
  81. public void Create(string fileName)
  82. {
  83. if (!File.Exists(fileName))
  84. throw new FileNotFoundException("Icon not found", fileName);
  85. Control = new NSImage(fileName);
  86. rep = GetBestRepresentation();
  87. bmprep = rep as NSBitmapImageRep;
  88. Control.Size = new CGSize(rep.PixelsWide, rep.PixelsHigh);
  89. alpha = rep.HasAlpha;
  90. }
  91. public void Create(Stream stream)
  92. {
  93. Control = new NSImage(NSData.FromStream(stream));
  94. rep = GetBestRepresentation();
  95. bmprep = rep as NSBitmapImageRep;
  96. Control.Size = new CGSize(rep.PixelsWide, rep.PixelsHigh);
  97. alpha = rep.HasAlpha;
  98. }
  99. public void Create(int width, int height, PixelFormat pixelFormat)
  100. {
  101. switch (pixelFormat)
  102. {
  103. case PixelFormat.Format32bppRgb:
  104. {
  105. alpha = false;
  106. const int numComponents = 4;
  107. const int bitsPerComponent = 8;
  108. const int bitsPerPixel = numComponents * bitsPerComponent;
  109. const int bytesPerPixel = bitsPerPixel / 8;
  110. int bytesPerRow = bytesPerPixel * width;
  111. rep = bmprep = new NSBitmapImageRep(IntPtr.Zero, width, height, bitsPerComponent, 3, false, false, NSColorSpace.DeviceRGB, bytesPerRow, bitsPerPixel);
  112. Control = new NSImage();
  113. Control.AddRepresentation(rep);
  114. break;
  115. }
  116. case PixelFormat.Format24bppRgb:
  117. {
  118. alpha = false;
  119. const int numComponents = 3;
  120. const int bitsPerComponent = 8;
  121. const int bitsPerPixel = numComponents * bitsPerComponent;
  122. const int bytesPerPixel = bitsPerPixel / 8;
  123. int bytesPerRow = bytesPerPixel * width;
  124. rep = bmprep = new NSBitmapImageRep(IntPtr.Zero, width, height, bitsPerComponent, numComponents, false, false, NSColorSpace.DeviceRGB, bytesPerRow, bitsPerPixel);
  125. Control = new NSImage();
  126. Control.AddRepresentation(rep);
  127. break;
  128. }
  129. case PixelFormat.Format32bppRgba:
  130. {
  131. alpha = true;
  132. const int numComponents = 4;
  133. const int bitsPerComponent = 8;
  134. const int bitsPerPixel = numComponents * bitsPerComponent;
  135. const int bytesPerPixel = bitsPerPixel / 8;
  136. int bytesPerRow = bytesPerPixel * width;
  137. rep = bmprep = new NSBitmapImageRep(IntPtr.Zero, width, height, bitsPerComponent, numComponents, true, false, NSColorSpace.DeviceRGB, bytesPerRow, bitsPerPixel);
  138. Control = new NSImage();
  139. Control.AddRepresentation(rep);
  140. break;
  141. }
  142. /*case PixelFormat.Format16bppRgb555:
  143. control = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 5, width, height);
  144. break;*/
  145. default:
  146. throw new ArgumentOutOfRangeException("pixelFormat", pixelFormat, string.Format(CultureInfo.CurrentCulture, "Not supported"));
  147. }
  148. }
  149. public void Create(int width, int height, Graphics graphics)
  150. {
  151. Create(width, height, PixelFormat.Format32bppRgba);
  152. }
  153. public void Create(Image image, int width, int height, ImageInterpolation interpolation)
  154. {
  155. var source = image.ToNS();
  156. Control = source.Resize(new CGSize(width, height), interpolation);
  157. }
  158. public override NSImage GetImage()
  159. {
  160. return Control;
  161. }
  162. public void Save(string fileName, ImageFormat format)
  163. {
  164. using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
  165. {
  166. Save(stream, format);
  167. }
  168. }
  169. public void Save(Stream stream, ImageFormat format)
  170. {
  171. NSBitmapImageFileType type;
  172. switch (format)
  173. {
  174. case ImageFormat.Bitmap:
  175. type = NSBitmapImageFileType.Bmp;
  176. break;
  177. case ImageFormat.Gif:
  178. type = NSBitmapImageFileType.Gif;
  179. break;
  180. case ImageFormat.Jpeg:
  181. type = NSBitmapImageFileType.Jpeg;
  182. break;
  183. case ImageFormat.Png:
  184. type = NSBitmapImageFileType.Png;
  185. break;
  186. case ImageFormat.Tiff:
  187. type = NSBitmapImageFileType.Tiff;
  188. break;
  189. default:
  190. throw new NotSupportedException();
  191. }
  192. var reps = Control.Representations();
  193. if (reps == null)
  194. throw new InvalidDataException();
  195. var newrep = reps.OfType<NSBitmapImageRep>().FirstOrDefault();
  196. if (newrep == null)
  197. {
  198. CGImage img;
  199. img = bmprep != null ? bmprep.CGImage : Control.CGImage;
  200. newrep = new NSBitmapImageRep(img);
  201. }
  202. var data = newrep.RepresentationUsingTypeProperties(type, new NSDictionary());
  203. var datastream = data.AsStream();
  204. datastream.CopyTo(stream);
  205. stream.Flush();
  206. datastream.Dispose();
  207. }
  208. public override Size Size => Control.Size.ToEtoSize();
  209. public override void DrawImage(GraphicsHandler graphics, RectangleF source, RectangleF destination)
  210. {
  211. if (Control.Template)
  212. {
  213. DrawTemplateImage(graphics, source, destination);
  214. return;
  215. }
  216. var sourceRect = new CGRect(source.X, (float)Control.Size.Height - source.Y - source.Height, source.Width, source.Height);
  217. var destRect = destination.ToNS();
  218. if (alpha)
  219. Control.Draw(destRect, sourceRect, NSCompositingOperation.SourceOver, 1, true, null);
  220. else
  221. Control.Draw(destRect, sourceRect, NSCompositingOperation.Copy, 1, true, null);
  222. }
  223. public Bitmap Clone(Rectangle? rectangle = null)
  224. {
  225. if (rectangle == null)
  226. return new Bitmap(new BitmapHandler((NSImage)Control.Copy()));
  227. else
  228. {
  229. var rect = new CGRect(CGPoint.Empty, Control.Size);
  230. var image = new NSImage();
  231. var cgimage = Control.AsCGImage(ref rect, null, null).WithImageInRect(rectangle.Value.ToNS());
  232. image.AddRepresentation(new NSBitmapImageRep(cgimage));
  233. return new Bitmap(new BitmapHandler(image));
  234. }
  235. }
  236. public Color GetPixel(int x, int y)
  237. {
  238. EnsureRep();
  239. if (bmprep == null)
  240. throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Cannot get pixel data for this type of bitmap ({0})", rep?.GetType()));
  241. // don't convert colorspace here otherwise we get incorrect data.. why?
  242. var nscolor = bmprep.ColorAt(x, y);
  243. if (nscolor.ComponentCount >= 3)
  244. {
  245. nscolor.GetRgba(out var red, out var green, out var blue, out var alpha);
  246. return new Color(nscolor, (float)red, (float)green, (float)blue, (float)alpha);
  247. }
  248. return nscolor.ToEto();
  249. }
  250. protected override void Dispose(bool disposing)
  251. {
  252. if (disposing)
  253. {
  254. if (rep != null)
  255. {
  256. rep.Dispose();
  257. rep = null;
  258. }
  259. }
  260. base.Dispose(disposing);
  261. }
  262. public BitmapData Lock()
  263. {
  264. EnsureRep();
  265. if (bmprep == null)
  266. return null;
  267. bool isPremultiplied = alpha && !bmprep.BitmapFormat.HasFlag(NSBitmapFormat.AlphaNonpremultiplied);
  268. return new BitmapDataHandler(Widget, bmprep.BitmapData, (int)bmprep.BytesPerRow, (int)bmprep.BitsPerPixel, Control, isPremultiplied);
  269. }
  270. public void Unlock(BitmapData bitmapData)
  271. {
  272. }
  273. public NSBitmapImageRep GetBitmapImageRep()
  274. {
  275. EnsureRep();
  276. return bmprep;
  277. }
  278. NSImageRep GetBestRepresentation()
  279. {
  280. // Control.BestRepresentationForDevice() is deprecated
  281. return Control.BestRepresentation(new CGRect(CGPoint.Empty, Control.Size), null, null);
  282. }
  283. protected void EnsureRep()
  284. {
  285. if (rep == null)
  286. rep = GetBestRepresentation();
  287. // on Big Sur, rep is usually going to be a proxy, so let's find the concrete NSBitmapImageRep class the slow way..
  288. if (bmprep != null)
  289. return;
  290. if (rep is IconFrameHandler.LazyImageRep lazyRep)
  291. {
  292. bmprep = lazyRep.Rep;
  293. }
  294. else
  295. {
  296. bmprep = rep as NSBitmapImageRep ?? GetBestRepresentation() as NSBitmapImageRep;
  297. }
  298. if (bmprep != null)
  299. return;
  300. // go through concrete representations as we might have a proxy (Big Sur)
  301. // this is fixed with MonoMac, but not Xamarin.Mac.
  302. var representations = Control.Representations();
  303. for (int i = 0; i < representations.Length; i++)
  304. {
  305. NSImageRep rep = representations[i];
  306. if (rep is NSBitmapImageRep brep)
  307. {
  308. bmprep = brep;
  309. return;
  310. }
  311. }
  312. // create a new bitmap rep and copy the contents
  313. var size = Size;
  314. int numComponents = rep.HasAlpha ? 4 : 3;
  315. int bitsPerComponent = 8;
  316. int bitsPerPixel = numComponents * bitsPerComponent;
  317. int bytesPerPixel = bitsPerPixel / 8;
  318. int bytesPerRow = bytesPerPixel * size.Width;
  319. bmprep = new NSBitmapImageRep(IntPtr.Zero, size.Width, size.Height, bitsPerComponent, numComponents, rep.HasAlpha, false, rep.ColorSpaceName, bytesPerRow, bitsPerPixel);
  320. var graphicsContext = NSGraphicsContext.FromBitmap(bmprep);
  321. NSGraphicsContext.GlobalSaveGraphicsState();
  322. NSGraphicsContext.CurrentContext = graphicsContext;
  323. Control.Draw(CGPoint.Empty, new CGRect(CGPoint.Empty, size.ToNS()), NSCompositingOperation.Copy, 1);
  324. NSGraphicsContext.GlobalRestoreGraphicsState();
  325. // remove all existing representations
  326. for (int i = 0; i < representations.Length; i++)
  327. {
  328. NSImageRep rep = representations[i];
  329. Control.RemoveRepresentation(rep);
  330. }
  331. // add the new one back
  332. Control.AddRepresentation(bmprep);
  333. }
  334. }
  335. }