ImageSizeCalculationHelper.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using System.Runtime.InteropServices;
  2. namespace PicView.Core.Calculations;
  3. public static class ImageSizeCalculationHelper
  4. {
  5. /// <summary>
  6. /// Returns the interface size of the titlebar based on OS
  7. /// </summary>
  8. public static double GetInterfaceSize()
  9. {
  10. // TODO: find a more elegant solution
  11. return RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 165 : 230;
  12. }
  13. public static ImageSize GetImageSize(double width,
  14. double height,
  15. double monitorWidth,
  16. double monitorHeight,
  17. double monitorMinWidth,
  18. double monitorMinHeight,
  19. double interfaceSize,
  20. double rotationAngle,
  21. double padding,
  22. double dpiScaling,
  23. double uiTopSize,
  24. double uiBottomSize,
  25. double galleryHeight,
  26. double containerWidth,
  27. double containerHeight)
  28. {
  29. if (width <= 0 || height <= 0 || rotationAngle > 360 || rotationAngle < 0)
  30. {
  31. return new ImageSize(0, 0, 0, 0, 0, 0, 0, 0);
  32. }
  33. double aspectRatio;
  34. double maxWidth, maxHeight;
  35. var margin = 0d;
  36. var fullscreen = Settings.WindowProperties.Fullscreen ||
  37. Settings.WindowProperties.Maximized;
  38. var borderSpaceHeight = fullscreen ? 0 : uiTopSize + uiBottomSize + galleryHeight;
  39. var borderSpaceWidth = fullscreen ? 0 : padding;
  40. var workAreaWidth = monitorWidth - borderSpaceWidth;
  41. var workAreaHeight = monitorHeight - borderSpaceHeight;
  42. if (Settings.Zoom.ScrollEnabled)
  43. {
  44. workAreaWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
  45. containerWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
  46. maxWidth = workAreaWidth - padding;
  47. maxHeight = height;
  48. }
  49. else if (Settings.WindowProperties.AutoFit)
  50. {
  51. maxWidth = Settings.ImageScaling.StretchImage
  52. ? workAreaWidth - padding
  53. : Math.Min(workAreaWidth - padding, width);
  54. maxHeight = Settings.ImageScaling.StretchImage
  55. ? workAreaHeight - padding
  56. : Math.Min(workAreaHeight - padding, height);
  57. }
  58. else
  59. {
  60. maxWidth = Settings.ImageScaling.StretchImage
  61. ? containerWidth
  62. : Math.Min(containerWidth, width);
  63. maxHeight = Settings.ImageScaling.StretchImage
  64. ? containerHeight - galleryHeight
  65. : Math.Min(containerHeight - galleryHeight, height);
  66. }
  67. if (Settings.Gallery.IsBottomGalleryShown)
  68. {
  69. if (!Settings.UIProperties.ShowInterface)
  70. {
  71. if (Settings.Gallery.ShowBottomGalleryInHiddenUI)
  72. {
  73. margin = galleryHeight > 0 ? galleryHeight : 0;
  74. }
  75. else
  76. {
  77. margin = 0;
  78. }
  79. }
  80. else
  81. {
  82. margin = galleryHeight > 0 ? galleryHeight : 0;
  83. }
  84. }
  85. // aspect ratio calculation
  86. switch (rotationAngle)
  87. {
  88. case 0:
  89. case 180:
  90. aspectRatio = Math.Min(maxWidth / width, maxHeight / height);
  91. break;
  92. case 90:
  93. case 270:
  94. aspectRatio = Math.Min(maxWidth / height, maxHeight / width);
  95. break;
  96. default:
  97. var rotationRadians = rotationAngle * Math.PI / 180;
  98. var newWidth = Math.Abs(width * Math.Cos(rotationRadians)) +
  99. Math.Abs(height * Math.Sin(rotationRadians));
  100. var newHeight = Math.Abs(width * Math.Sin(rotationRadians)) +
  101. Math.Abs(height * Math.Cos(rotationRadians));
  102. aspectRatio = Math.Min(maxWidth / newWidth, maxHeight / newHeight);
  103. break;
  104. }
  105. // Fit image by aspect ratio calculation
  106. // and update values
  107. double scrollWidth, scrollHeight, xWidth, xHeight;
  108. if (Settings.Zoom.ScrollEnabled)
  109. {
  110. if (Settings.WindowProperties.AutoFit)
  111. {
  112. xWidth = maxWidth - SizeDefaults.ScrollbarSize - 10;
  113. xHeight = maxWidth * height / width;
  114. scrollWidth = maxWidth;
  115. scrollHeight = containerHeight - padding - 8;
  116. }
  117. else
  118. {
  119. scrollWidth = containerWidth + SizeDefaults.ScrollbarSize;
  120. scrollHeight = containerHeight;
  121. xWidth = containerWidth - SizeDefaults.ScrollbarSize + 10;
  122. xHeight = height / width * xWidth;
  123. }
  124. }
  125. else
  126. {
  127. scrollWidth = double.NaN;
  128. scrollHeight = double.NaN;
  129. xWidth = width * aspectRatio;
  130. xHeight = height * aspectRatio;
  131. }
  132. var titleMaxWidth = GetTitleMaxWidth(rotationAngle, xWidth, xHeight, monitorMinWidth, monitorMinHeight,
  133. interfaceSize, containerWidth);
  134. return new ImageSize(xWidth, xHeight, 0, scrollWidth, scrollHeight, titleMaxWidth, margin, aspectRatio);
  135. }
  136. public static ImageSize GetImageSize(double width,
  137. double height,
  138. double secondaryWidth,
  139. double secondaryHeight,
  140. double monitorWidth,
  141. double monitorHeight,
  142. double monitorMinWidth,
  143. double monitorMinHeight,
  144. double interfaceSize,
  145. double rotationAngle,
  146. double padding,
  147. double dpiScaling,
  148. double uiTopSize,
  149. double uiBottomSize,
  150. double galleryHeight,
  151. double containerWidth,
  152. double containerHeight)
  153. {
  154. if (width <= 0 || height <= 0 || secondaryWidth <= 0 || secondaryHeight <= 0 || rotationAngle > 360 ||
  155. rotationAngle < 0)
  156. {
  157. return new ImageSize(0, 0, 0, 0, 0, 0, 0, 0);
  158. }
  159. // Get sizes for both images
  160. var firstSize = GetImageSize(width, height, monitorWidth, monitorHeight, monitorMinWidth, monitorMinHeight,
  161. interfaceSize, rotationAngle, padding, dpiScaling, uiTopSize, uiBottomSize, galleryHeight,
  162. containerWidth,
  163. containerHeight);
  164. var secondSize = GetImageSize(secondaryWidth, secondaryHeight, monitorWidth, monitorHeight, monitorMinWidth,
  165. monitorMinHeight, interfaceSize, rotationAngle, padding, dpiScaling, uiTopSize, uiBottomSize,
  166. galleryHeight,
  167. containerWidth, containerHeight);
  168. // Determine maximum height for both images
  169. var xHeight = Math.Max(firstSize.Height, secondSize.Height);
  170. // Recalculate the widths to maintain the aspect ratio with the new maximum height
  171. var xWidth1 = firstSize.Width / firstSize.Height * xHeight;
  172. var xWidth2 = secondSize.Width / secondSize.Height * xHeight;
  173. // Combined width of both images
  174. var combinedWidth = xWidth1 + xWidth2;
  175. if (Settings.WindowProperties.AutoFit)
  176. {
  177. var widthPadding = Settings.ImageScaling.StretchImage ? 4 : padding;
  178. var availableWidth = monitorWidth - widthPadding;
  179. var availableHeight = monitorHeight - (widthPadding + uiBottomSize + uiTopSize);
  180. if (rotationAngle is 0 or 180)
  181. {
  182. // If combined width exceeds available width, scale both images down proportionally
  183. if (combinedWidth > availableWidth)
  184. {
  185. var scaleFactor = availableWidth / combinedWidth;
  186. xWidth1 *= scaleFactor;
  187. xWidth2 *= scaleFactor;
  188. xHeight *= scaleFactor;
  189. combinedWidth = xWidth1 + xWidth2;
  190. }
  191. }
  192. else
  193. {
  194. if (combinedWidth > availableHeight)
  195. {
  196. var scaleFactor = availableHeight / combinedWidth;
  197. xWidth1 *= scaleFactor;
  198. xWidth2 *= scaleFactor;
  199. xHeight *= scaleFactor;
  200. combinedWidth = xWidth1 + xWidth2;
  201. }
  202. }
  203. }
  204. else
  205. {
  206. if (rotationAngle is 0 or 180)
  207. {
  208. if (combinedWidth > containerWidth)
  209. {
  210. var scaleFactor = containerWidth / combinedWidth;
  211. xWidth1 *= scaleFactor;
  212. xWidth2 *= scaleFactor;
  213. xHeight *= scaleFactor;
  214. combinedWidth = xWidth1 + xWidth2;
  215. }
  216. }
  217. else
  218. {
  219. if (combinedWidth > containerHeight)
  220. {
  221. var scaleFactor = containerHeight / combinedWidth;
  222. xWidth1 *= scaleFactor;
  223. xWidth2 *= scaleFactor;
  224. xHeight *= scaleFactor;
  225. combinedWidth = xWidth1 + xWidth2;
  226. }
  227. }
  228. }
  229. double scrollWidth, scrollHeight;
  230. if (Settings.Zoom.ScrollEnabled)
  231. {
  232. if (Settings.WindowProperties.AutoFit)
  233. {
  234. combinedWidth -= SizeDefaults.ScrollbarSize;
  235. scrollWidth = combinedWidth + SizeDefaults.ScrollbarSize + 8;
  236. var fullscreen = Settings.WindowProperties.Fullscreen ||
  237. Settings.WindowProperties.Maximized;
  238. var borderSpaceHeight = fullscreen ? 0 : uiTopSize + uiBottomSize + galleryHeight;
  239. var workAreaHeight = monitorHeight * dpiScaling - borderSpaceHeight;
  240. scrollHeight = Settings.ImageScaling.StretchImage
  241. ? workAreaHeight
  242. : workAreaHeight - padding;
  243. }
  244. else
  245. {
  246. combinedWidth -= SizeDefaults.ScrollbarSize + 8;
  247. scrollWidth = double.NaN;
  248. scrollHeight = double.NaN;
  249. }
  250. }
  251. else
  252. {
  253. scrollWidth = double.NaN;
  254. scrollHeight = double.NaN;
  255. }
  256. var titleMaxWidth = GetTitleMaxWidth(rotationAngle, combinedWidth, xHeight, monitorMinWidth,
  257. monitorMinHeight, interfaceSize, containerWidth);
  258. var margin = firstSize.Height > secondSize.Height ? firstSize.Margin : secondSize.Margin;
  259. return new ImageSize(combinedWidth, xHeight, xWidth2, scrollWidth, scrollHeight, titleMaxWidth, margin,
  260. firstSize.AspectRatio);
  261. }
  262. public static double GetTitleMaxWidth(double rotationAngle, double width, double height, double monitorMinWidth,
  263. double monitorMinHeight, double interfaceSize, double containerWidth)
  264. {
  265. double titleMaxWidth;
  266. var maximized = Settings.WindowProperties.Fullscreen ||
  267. Settings.WindowProperties.Maximized;
  268. if (Settings.WindowProperties.AutoFit && !maximized)
  269. {
  270. switch (rotationAngle)
  271. {
  272. case 0 or 180:
  273. titleMaxWidth = Math.Max(width, monitorMinWidth);
  274. break;
  275. case 90 or 270:
  276. titleMaxWidth = Math.Max(height, monitorMinHeight);
  277. break;
  278. default:
  279. {
  280. var rotationRadians = rotationAngle * Math.PI / 180;
  281. var newWidth = Math.Abs(width * Math.Cos(rotationRadians)) +
  282. Math.Abs(height * Math.Sin(rotationRadians));
  283. titleMaxWidth = Math.Max(newWidth, monitorMinWidth);
  284. break;
  285. }
  286. }
  287. titleMaxWidth = titleMaxWidth - interfaceSize < interfaceSize
  288. ? interfaceSize
  289. : titleMaxWidth - interfaceSize;
  290. if (Settings.Zoom.ScrollEnabled)
  291. {
  292. titleMaxWidth += SizeDefaults.ScrollbarSize + 10;
  293. }
  294. }
  295. else
  296. {
  297. // Fix title width to window size
  298. titleMaxWidth = containerWidth - interfaceSize <= 0 ? 0 : containerWidth - interfaceSize;
  299. }
  300. return titleMaxWidth;
  301. }
  302. public readonly struct ImageSize(
  303. double width,
  304. double height,
  305. double secondaryWidth,
  306. double scrollViewerWidth,
  307. double scrollViewerHeight,
  308. double titleMaxWidth,
  309. double margin,
  310. double aspectRatio)
  311. {
  312. public double TitleMaxWidth { get; } = titleMaxWidth;
  313. public double Width { get; } = width;
  314. public double Height { get; } = height;
  315. public double ScrollViewerWidth { get; } = scrollViewerWidth;
  316. public double ScrollViewerHeight { get; } = scrollViewerHeight;
  317. public double SecondaryWidth { get; } = secondaryWidth;
  318. public double Margin { get; } = margin;
  319. public double AspectRatio { get; } = aspectRatio;
  320. }
  321. }