PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/Effects/SizePosEffect.cs

https://bitbucket.org/rstarkov/tankiconmaker
C# | 382 lines | 346 code | 31 blank | 5 comment | 48 complexity | 755b9d10bc1997ad0fb2b0ecd9c3057b MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, GPL-3.0, CC-BY-SA-3.0
  1. using System;
  2. using System.ComponentModel;
  3. using System.Linq;
  4. using System.Text.RegularExpressions;
  5. using System.Xml.Linq;
  6. using RT.Util;
  7. using RT.Util.Lingo;
  8. using RT.Util.Serialization;
  9. namespace TankIconMaker.Effects
  10. {
  11. [TypeConverter(typeof(SizeModeTranslation.Conv))]
  12. enum SizeMode2
  13. {
  14. NoChange,
  15. ByPercentage,
  16. BySizeWidthOnly,
  17. BySizeHeightOnly,
  18. BySizeFit,
  19. BySizeStretch,
  20. }
  21. [TypeConverter(typeof(GrowShrinkModeTranslation.Conv))]
  22. enum GrowShrinkMode
  23. {
  24. GrowAndShrink,
  25. GrowOnly,
  26. ShrinkOnly,
  27. }
  28. [TypeConverter(typeof(FilterTranslation.Conv))]
  29. enum Filter
  30. {
  31. Auto, // Lanczos for downsampling and Mitchell for upsampling
  32. Mitchell,
  33. Bicubic,
  34. Lanczos,
  35. Sinc256,
  36. Sinc1024,
  37. }
  38. class SizePosEffect : EffectBase
  39. {
  40. public override int Version { get { return 3; } }
  41. public override string TypeName { get { return App.Translation.EffectSizePos.EffectName; } }
  42. public override string TypeDescription { get { return App.Translation.EffectSizePos.EffectDescription; } }
  43. public bool PositionByPixels { get; set; }
  44. public static MemberTr PositionByPixelsTr(Translation tr) { return new MemberTr(tr.Category.Position, tr.EffectSizePos.PositionByPixels); }
  45. public Anchor Anchor { get; set; }
  46. public static MemberTr AnchorTr(Translation tr) { return new MemberTr(tr.Category.Position, tr.EffectSizePos.Anchor); }
  47. public string X { get; set; }
  48. public static MemberTr XTr(Translation tr) { return new MemberTr(tr.Category.Position, tr.EffectSizePos.X); }
  49. public string Y { get; set; }
  50. public static MemberTr YTr(Translation tr) { return new MemberTr(tr.Category.Position, tr.EffectSizePos.Y); }
  51. public bool SizeByPixels { get; set; }
  52. public static MemberTr SizeByPixelsTr(Translation tr) { return new MemberTr(tr.Category.Size, tr.EffectSizePos.SizeByPixels); }
  53. public double Percentage { get { return _Percentage; } set { _Percentage = Math.Max(0.0, value); } }
  54. private double _Percentage;
  55. public static MemberTr PercentageTr(Translation tr) { return new MemberTr(tr.Category.Size, tr.EffectSizePos.Percentage); }
  56. public string Width { get; set; }
  57. public static MemberTr WidthTr(Translation tr) { return new MemberTr(tr.Category.Size, tr.EffectSizePos.Width); }
  58. public string Height { get; set; }
  59. public static MemberTr HeightTr(Translation tr) { return new MemberTr(tr.Category.Size, tr.EffectSizePos.Height); }
  60. public SizeMode2 SizeMode2 { get; set; }
  61. public static MemberTr SizeMode2Tr(Translation tr) { return new MemberTr(tr.Category.Size, tr.EffectSizePos.SizeMode); }
  62. public GrowShrinkMode GrowShrinkMode { get; set; }
  63. public static MemberTr GrowShrinkModeTr(Translation tr) { return new MemberTr(tr.Category.Size, tr.EffectSizePos.GrowShrinkMode); }
  64. public Filter Filter { get; set; }
  65. public static MemberTr FilterTr(Translation tr) { return new MemberTr(tr.Category.Size, tr.EffectSizePos.Filter); }
  66. public int PixelAlphaThreshold { get { return _PixelAlphaThreshold; } set { _PixelAlphaThreshold = Math.Min(255, Math.Max(0, value)); } }
  67. private int _PixelAlphaThreshold;
  68. public static MemberTr PixelAlphaThresholdTr(Translation tr) { return new MemberTr(tr.Category.General, tr.EffectSizePos.PixelAlphaThreshold); }
  69. public bool ShowLayerBorders { get; set; }
  70. public static MemberTr ShowLayerBordersTr(Translation tr) { return new MemberTr(tr.Category.Debug, tr.EffectSizePos.ShowLayerBorders); }
  71. public bool ShowPixelBorders { get; set; }
  72. public static MemberTr ShowPixelBordersTr(Translation tr) { return new MemberTr(tr.Category.Debug, tr.EffectSizePos.ShowPixelBorders); }
  73. public bool ShowAnchor { get; set; }
  74. public static MemberTr ShowAnchorTr(Translation tr) { return new MemberTr(tr.Category.Debug, tr.EffectSizePos.ShowAnchor); }
  75. #region Old
  76. // Old stuff, to be deleted eventually...
  77. [ClassifyIgnoreIfDefault]
  78. private int Left, Right, Top, Bottom;
  79. [ClassifyIgnoreIfDefault]
  80. private bool LeftAnchor, RightAnchor, TopAnchor, BottomAnchor;
  81. [ClassifyIgnoreIfDefault]
  82. private SizeModeOld SizeMode;
  83. enum SizeModeOld { NoChange, ByPercentage, BySizeWidthOnly, BySizeHeightOnly, BySizeWidthHeightStretch, ByPosLeftRight, ByPosTopBottom, ByPosAllFit, ByPosAllStretch, }
  84. #endregion
  85. public SizePosEffect()
  86. {
  87. PositionByPixels = true;
  88. SizeByPixels = true;
  89. PixelAlphaThreshold = 120;
  90. X = Y = "0";
  91. Anchor = Anchor.TopLeft;
  92. Percentage = 50;
  93. Width = "30";
  94. Height = "18";
  95. SizeMode2 = SizeMode2.NoChange;
  96. GrowShrinkMode = GrowShrinkMode.GrowAndShrink;
  97. Filter = Filter.Auto;
  98. }
  99. public override BitmapBase Apply(RenderTask renderTask, BitmapBase layer)
  100. {
  101. var calculator = new SizeCalculator(renderTask, layer);
  102. Func<string, string> describe = property =>
  103. "*{0}:* {1}\n".Fmt(EggsML.Escape(App.Translation.Calculator.ErrLabel_Layer), EggsML.Escape((string.IsNullOrEmpty(Layer.Name) ? "" : (Layer.Name + " – ")) + Layer.TypeName)) +
  104. "*{0}:* {1}\n".Fmt(EggsML.Escape(App.Translation.Calculator.ErrLabel_Effect), EggsML.Escape((string.IsNullOrEmpty(Name) ? "" : (Name + " – ")) + TypeName)) +
  105. "*{0}:* {1}".Fmt(EggsML.Escape(App.Translation.Calculator.ErrLabel_Property), EggsML.Escape(property));
  106. double ParsedWidth = Math.Max(0, calculator.Parse(Width, describe(WidthTr(App.Translation).DisplayName))),
  107. ParsedHeight = Math.Max(0, calculator.Parse(Height, describe(HeightTr(App.Translation).DisplayName))),
  108. ParsedX = calculator.Parse(X, describe(XTr(App.Translation).DisplayName)),
  109. ParsedY = calculator.Parse(Y, describe(YTr(App.Translation).DisplayName));
  110. Tank tank = renderTask.Tank;
  111. var pixels = PixelRect.FromMixed(0, 0, layer.Width, layer.Height);
  112. if (ShowPixelBorders || PositionByPixels || (SizeByPixels && SizeMode2 != SizeMode2.NoChange && SizeMode2 != SizeMode2.ByPercentage))
  113. pixels = layer.PreciseSize(PixelAlphaThreshold);
  114. bool emptyPixels = pixels.Width <= 0 || pixels.Height <= 0;
  115. if (emptyPixels)
  116. pixels = PixelRect.FromMixed(0, 0, layer.Width, layer.Height);
  117. double scaleWidth, scaleHeight;
  118. int sourceWidth = SizeByPixels ? pixels.Width : layer.Width;
  119. int sourceHeight = SizeByPixels ? pixels.Height : layer.Height;
  120. switch (SizeMode2)
  121. {
  122. case SizeMode2.NoChange:
  123. scaleWidth = scaleHeight = 1;
  124. break;
  125. case SizeMode2.ByPercentage:
  126. scaleWidth = scaleHeight = Percentage / 100.0;
  127. break;
  128. case SizeMode2.BySizeWidthOnly:
  129. scaleWidth = scaleHeight = ParsedWidth / (double) sourceWidth;
  130. break;
  131. case SizeMode2.BySizeHeightOnly:
  132. scaleWidth = scaleHeight = ParsedHeight / (double) sourceHeight;
  133. break;
  134. case SizeMode2.BySizeFit:
  135. scaleWidth = scaleHeight = Math.Min(ParsedWidth / (double) sourceWidth, ParsedHeight / (double) sourceHeight);
  136. break;
  137. case SizeMode2.BySizeStretch:
  138. scaleWidth = ParsedWidth / (double) sourceWidth;
  139. scaleHeight = ParsedHeight / (double) sourceHeight;
  140. break;
  141. default:
  142. throw new Exception("7924688");
  143. }
  144. if (GrowShrinkMode == GrowShrinkMode.GrowOnly)
  145. {
  146. scaleWidth = Math.Max(1.0, scaleWidth);
  147. scaleHeight = Math.Max(1.0, scaleHeight);
  148. }
  149. else if (GrowShrinkMode == GrowShrinkMode.ShrinkOnly)
  150. {
  151. scaleWidth = Math.Min(1.0, scaleWidth);
  152. scaleHeight = Math.Min(1.0, scaleHeight);
  153. }
  154. var anchor = (AnchorRaw) Anchor;
  155. int anchorWidth = (int) Math.Ceiling((PositionByPixels ? pixels.Width : layer.Width) * scaleWidth);
  156. int anchorHeight = (int) Math.Ceiling((PositionByPixels ? pixels.Height : layer.Height) * scaleHeight);
  157. // Location of the top left corner of the anchored rectangle
  158. int tgtX = (int) ParsedX - (anchor.HasFlag(AnchorRaw.Right) ? anchorWidth - 1 : anchor.HasFlag(AnchorRaw.Center) ? (anchorWidth - 1) / 2 : 0);
  159. int tgtY = (int) ParsedY - (anchor.HasFlag(AnchorRaw.Bottom) ? anchorHeight - 1 : anchor.HasFlag(AnchorRaw.Mid) ? (anchorHeight - 1) / 2 : 0);
  160. // Location of the top left corner of the whole scaled layer image
  161. double x = tgtX - (PositionByPixels ? pixels.Left * scaleWidth : 0);
  162. double y = tgtY - (PositionByPixels ? pixels.Top * scaleHeight : 0);
  163. int offsetX = (PositionByPixels ? pixels.Left : 0);
  164. int offsetY = (PositionByPixels ? pixels.Top : 0);
  165. if (ShowLayerBorders || ShowPixelBorders)
  166. {
  167. using (var image = layer.ToMagickImage())
  168. {
  169. image.StrokeWidth = 1;
  170. if (ShowLayerBorders)
  171. {
  172. image.FillColor = ImageMagick.MagickColor.Transparent;
  173. image.StrokeColor = new ImageMagick.MagickColor("aqua");
  174. image.Draw(new ImageMagick.DrawableRectangle(0, 0, layer.Width - 1, layer.Height - 1));
  175. }
  176. if (ShowPixelBorders && !emptyPixels)
  177. {
  178. image.FillColor = ImageMagick.MagickColor.Transparent;
  179. image.StrokeColor = new ImageMagick.MagickColor("red");
  180. image.Draw(new ImageMagick.DrawableRectangle(pixels.Left, pixels.Top, pixels.Right, pixels.Bottom));
  181. }
  182. layer.CopyPixelsFrom(image.ToBitmapSource());
  183. }
  184. }
  185. BitmapResampler.Filter filter;
  186. switch (Filter)
  187. {
  188. case Filter.Auto: filter = null; break;
  189. case Filter.Mitchell: filter = new BitmapResampler.MitchellFilter(); break;
  190. case Filter.Bicubic: filter = new BitmapResampler.CatmullRomFilter(); break;
  191. case Filter.Lanczos: filter = new BitmapResampler.LanczosFilter(); break;
  192. case Filter.Sinc256: filter = new BitmapResampler.LanczosFilter(8); break;
  193. case Filter.Sinc1024: filter = new BitmapResampler.LanczosFilter(16); break;
  194. default: throw new Exception("SizePosEffect.Filter 4107");
  195. }
  196. layer = BitmapResampler.SizePos(layer, scaleWidth, scaleHeight, offsetX, offsetY, tgtX, tgtY, Math.Max(layer.Width, Layer.ParentStyle.IconWidth), Math.Max(layer.Height, Layer.ParentStyle.IconHeight), filter);
  197. if (ShowAnchor)
  198. {
  199. using (var image = layer.ToMagickImage())
  200. {
  201. image.StrokeWidth = 1;
  202. image.StrokeColor = new ImageMagick.MagickColor(255, 255, 0, 120);
  203. image.Draw(new ImageMagick.DrawableLine((int) ParsedX - 1, (int) ParsedY, (int) ParsedX + 1, (int) ParsedY));
  204. image.Draw(new ImageMagick.DrawableLine((int) ParsedX, (int) ParsedY - 1, (int) ParsedX, (int) ParsedY + 1));
  205. layer.CopyPixelsFrom(image.ToBitmapSource());
  206. }
  207. }
  208. return layer;
  209. }
  210. protected override void AfterDeserialize(XElement xml)
  211. {
  212. base.AfterDeserialize(xml);
  213. // At one point, a field called "ConvertedFromOld" was introduced instead of increasing Version to 2. The following is a fix for this.
  214. if (xml.Element("ConvertedFromOld") != null && xml.Element("ConvertedFromOld").Value == "True")
  215. SavedByVersion = 2;
  216. // Upgrade to v2
  217. if (SavedByVersion < 2)
  218. {
  219. SavedByVersion = 2;
  220. AnchorRaw anchor;
  221. if (LeftAnchor && RightAnchor)
  222. {
  223. X = ((Left + Right) / 2).ToString();
  224. anchor = AnchorRaw.Center;
  225. }
  226. else if (LeftAnchor)
  227. {
  228. X = (Left).ToString();
  229. anchor = AnchorRaw.Left;
  230. }
  231. else if (RightAnchor)
  232. {
  233. X = (Right).ToString();
  234. anchor = AnchorRaw.Right;
  235. }
  236. else
  237. {
  238. X = (80 / 2).ToString(); // ok to hard-code 80 because that was the IconWidth of all styles as old as this one
  239. anchor = AnchorRaw.Center;
  240. }
  241. if (TopAnchor && BottomAnchor)
  242. {
  243. Y = ((Top + Bottom) / 2).ToString();
  244. anchor |= AnchorRaw.Mid;
  245. }
  246. else if (TopAnchor)
  247. {
  248. Y = Top.ToString();
  249. anchor |= AnchorRaw.Top;
  250. }
  251. else if (BottomAnchor)
  252. {
  253. Y = Bottom.ToString();
  254. anchor |= AnchorRaw.Bottom;
  255. }
  256. else
  257. {
  258. Y = (24 / 2).ToString(); // ok to hard-code 24 because that was the IconHeight of all styles as old as this one
  259. anchor |= AnchorRaw.Mid;
  260. }
  261. Anchor = (Anchor) anchor;
  262. switch (SizeMode)
  263. {
  264. case SizeModeOld.NoChange:
  265. SizeMode2 = SizeMode2.NoChange;
  266. break;
  267. case SizeModeOld.ByPercentage:
  268. SizeMode2 = SizeMode2.ByPercentage;
  269. break;
  270. case SizeModeOld.BySizeWidthOnly:
  271. SizeMode2 = SizeMode2.BySizeWidthOnly;
  272. break;
  273. case SizeModeOld.BySizeHeightOnly:
  274. SizeMode2 = SizeMode2.BySizeHeightOnly;
  275. break;
  276. case SizeModeOld.BySizeWidthHeightStretch:
  277. SizeMode2 = SizeMode2.BySizeStretch;
  278. break;
  279. case SizeModeOld.ByPosLeftRight:
  280. SizeMode2 = SizeMode2.BySizeWidthOnly;
  281. Width = (Right - Left + 1).ToString();
  282. break;
  283. case SizeModeOld.ByPosTopBottom:
  284. SizeMode2 = SizeMode2.BySizeHeightOnly;
  285. Height = (Bottom - Top + 1).ToString();
  286. break;
  287. case SizeModeOld.ByPosAllFit:
  288. SizeMode2 = SizeMode2.BySizeFit;
  289. Width = (Right - Left + 1).ToString();
  290. Height = (Bottom - Top + 1).ToString();
  291. break;
  292. case SizeModeOld.ByPosAllStretch:
  293. SizeMode2 = SizeMode2.BySizeStretch;
  294. Width = (Right - Left + 1).ToString();
  295. Height = (Bottom - Top + 1).ToString();
  296. break;
  297. }
  298. }
  299. Left = Right = Top = Bottom = 0;
  300. LeftAnchor = RightAnchor = TopAnchor = BottomAnchor = false;
  301. SizeMode = default(SizeModeOld);
  302. }
  303. }
  304. class SizeCalculator : Calculator
  305. {
  306. private RenderTask _renderTask;
  307. private BitmapBase _layer;
  308. public SizeCalculator(RenderTask renderTask, BitmapBase layer)
  309. : base()
  310. {
  311. _renderTask = renderTask;
  312. _layer = layer;
  313. }
  314. protected override double EvalVariable(string variable)
  315. {
  316. var m = Regex.Match(variable, @"^([A-Za-z0-9_\-]+)\.(\w+)$");
  317. if (!m.Success)
  318. return base.EvalVariable(variable);
  319. var layerId = m.Groups[1].Value;
  320. var layerProperty = m.Groups[2].Value;
  321. BitmapBase varImg;
  322. if (layerId == "this")
  323. {
  324. varImg = _layer;
  325. }
  326. else
  327. {
  328. var varLayer = _renderTask.Style.Layers.FirstOrDefault(x => x.Id == layerId);
  329. if (varLayer == null)
  330. throw NewParseException(App.Translation.Calculator.Err_NoLayerWithId.Fmt(layerId));
  331. if (_renderTask.IsLayerAlreadyReferenced(varLayer))
  332. throw NewParseException(App.Translation.Calculator.Err_RecursiveLayerReference.Fmt(layerId));
  333. varImg = _renderTask.RenderLayer(varLayer);
  334. }
  335. var pixels = varImg.PreciseSize();
  336. switch (layerProperty.ToLower())
  337. {
  338. case "width": return pixels.Width;
  339. case "height": return pixels.Height;
  340. case "top": return pixels.Top;
  341. case "left": return pixels.Left;
  342. case "right": return pixels.Right;
  343. case "bottom": return pixels.Bottom;
  344. case "centerhorz": return pixels.CenterHorzD;
  345. case "centervert": return pixels.CenterVertD;
  346. default:
  347. throw NewParseException(App.Translation.Calculator.Err_UnknownLayerProperty.Fmt(layerProperty));
  348. }
  349. }
  350. }
  351. }