BitmapHistoryMemento.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using PaintDotNet.SystemLayer;
  10. using System;
  11. using System.Drawing;
  12. using System.IO;
  13. using System.Runtime.InteropServices;
  14. namespace PaintDotNet.Measurement.HistoryMementos
  15. {
  16. public class BitmapHistoryMemento
  17. : HistoryMemento
  18. {
  19. private IDocumentWorkspace historyWorkspace;
  20. private int layerIndex;
  21. private string tempFileName;
  22. private DeleteFileOnFree tempFileHandle;
  23. private Guid poMaskedSurfaceRef; // if this is non-Guid.Empty, then tempFileName, tempFileHandle, and Data must be null
  24. private Guid poUndoMaskedSurfaceRef;
  25. private class DeleteFileOnFree
  26. : IDisposable
  27. {
  28. private IntPtr bstrFileName;
  29. public DeleteFileOnFree(string fileName)
  30. {
  31. this.bstrFileName = Marshal.StringToBSTR(fileName);
  32. }
  33. ~DeleteFileOnFree()
  34. {
  35. Dispose(false);
  36. }
  37. public void Dispose()
  38. {
  39. Dispose(true);
  40. GC.SuppressFinalize(this);
  41. }
  42. private void Dispose(bool disposing)
  43. {
  44. if (this.bstrFileName != IntPtr.Zero)
  45. {
  46. string fileName = Marshal.PtrToStringBSTR(this.bstrFileName);
  47. Marshal.FreeBSTR(this.bstrFileName);
  48. bool result = FileSystem.TryDeleteFile(fileName);
  49. this.bstrFileName = IntPtr.Zero;
  50. }
  51. }
  52. }
  53. [Serializable]
  54. private sealed class BitmapHistoryMementoData
  55. : HistoryMementoData
  56. {
  57. // only one of the following may be non-null
  58. private IrregularSurface undoImage;
  59. private PdnRegion savedRegion;
  60. public IrregularSurface UndoImage
  61. {
  62. get
  63. {
  64. return undoImage;
  65. }
  66. }
  67. public PdnRegion SavedRegion
  68. {
  69. get
  70. {
  71. return savedRegion;
  72. }
  73. }
  74. public BitmapHistoryMementoData(IrregularSurface undoImage, PdnRegion savedRegion)
  75. {
  76. if (undoImage != null && savedRegion != null)
  77. {
  78. throw new ArgumentException("Only one of undoImage or savedRegion may be non-null");
  79. }
  80. this.undoImage = undoImage;
  81. this.savedRegion = savedRegion;
  82. }
  83. protected override void Dispose(bool disposing)
  84. {
  85. if (disposing)
  86. {
  87. if (undoImage != null)
  88. {
  89. undoImage.Dispose();
  90. undoImage = null;
  91. }
  92. if (savedRegion != null)
  93. {
  94. savedRegion.Dispose();
  95. savedRegion = null;
  96. }
  97. }
  98. base.Dispose(disposing);
  99. }
  100. }
  101. private static unsafe void LoadOrSaveSurfaceRegion(FileStream fileHandle, Surface surface, PdnRegion region, bool trueForSave)
  102. {
  103. Rectangle[] scans = region.GetRegionScansReadOnlyInt();
  104. Rectangle regionBounds = region.GetBoundsInt();
  105. Rectangle surfaceBounds = surface.Bounds;
  106. int scanCount = 0;
  107. void*[] ppvBuffers;
  108. uint[] lengths;
  109. regionBounds.Intersect(surfaceBounds);
  110. long length = (long)regionBounds.Width * (long)regionBounds.Height * (long)ColorBgra.SizeOf;
  111. if (scans.Length == 1 &&
  112. length <= uint.MaxValue &&
  113. surface.IsContiguousMemoryRegion(regionBounds))
  114. {
  115. ppvBuffers = new void*[1];
  116. lengths = new uint[1];
  117. ppvBuffers[0] = surface.GetPointAddressUnchecked(regionBounds.Location);
  118. lengths[0] = (uint)length;
  119. }
  120. else
  121. {
  122. for (int i = 0; i < scans.Length; ++i)
  123. {
  124. Rectangle rect = scans[i];
  125. rect.Intersect(surfaceBounds);
  126. if (rect.Width != 0 && rect.Height != 0)
  127. {
  128. scanCount += rect.Height;
  129. }
  130. }
  131. int scanIndex = 0;
  132. ppvBuffers = new void*[scanCount];
  133. lengths = new uint[scanCount];
  134. for (int i = 0; i < scans.Length; ++i)
  135. {
  136. Rectangle rect = scans[i];
  137. rect.Intersect(surfaceBounds);
  138. if (rect.Width != 0 && rect.Height != 0)
  139. {
  140. for (int y = rect.Top; y < rect.Bottom; ++y)
  141. {
  142. ppvBuffers[scanIndex] = surface.GetPointAddressUnchecked(rect.Left, y);
  143. lengths[scanIndex] = (uint)(rect.Width * ColorBgra.SizeOf);
  144. ++scanIndex;
  145. }
  146. }
  147. }
  148. }
  149. if (trueForSave)
  150. {
  151. FileSystem.WriteToStreamingFileGather(fileHandle, ppvBuffers, lengths);
  152. }
  153. else
  154. {
  155. FileSystem.ReadFromStreamScatter(fileHandle, ppvBuffers, lengths);
  156. }
  157. }
  158. private static unsafe void SaveSurfaceRegion(FileStream outputHandle, Surface surface, PdnRegion region)
  159. {
  160. LoadOrSaveSurfaceRegion(outputHandle, surface, region, true);
  161. }
  162. private static unsafe void LoadSurfaceRegion(FileStream inputHandle, Surface surface, PdnRegion region)
  163. {
  164. LoadOrSaveSurfaceRegion(inputHandle, surface, region, false);
  165. }
  166. public BitmapHistoryMemento(string name, ImageResource image, IDocumentWorkspace historyWorkspace,
  167. int layerIndex, Guid poMaskedSurfaceRef)
  168. : base(name, image)
  169. {
  170. this.layerIndex = layerIndex;
  171. this.historyWorkspace = historyWorkspace;
  172. this.poMaskedSurfaceRef = poMaskedSurfaceRef;
  173. }
  174. public BitmapHistoryMemento(string name, ImageResource image, IDocumentWorkspace historyWorkspace,
  175. int layerIndex, PdnRegion changedRegion)
  176. : this(name, image, historyWorkspace, layerIndex, changedRegion,
  177. ((BitmapLayer)historyWorkspace.GetDocument().Layers[layerIndex]).Surface)
  178. {
  179. }
  180. public BitmapHistoryMemento(string name, ImageResource image, IDocumentWorkspace historyWorkspace,
  181. int layerIndex, PdnRegion changedRegion, Surface copyFromThisSurface)
  182. : base(name, image)
  183. {
  184. this.historyWorkspace = historyWorkspace;
  185. this.layerIndex = layerIndex;
  186. PdnRegion region = changedRegion.Clone();
  187. this.tempFileName = FileSystem.GetTempFileName();
  188. FileStream outputStream = null;
  189. try
  190. {
  191. outputStream = FileSystem.OpenStreamingFile(this.tempFileName, FileAccess.Write);
  192. SaveSurfaceRegion(outputStream, copyFromThisSurface, region);
  193. }
  194. finally
  195. {
  196. if (outputStream != null)
  197. {
  198. outputStream.Dispose();
  199. outputStream = null;
  200. }
  201. }
  202. this.tempFileHandle = new DeleteFileOnFree(this.tempFileName);
  203. BitmapHistoryMementoData data = new BitmapHistoryMementoData(null, region);
  204. this.Data = data;
  205. }
  206. public BitmapHistoryMemento(string name, ImageResource image, IDocumentWorkspace historyWorkspace,
  207. int layerIndex, IrregularSurface saved)
  208. : this(name, image, historyWorkspace, layerIndex, saved, false)
  209. {
  210. }
  211. public BitmapHistoryMemento(string name, ImageResource image, IDocumentWorkspace historyWorkspace, int layerIndex,
  212. IrregularSurface saved, bool takeOwnershipOfSaved)
  213. : base(name, image)
  214. {
  215. this.historyWorkspace = historyWorkspace;
  216. this.layerIndex = layerIndex;
  217. IrregularSurface iss;
  218. if (takeOwnershipOfSaved)
  219. {
  220. iss = saved;
  221. }
  222. else
  223. {
  224. iss = (IrregularSurface)saved.Clone();
  225. }
  226. BitmapHistoryMementoData data = new BitmapHistoryMementoData(iss, null);
  227. this.Data = data;
  228. }
  229. protected override HistoryMemento OnUndo()
  230. {
  231. BitmapHistoryMementoData data = this.Data as BitmapHistoryMementoData;
  232. BitmapLayer layer = (BitmapLayer)this.historyWorkspace.GetDocument().Layers[this.layerIndex];
  233. PdnRegion region;
  234. MaskedSurface maskedSurface = null;
  235. if (this.poMaskedSurfaceRef != Guid.Empty)
  236. {
  237. PersistedObject<MaskedSurface> poMS = PersistedObjectLocker.Get<MaskedSurface>(this.poMaskedSurfaceRef);
  238. maskedSurface = poMS.Object;
  239. region = maskedSurface.CreateRegion();
  240. }
  241. else if (data.UndoImage == null)
  242. {
  243. region = data.SavedRegion;
  244. }
  245. else
  246. {
  247. region = data.UndoImage.Region;
  248. }
  249. BitmapHistoryMemento redo;
  250. if (this.poUndoMaskedSurfaceRef == Guid.Empty)
  251. {
  252. redo = new BitmapHistoryMemento(Name, Image, this.historyWorkspace, this.layerIndex, region);
  253. redo.poUndoMaskedSurfaceRef = this.poMaskedSurfaceRef;
  254. }
  255. else
  256. {
  257. redo = new BitmapHistoryMemento(Name, Image, this.historyWorkspace, this.layerIndex, this.poUndoMaskedSurfaceRef);
  258. }
  259. PdnRegion simplified = Utility.SimplifyAndInflateRegion(region);
  260. if (maskedSurface != null)
  261. {
  262. maskedSurface.Draw(layer.Surface);
  263. }
  264. else if (data.UndoImage == null)
  265. {
  266. using (FileStream input = FileSystem.OpenStreamingFile(this.tempFileName, FileAccess.Read))
  267. {
  268. LoadSurfaceRegion(input, layer.Surface, data.SavedRegion);
  269. }
  270. data.SavedRegion.Dispose();
  271. this.tempFileHandle.Dispose();
  272. this.tempFileHandle = null;
  273. }
  274. else
  275. {
  276. data.UndoImage.Draw(layer.Surface);
  277. data.UndoImage.Dispose();
  278. }
  279. layer.Invalidate(simplified);
  280. simplified.Dispose();
  281. return redo;
  282. }
  283. }
  284. }