PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/extras/HDPhoto/HDPhoto/HDPhotoFileType.cs

https://bitbucket.org/tuldok89/openpdn
C# | 425 lines | 330 code | 76 blank | 19 comment | 16 complexity | 4aac451364e82557b285b134e8fc3159 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) Rick Brewster, Tom Jackson, and past 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;
  10. using PaintDotNet.Data;
  11. using PaintDotNet.SystemLayer;
  12. using System;
  13. using System.Collections.Generic;
  14. using System.Collections.ObjectModel;
  15. using System.ComponentModel;
  16. using System.Drawing.Imaging;
  17. using System.IO;
  18. using System.Reflection;
  19. using System.Text;
  20. using System.Threading;
  21. using System.Windows;
  22. using System.Windows.Media;
  23. using System.Windows.Media.Imaging;
  24. namespace PaintDotNet.Data
  25. {
  26. public sealed class HDPhotoFileType
  27. : FileType
  28. {
  29. public HDPhotoFileType()
  30. : base(Strings.HDPhotoFileType_Name,
  31. FileTypeFlags.SupportsLoading | FileTypeFlags.SupportsSaving,
  32. new string[] { ".wdp", ".hdp" })
  33. {
  34. }
  35. public override SaveConfigWidget CreateSaveConfigWidget()
  36. {
  37. return new HDPhotoSaveConfigWidget();
  38. }
  39. public override bool IsReflexive(SaveConfigToken token)
  40. {
  41. return false;
  42. }
  43. protected override SaveConfigToken OnCreateDefaultSaveConfigToken()
  44. {
  45. return new HDPhotoSaveConfigToken(90, 32);
  46. }
  47. protected override Document OnLoad(Stream input)
  48. {
  49. Document document;
  50. // WIC does not support MTA, so we must marshal this stuff to another thread
  51. // that is then guaranteed to be STA.
  52. switch (Thread.CurrentThread.GetApartmentState())
  53. {
  54. case ApartmentState.Unknown:
  55. case ApartmentState.MTA:
  56. OnLoadArgs ola = new OnLoadArgs();
  57. ola.input = input;
  58. ola.output = null;
  59. ParameterizedThreadStart pts = new ParameterizedThreadStart(OnLoadThreadProc);
  60. Thread staThread = new Thread(pts);
  61. staThread.SetApartmentState(ApartmentState.STA);
  62. staThread.Start(ola);
  63. staThread.Join();
  64. if (ola.exception != null)
  65. {
  66. throw new ApplicationException("OnLoadImpl() threw an exception", ola.exception);
  67. }
  68. document = ola.output;
  69. break;
  70. case ApartmentState.STA:
  71. document = OnLoadImpl(input);
  72. break;
  73. default:
  74. throw new InvalidEnumArgumentException();
  75. }
  76. return document;
  77. }
  78. private sealed class OnLoadArgs
  79. {
  80. public Stream input;
  81. public Document output;
  82. public Exception exception;
  83. }
  84. private void OnLoadThreadProc(object context)
  85. {
  86. OnLoadArgs ola = (OnLoadArgs)context;
  87. try
  88. {
  89. ola.output = OnLoadImpl(ola.input);
  90. }
  91. catch (Exception ex)
  92. {
  93. ola.exception = ex;
  94. }
  95. }
  96. private Document OnLoadImpl(Stream input)
  97. {
  98. WmpBitmapDecoder wbd = new WmpBitmapDecoder(input, BitmapCreateOptions.None, BitmapCacheOption.None);
  99. BitmapFrame frame0 = wbd.Frames[0];
  100. Document output = new Document(frame0.PixelWidth, frame0.PixelHeight);
  101. output.DpuUnit = MeasurementUnit.Inch;
  102. output.DpuX = frame0.DpiX;
  103. output.DpuY = frame0.DpiY;
  104. BitmapLayer layer = Layer.CreateBackgroundLayer(output.Width, output.Height);
  105. MemoryBlock memoryBlock = layer.Surface.Scan0;
  106. IntPtr scan0 = memoryBlock.Pointer;
  107. FormatConvertedBitmap fcb = new FormatConvertedBitmap(frame0, System.Windows.Media.PixelFormats.Bgra32, null, 0);
  108. fcb.CopyPixels(Int32Rect.Empty, scan0, (int)memoryBlock.Length, layer.Surface.Stride);
  109. output.Layers.Add(layer);
  110. BitmapMetadata hdMetadata = (BitmapMetadata)frame0.Metadata;
  111. CopyMetadataTo(output.Metadata, hdMetadata);
  112. // WPF doesn't give us an IDisposable implementation on its types
  113. Utility.GCFullCollect();
  114. return output;
  115. }
  116. private void CopyStringTagTo(BitmapMetadata dst, string dstPropertyName, Metadata src, ExifTagID srcTagID)
  117. {
  118. PropertyItem[] pis = src.GetExifValues(srcTagID);
  119. if (pis.Length > 0)
  120. {
  121. PropertyInfo pi = dst.GetType().GetProperty(dstPropertyName);
  122. string piValue = Exif.DecodeAsciiValue(pis[0]);
  123. try
  124. {
  125. pi.SetValue(dst, piValue, null);
  126. }
  127. catch (Exception)
  128. {
  129. // *shrug*
  130. }
  131. }
  132. }
  133. private void CopyMetadataTo(BitmapMetadata dst, Metadata src)
  134. {
  135. // ApplicationName
  136. CopyStringTagTo(dst, "ApplicationName", src, ExifTagID.Software);
  137. // Author
  138. PropertyItem[] authorsPI = src.GetExifValues(ExifTagID.Artist);
  139. if (authorsPI.Length > 0)
  140. {
  141. List<string> authors = new List<string>();
  142. foreach (PropertyItem pi in authorsPI)
  143. {
  144. string author = Exif.DecodeAsciiValue(pi);
  145. authors.Add(author);
  146. }
  147. ReadOnlyCollection<string> authorsRO = new ReadOnlyCollection<string>(authors);
  148. dst.Author = authorsRO;
  149. }
  150. CopyStringTagTo(dst, "CameraManufacturer", src, ExifTagID.Make);
  151. CopyStringTagTo(dst, "CameraModel", src, ExifTagID.Model);
  152. CopyStringTagTo(dst, "Copyright", src, ExifTagID.Copyright);
  153. CopyStringTagTo(dst, "Title", src, ExifTagID.ImageDescription);
  154. PropertyItem[] dateTimePis = src.GetExifValues(ExifTagID.DateTime);
  155. if (dateTimePis.Length > 0)
  156. {
  157. string dateTime = Exif.DecodeAsciiValue(dateTimePis[0]);
  158. try
  159. {
  160. dst.DateTaken = dateTime;
  161. }
  162. catch (Exception)
  163. {
  164. try
  165. {
  166. string newDateTime = FixDateTimeString(dateTime);
  167. dst.DateTaken = newDateTime;
  168. }
  169. catch (Exception)
  170. {
  171. // *shrug*
  172. }
  173. }
  174. }
  175. }
  176. private string FixDateTimeString(string brokenDateTime)
  177. {
  178. // It may be in the form of YYYY:MM:YY HH:MM:SS
  179. // But we need those first two :'s to be -'s
  180. StringBuilder fixedDateTime = new StringBuilder(brokenDateTime);
  181. for (int i = 0; i < Math.Min(brokenDateTime.Length, 10); ++i)
  182. {
  183. if (fixedDateTime[i] == ':')
  184. {
  185. fixedDateTime[i] = '-';
  186. }
  187. }
  188. return fixedDateTime.ToString();
  189. }
  190. private void CopyMetadataTo(Metadata dst, BitmapMetadata src)
  191. {
  192. dst.AddExifValues(new PropertyItem[1] { Exif.CreateAscii(ExifTagID.Software, src.ApplicationName) });
  193. ReadOnlyCollection<string> authors = src.Author;
  194. if (authors != null)
  195. {
  196. List<PropertyItem> piAuthors = new List<PropertyItem>();
  197. foreach (string author in authors)
  198. {
  199. PropertyItem piAuthor = Exif.CreateAscii(ExifTagID.Artist, author);
  200. piAuthors.Add(piAuthor);
  201. }
  202. dst.AddExifValues(piAuthors.ToArray());
  203. }
  204. dst.AddExifValues(new PropertyItem[1] { Exif.CreateAscii(ExifTagID.Make, src.CameraManufacturer) });
  205. dst.AddExifValues(new PropertyItem[1] { Exif.CreateAscii(ExifTagID.Model, src.CameraModel) });
  206. dst.AddExifValues(new PropertyItem[1] { Exif.CreateAscii(ExifTagID.Copyright, src.Copyright) });
  207. dst.AddExifValues(new PropertyItem[1] { Exif.CreateAscii(ExifTagID.DateTime, src.DateTaken) });
  208. dst.AddExifValues(new PropertyItem[1] { Exif.CreateAscii(ExifTagID.ImageDescription, src.Title) });
  209. }
  210. protected override void OnSave(
  211. Document input,
  212. Stream output,
  213. SaveConfigToken token,
  214. Surface scratchSurface,
  215. ProgressEventHandler callback)
  216. {
  217. switch (Thread.CurrentThread.GetApartmentState())
  218. {
  219. // WIC does not support MTA, so we must marshal this stuff to another thread that is guaranteed to be STA.
  220. case ApartmentState.Unknown:
  221. case ApartmentState.MTA:
  222. ParameterizedThreadStart pts = new ParameterizedThreadStart(OnSaveThreadProc);
  223. OnSaveArgs osa = new OnSaveArgs();
  224. osa.input = input;
  225. osa.output = output;
  226. osa.token = token;
  227. osa.scratchSurface = scratchSurface;
  228. osa.callback = callback;
  229. Thread staThread = new Thread(pts);
  230. staThread.SetApartmentState(ApartmentState.STA);
  231. staThread.Start(osa);
  232. staThread.Join();
  233. if (osa.exception != null)
  234. {
  235. throw new ApplicationException("OnSaveImpl() threw an exception", osa.exception);
  236. }
  237. break;
  238. case ApartmentState.STA:
  239. OnSaveImpl(input, output, token, scratchSurface, callback);
  240. break;
  241. default:
  242. throw new InvalidOperationException();
  243. }
  244. }
  245. private sealed class OnSaveArgs
  246. {
  247. public Document input;
  248. public Stream output;
  249. public SaveConfigToken token;
  250. public Surface scratchSurface;
  251. public ProgressEventHandler callback;
  252. public Exception exception;
  253. }
  254. private void OnSaveThreadProc(object context)
  255. {
  256. OnSaveArgs osa = (OnSaveArgs)context;
  257. try
  258. {
  259. OnSave(osa);
  260. }
  261. catch (Exception ex)
  262. {
  263. osa.exception = ex;
  264. }
  265. }
  266. private void OnSave(OnSaveArgs args)
  267. {
  268. OnSaveImpl(args.input, args.output, args.token, args.scratchSurface, args.callback);
  269. }
  270. private void OnSaveImpl(
  271. Document input,
  272. Stream output,
  273. SaveConfigToken token,
  274. Surface scratchSurface,
  275. ProgressEventHandler callback)
  276. {
  277. HDPhotoSaveConfigToken hdToken = token as HDPhotoSaveConfigToken;
  278. WmpBitmapEncoder wbe = new WmpBitmapEncoder();
  279. using (RenderArgs ra = new RenderArgs(scratchSurface))
  280. {
  281. input.Render(ra, true);
  282. }
  283. MemoryBlock block = scratchSurface.Scan0;
  284. IntPtr scan0 = block.Pointer;
  285. double dpiX;
  286. double dpiY;
  287. switch (input.DpuUnit)
  288. {
  289. case MeasurementUnit.Centimeter:
  290. dpiX = Document.DotsPerCmToDotsPerInch(input.DpuX);
  291. dpiY = Document.DotsPerCmToDotsPerInch(input.DpuY);
  292. break;
  293. case MeasurementUnit.Inch:
  294. dpiX = input.DpuX;
  295. dpiY = input.DpuY;
  296. break;
  297. case MeasurementUnit.Pixel:
  298. dpiX = Document.GetDefaultDpu(MeasurementUnit.Inch);
  299. dpiY = Document.GetDefaultDpu(MeasurementUnit.Inch);
  300. break;
  301. default:
  302. throw new InvalidEnumArgumentException();
  303. }
  304. BitmapSource bitmapSource = BitmapFrame.Create(
  305. scratchSurface.Width,
  306. scratchSurface.Height,
  307. dpiX,
  308. dpiY,
  309. System.Windows.Media.PixelFormats.Bgra32,
  310. null,
  311. scan0,
  312. (int)block.Length, // TODO: does not support >2GB images
  313. scratchSurface.Stride);
  314. FormatConvertedBitmap fcBitmap = new FormatConvertedBitmap(
  315. bitmapSource,
  316. hdToken.BitDepth == 24 ? PixelFormats.Bgr24 : PixelFormats.Bgra32,
  317. null,
  318. 0);
  319. BitmapFrame outputFrame0 = BitmapFrame.Create(fcBitmap);
  320. wbe.Frames.Add(outputFrame0);
  321. wbe.ImageQualityLevel = (float)hdToken.Quality / 100.0f;
  322. string tempFileName = FileSystem.GetTempFileName();
  323. FileStream tempFileOut = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.Read);
  324. wbe.Save(tempFileOut);
  325. tempFileOut.Close();
  326. tempFileOut = null;
  327. FileStream tempFileIn = new FileStream(tempFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
  328. WmpBitmapDecoder wbd = new WmpBitmapDecoder(tempFileIn, BitmapCreateOptions.None, BitmapCacheOption.None);
  329. BitmapFrame ioFrame0 = wbd.Frames[0];
  330. InPlaceBitmapMetadataWriter metadata2 = ioFrame0.CreateInPlaceBitmapMetadataWriter();
  331. CopyMetadataTo(metadata2, input.Metadata);
  332. tempFileIn.Close();
  333. tempFileIn = null;
  334. FileStream tempFileIn2 = new FileStream(tempFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
  335. Utility.CopyStream(tempFileIn2, output);
  336. tempFileIn2.Close();
  337. tempFileIn2 = null;
  338. try
  339. {
  340. File.Delete(tempFileName);
  341. }
  342. catch (Exception)
  343. {
  344. }
  345. // WPF doesn't give us an IDisposable implementation on its types
  346. Utility.GCFullCollect();
  347. }
  348. }
  349. }