diff --git a/Src/Dnn.Tests/ToSic.Sxc.Tests/ToSic.Sxc.Tests.csproj b/Src/Dnn.Tests/ToSic.Sxc.Tests/ToSic.Sxc.Tests.csproj index 503c029a1b..703d1e2e7e 100644 --- a/Src/Dnn.Tests/ToSic.Sxc.Tests/ToSic.Sxc.Tests.csproj +++ b/Src/Dnn.Tests/ToSic.Sxc.Tests/ToSic.Sxc.Tests.csproj @@ -73,6 +73,8 @@ + + diff --git a/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/CleanParamTests.cs b/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/CleanParamTests.cs new file mode 100644 index 0000000000..587a70e004 --- /dev/null +++ b/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/CleanParamTests.cs @@ -0,0 +1,134 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using ToSic.Sxc.Web; + +namespace ToSic.Sxc.Tests.Web +{ + [TestClass] + public class CleanParamTests + { + [TestMethod] + public void FloatOrNull() + { + // Expected Nulls + Assert.AreEqual(null, CleanParam.FloatOrNull(null), "null"); + Assert.AreEqual(null, CleanParam.FloatOrNull(new object()), "null"); + + // Expected Floats from other number formats + Assert.AreEqual(0f, CleanParam.FloatOrNull(0d), "double zero"); + Assert.AreEqual(0f, CleanParam.FloatOrNull(0f), "float zero"); + Assert.AreEqual(1.1f, CleanParam.FloatOrNull(1.1f), "float 1.1"); + Assert.AreEqual(1.1f, CleanParam.FloatOrNull(1.1d), "double 1.1"); + + // Expected Floats + Assert.AreEqual(0f, CleanParam.FloatOrNull(0), "int zero"); + Assert.AreEqual(0f, CleanParam.FloatOrNull("0"), "string zero"); + Assert.AreEqual(0f, CleanParam.FloatOrNull("0.0"), "string 0.0"); + Assert.AreEqual(7f, CleanParam.FloatOrNull(7), "int 7"); + Assert.AreEqual(7f, CleanParam.FloatOrNull("7"), "string 7"); + Assert.AreEqual(7.1f, CleanParam.FloatOrNull("7.1"), "string 7.1"); + Assert.AreEqual(6.9f, CleanParam.FloatOrNull("6.9"), "string 6.9"); + } + + [TestMethod] + public void FloatOrNullWithCalculation() + { + // Check non-calculations + Assert.AreEqual(0, CleanParam.DoubleOrNullWithCalculation(0)); + Assert.AreEqual(0, CleanParam.DoubleOrNullWithCalculation("0")); + Assert.AreEqual(2, CleanParam.DoubleOrNullWithCalculation("2")); + Assert.AreEqual(null, CleanParam.DoubleOrNullWithCalculation("")); + + + // Check calculations + Assert.AreEqual(1, CleanParam.DoubleOrNullWithCalculation("1/1")); + Assert.AreEqual(1, CleanParam.DoubleOrNullWithCalculation("1:1")); + Assert.AreEqual(0.5, CleanParam.DoubleOrNullWithCalculation("1/2")); + Assert.AreEqual(0.5, CleanParam.DoubleOrNullWithCalculation("1:2")); + Assert.AreEqual(2, CleanParam.DoubleOrNullWithCalculation("2/1")); + Assert.AreEqual(2, CleanParam.DoubleOrNullWithCalculation("2:1")); + Assert.AreEqual(16d / 9, CleanParam.DoubleOrNullWithCalculation("16:9")); + Assert.AreEqual(16d / 9, CleanParam.DoubleOrNullWithCalculation("16:9")); + Assert.AreEqual(16d / 9, CleanParam.DoubleOrNullWithCalculation("16:9")); + + // Bad calculations + Assert.AreEqual(null, CleanParam.DoubleOrNullWithCalculation("/1")); + Assert.AreEqual(null, CleanParam.DoubleOrNullWithCalculation(":1")); + Assert.AreEqual(null, CleanParam.DoubleOrNullWithCalculation("1:0")); + Assert.AreEqual(null, CleanParam.DoubleOrNullWithCalculation("0:0")); + } + + [TestMethod] + public void IntOrNull() + { + // Expected Nulls + Assert.AreEqual(null, CleanParam.IntOrNull(null), "null"); + Assert.AreEqual(null, CleanParam.IntOrNull(new object()), "null"); + + // Expected Int + Assert.AreEqual(0, CleanParam.IntOrNull(0), "int zero"); + Assert.AreEqual(0, CleanParam.IntOrNull(0f), "float zero"); + Assert.AreEqual(0, CleanParam.IntOrNull(0d), "double zero"); + Assert.AreEqual(0, CleanParam.IntOrNull("0"), "string zero"); + Assert.AreEqual(0, CleanParam.IntOrNull("0.0"), "string 0.0"); + Assert.AreEqual(7, CleanParam.IntOrNull(7), "int 7"); + Assert.AreEqual(7, CleanParam.IntOrNull("7"), "string 7"); + Assert.AreEqual(7, CleanParam.IntOrNull("7.1"), "string 7.1"); + Assert.AreEqual(7, CleanParam.IntOrNull("6.9"), "string 6.9"); + } + + + [TestMethod] + public void IntOrZeroNull() + { + // Expected Nulls + Assert.AreEqual(null, CleanParam.IntOrZeroAsNull(null), "null"); + Assert.AreEqual(null, CleanParam.IntOrZeroAsNull(new object()), "null"); + + // Expected Zero Null Int + Assert.AreEqual(null, CleanParam.IntOrZeroAsNull(0), "int zero"); + Assert.AreEqual(null, CleanParam.IntOrZeroAsNull("0"), "string zero"); + Assert.AreEqual(null, CleanParam.IntOrZeroAsNull("0.0"), "string 0.0"); + Assert.AreEqual(null, CleanParam.IntOrZeroAsNull(0f), "float zero"); + Assert.AreEqual(null, CleanParam.IntOrZeroAsNull(0d), "double zero"); + + // Expected number + Assert.AreEqual(7, CleanParam.IntOrZeroAsNull(7), "int 7"); + Assert.AreEqual(7, CleanParam.IntOrZeroAsNull("7"), "string 7"); + Assert.AreEqual(7, CleanParam.IntOrZeroAsNull("7.1"), "string 7.1"); + Assert.AreEqual(7, CleanParam.IntOrZeroAsNull("6.9"), "string 6.9"); + } + + [TestMethod] + public void RealStringOrNull() + { + // Expected Nulls + Assert.AreEqual(null, CleanParam.RealStringOrNull(null), "null"); + Assert.AreEqual(null, CleanParam.RealStringOrNull(new object()), "null"); + + // Expected Zero Null Int + Assert.AreEqual("0", CleanParam.RealStringOrNull(0), "int zero"); + Assert.AreEqual("0", CleanParam.RealStringOrNull("0"), "string zero"); + Assert.AreEqual("0.0", CleanParam.RealStringOrNull("0.0"), "string 0.0"); + Assert.AreEqual("0", CleanParam.RealStringOrNull(0f), "float zero"); + Assert.AreEqual("0", CleanParam.RealStringOrNull(0d), "double zero"); + + // Expected number + Assert.AreEqual("7", CleanParam.RealStringOrNull(7), "int 7"); + Assert.AreEqual("7", CleanParam.RealStringOrNull("7"), "string 7"); + Assert.AreEqual("7.1", CleanParam.RealStringOrNull("7.1"), "string 7.1"); + Assert.AreEqual("6.9", CleanParam.RealStringOrNull("6.9"), "string 6.9"); + } + + [TestMethod] + public void FNearZero() + { + Assert.IsTrue(CleanParam.FNearZero(0)); + Assert.IsTrue(CleanParam.FNearZero(0.0001f)); + Assert.IsTrue(CleanParam.FNearZero(-0.009f)); + Assert.IsFalse(CleanParam.FNearZero(0.2f)); + Assert.IsFalse(CleanParam.FNearZero(2f)); + } + + } +} diff --git a/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/LinkImageBasicTests.cs b/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/LinkImageBasicTests.cs index 920a2f40a1..598c2c35d4 100644 --- a/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/LinkImageBasicTests.cs +++ b/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/LinkImageBasicTests.cs @@ -42,8 +42,45 @@ public void BasicWidthAndAspectRatio() Assert.AreEqual("test.jpg?w=200", linker.Image("test.jpg", width: 200, aspectRatio: 0)); Assert.AreEqual("test.jpg?w=200&h=200", linker.Image("test.jpg", width: 200, aspectRatio: 1)); - Assert.AreEqual("test.jpg?w=200&h=400", linker.Image("test.jpg", width: 200, aspectRatio: 2)); - Assert.AreEqual("test.jpg?w=200&h=100", linker.Image("test.jpg", width: 200, aspectRatio: 0.5)); + Assert.AreEqual("test.jpg?w=200&h=400", linker.Image("test.jpg", width: 200, aspectRatio: 0.5)); + Assert.AreEqual("test.jpg?w=200&h=100", linker.Image("test.jpg", width: 200, aspectRatio: 2)); + Assert.AreEqual("test.jpg?w=200&h=80", linker.Image("test.jpg", width: 200, aspectRatio: 2.5)); + + // Note: in this case it should be 112.5 and will be rounded down by default + Assert.AreEqual("test.jpg?w=200&h=112", linker.Image("test.jpg", width: 200, aspectRatio: 16f/9)); + } + + [TestMethod] + public void BasicWidthAndAspectRatioString() + { + var linker = new ImgResizeLinker(); + + // Simple Strings + Assert.AreEqual("test.jpg?w=200", linker.Image("test.jpg", width: 200, aspectRatio: "0")); + Assert.AreEqual("test.jpg?w=200&h=200", linker.Image("test.jpg", width: 200, aspectRatio: "1")); + Assert.AreEqual("test.jpg?w=200&h=400", linker.Image("test.jpg", width: 200, aspectRatio: "0.5")); + Assert.AreEqual("test.jpg?w=200&h=100", linker.Image("test.jpg", width: 200, aspectRatio: "2")); + Assert.AreEqual("test.jpg?w=200&h=80", linker.Image("test.jpg", width: 200, aspectRatio: "2.5")); + } + + [TestMethod] + public void BasicWidthAndAspectRatioStringWithSeparator() + { + var linker = new ImgResizeLinker(); + + // Simple Strings + Assert.AreEqual("test.jpg?w=200", linker.Image("test.jpg", width: 200, aspectRatio: "0")); + Assert.AreEqual("test.jpg?w=200&h=200", linker.Image("test.jpg", width: 200, aspectRatio: "1:1")); + Assert.AreEqual("test.jpg?w=200&h=200", linker.Image("test.jpg", width: 200, aspectRatio: "1/1")); + Assert.AreEqual("test.jpg?w=200&h=400", linker.Image("test.jpg", width: 200, aspectRatio: "1:2")); + Assert.AreEqual("test.jpg?w=200&h=400", linker.Image("test.jpg", width: 200, aspectRatio: "1/2")); + Assert.AreEqual("test.jpg?w=200&h=100", linker.Image("test.jpg", width: 200, aspectRatio: "2:1")); + Assert.AreEqual("test.jpg?w=200&h=100", linker.Image("test.jpg", width: 200, aspectRatio: "2/1")); + Assert.AreEqual("test.jpg?w=200&h=100", linker.Image("test.jpg", width: 200, aspectRatio: "2")); + Assert.AreEqual("test.jpg?w=200&h=80", linker.Image("test.jpg", width: 200, aspectRatio: "2.5")); + + // Note: in this case it should be 112.5 and will be rounded down by default + Assert.AreEqual("test.jpg?w=200&h=112", linker.Image("test.jpg", width: 200, aspectRatio: "16/9")); } [TestMethod] diff --git a/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/LinkImageInternalsTests.cs b/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/LinkImageInternalsTests.cs new file mode 100644 index 0000000000..093c3cb42f --- /dev/null +++ b/Src/Dnn.Tests/ToSic.Sxc.Tests/Web/LinkImageInternalsTests.cs @@ -0,0 +1,100 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using ToSic.Sxc.Data; +using ToSic.Sxc.Web; + +namespace ToSic.Sxc.Tests.Web +{ + [TestClass] + public class LinkImageInternalsTests + { + + + const int s = 1440; // standard value, divisible by 1/2/3/4/6/12 + + [TestMethod] + public void FigureOutBestWidthAndHeight() + { + // All Zero and Nulls + Assert.IsTrue(TestBestWH(0, 0), "Null everything"); + Assert.IsTrue(TestBestWH(0, 0, 0), "Null everything"); + Assert.IsTrue(TestBestWH(0, 0, 0, 0), "Null everything"); + Assert.IsTrue(TestBestWH(0, 0, 0, 0, 0), "Null everything"); + Assert.IsTrue(TestBestWH(0, 0, 0, 0, 0, 0), "Null everything"); + + // Single Aspect is set + Assert.IsTrue(TestBestWH(s, 0, width: s), "W only"); + Assert.IsTrue(TestBestWH(0, s, height: s), "H only"); + Assert.IsTrue(TestBestWH(0, 0, factor: 7), "factor only"); + Assert.IsTrue(TestBestWH(0, 0, ar: 7), "ar only"); + + // H / W + Assert.IsTrue(TestBestWH(s, s, width: s, height: s), "W & H only"); + } + + [TestMethod] + public void FigureOutBestWidthAndHeight_Factors() + { + // W / Factor - factor shouldn't apply + Assert.IsTrue(TestBestWH(s, 0, width: s, factor: 1), "W/F only"); + Assert.IsTrue(TestBestWH(s, 0, width: s, factor: .5), "W/F only"); + Assert.IsTrue(TestBestWH(s, 0, width: s, factor: 2), "W/F only"); + Assert.IsTrue(TestBestWH(s, 0, width: s, factor: 1.5), "W/F only"); + + // H / Factor - factor shouldn't apply + Assert.IsTrue(TestBestWH(0, s, height: s, factor: 1), "H/F only"); + Assert.IsTrue(TestBestWH(0, s, height: s, factor: .5), "H/F only"); + Assert.IsTrue(TestBestWH(0, s, height: s, factor: 2), "H/F only"); + Assert.IsTrue(TestBestWH(0, s, height: s, factor: 1.5), "H/F only"); + + // H / W / Factor - factor shouldn't apply + Assert.IsTrue(TestBestWH(s, s, width: s, height: s, factor: 1), "W/H/F"); + Assert.IsTrue(TestBestWH(s, s, width: s, height: s, factor: .5), "W/H/F"); + Assert.IsTrue(TestBestWH(s, s, width: s, height: s, factor: 2), "W/H/F"); + Assert.IsTrue(TestBestWH(s, s, width: s, height: s, factor: 1.5), "W/H/F"); + } + + [TestMethod] + public void FigureOutBestWidthAndHeight_SettingsWH() + { + Assert.IsTrue(TestBestWH(s, 0, settings: ToDyn(new { Width = s })), "W only"); + Assert.IsTrue(TestBestWH(0, s, settings: ToDyn(new { Height = s })), "H only"); + Assert.IsTrue(TestBestWH(0, 0, settings: ToDyn(new { })), "No params"); + Assert.IsTrue(TestBestWH(s, s, settings: ToDyn(new { Width = s, Height = s })), "W/H params"); + } + + [TestMethod] + public void FigureOutBestWidthAndHeight_SettingsWHF() + { + FigureOutWHFactors(0, 1); + FigureOutWHFactors(1, 1); + FigureOutWHFactors(2, 2); + FigureOutWHFactors(0.5, 0.5); + } + + private void FigureOutWHFactors(double factor, double fResult) + { + Console.WriteLine("Run with Factor: " + factor); + // Factor 1 + Assert.IsTrue(TestBestWH((int)(fResult * s), 0, factor: factor, settings: ToDyn(new { Width = s })), $"W with f:{factor}"); + Assert.IsTrue(TestBestWH(0, (int)(fResult * s), factor: factor, settings: ToDyn(new { Height = s })), $"H with f:{factor}"); + Assert.IsTrue(TestBestWH(0, 0, factor: factor, settings: ToDyn(new { })), $"No params f:{factor}"); + Assert.IsTrue(TestBestWH((int)(fResult * s), (int)(fResult * s), factor: factor, settings: ToDyn(new { Width = s, Height = s })), $"W/H f:{factor}"); + } + + private DynamicReadObject ToDyn(object o) => new DynamicReadObject(o); + + + private bool TestBestWH(int expW, int expH, object width = null, object height = null, object factor = null, object ar = null, ICanGetNameNotFinal settings = null) + { + var linker = new ImgResizeLinker(); + + var t1 = linker.FigureOutBestWidthAndHeight(width, height, factor, ar, settings); + var ok = Equals(new Tuple(expW, expH), t1); + var okW = expW.Equals(t1.Item1); + var okH = expH.Equals(t1.Item2); + Console.WriteLine((ok ? "ok" : "error") + "; W:" + t1.Item1 + (okW ? "==": "!=") + expW + "; H:" + t1.Item2 + (okH ? "==" : "!=") + expH); + return ok; + } + } +} diff --git a/Src/Sxc/ToSic.Sxc/Data/DynamicWrapper/DynamicReadObject.cs b/Src/Sxc/ToSic.Sxc/Data/DynamicWrapper/DynamicReadObject.cs index 516a6b3d47..6a513b77ef 100644 --- a/Src/Sxc/ToSic.Sxc/Data/DynamicWrapper/DynamicReadObject.cs +++ b/Src/Sxc/ToSic.Sxc/Data/DynamicWrapper/DynamicReadObject.cs @@ -30,7 +30,7 @@ public partial class DynamicReadObject: DynamicObject, IWrapper, IProper /// /// Determines if properties which are objects should again be wrapped. When using this for DynamicModel it should be false, otherwise usually true. [PrivateApi] - public DynamicReadObject(object item, bool reWrapObjects) + public DynamicReadObject(object item, bool reWrapObjects = false) { _reWrapObjects = reWrapObjects; UnwrappedContents = item; diff --git a/Src/Sxc/ToSic.Sxc/Properties/AssemblyInfo.cs b/Src/Sxc/ToSic.Sxc/Properties/AssemblyInfo.cs index 32e4b287a8..83a044c97a 100644 --- a/Src/Sxc/ToSic.Sxc/Properties/AssemblyInfo.cs +++ b/Src/Sxc/ToSic.Sxc/Properties/AssemblyInfo.cs @@ -4,11 +4,6 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -//[assembly: AssemblyTitle("ToSic.Sxc")] -//[assembly: AssemblyDescription("2sxc - the content module for DotNetNuke")] -//[assembly: AssemblyConfiguration("")] -//[assembly: AssemblyCompany("2sic internet solutions GmbH")] -//[assembly: AssemblyProduct("ToSic.Sxc")] [assembly: AssemblyCopyright("Copyright © 2sic 2021")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -20,3 +15,5 @@ [assembly: InternalsVisibleTo("ToSic.SexyContent.Razor")] [assembly: InternalsVisibleTo("ToSic.Sxc.Mvc")] [assembly: InternalsVisibleTo("ToSic.Sxc.Oqtane.Server")] + +[assembly: InternalsVisibleTo("ToSic.Sxc.Tests")] diff --git a/Src/Sxc/ToSic.Sxc/Web/Images/CleanParam.cs b/Src/Sxc/ToSic.Sxc/Web/Images/CleanParam.cs new file mode 100644 index 0000000000..8b537c7a5d --- /dev/null +++ b/Src/Sxc/ToSic.Sxc/Web/Images/CleanParam.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ToSic.Sxc.Web +{ + internal static class CleanParam + { + internal static string RealStringOrNull(object value) + { + if (value == null) return null; + if (value is string strValue) return strValue; + if (!value.GetType().IsValueType) return null; + + // Only do this for value types + strValue = value.ToString(); + return string.IsNullOrEmpty(strValue) ? null : strValue; + } + + /// + /// Check if an object + /// + /// + /// + internal static int? IntOrNull(object value) + { + if (value is null) return null; + if (value is int intVal) return intVal; + + var floatVal = FloatOrNull(value); + if (floatVal is null) return null; + + var rounded = (int)Math.Round(floatVal.Value); + if (rounded < 1) return 0; + return rounded; + } + + + internal static int? IntOrZeroAsNull(object value) + { + var val = IntOrNull(value); + if (val == 0) return null; + return val; + } + + + internal static float? FloatOrNull(object value) + { + if (value is null) return null; + if (value is float floatVal) return floatVal; + if (value is double dVal) return (float)dVal; + + var strValue = RealStringOrNull(value); + if (strValue == null) return null; + if (!double.TryParse(strValue, out var doubleValue)) return null; + return (float)doubleValue; + } + + internal static double? DoubleOrNullWithCalculation(object value) + { + // First, check if it's a string like "4:2" or "16/9" + if (value is string strValue && !string.IsNullOrWhiteSpace(strValue)) + { + // check for separator + var separator = strValue.IndexOfAny(new[] { ':', '/' }); + if (separator > 0) // if it starts with the separator, something is wrong + { + var leftPart = strValue.Substring(0, separator); + var rightPart = strValue.Substring(separator + 1); + + if (double.TryParse(leftPart, out var top) && double.TryParse(rightPart, out var bottom) && + top > 0 && bottom > 0) + { + var result = top / bottom; + return result; + } + } + } + + return FloatOrNull(value); + } + + internal static bool FNearZero(float f) => Math.Abs(f) <= 0.01; + internal static bool DNearZero(double f) => Math.Abs(f) <= 0.01; + } +} diff --git a/Src/Sxc/ToSic.Sxc/Web/Images/ImageLinkerBase.cs b/Src/Sxc/ToSic.Sxc/Web/Images/ImageLinkerBase.cs index 510ac97438..e90c22ec7a 100644 --- a/Src/Sxc/ToSic.Sxc/Web/Images/ImageLinkerBase.cs +++ b/Src/Sxc/ToSic.Sxc/Web/Images/ImageLinkerBase.cs @@ -3,6 +3,7 @@ using ToSic.Eav.Logging; using ToSic.Razor.Blade; using ToSic.Sxc.Data; +using static ToSic.Sxc.Web.CleanParam; namespace ToSic.Sxc.Web.Images { @@ -49,8 +50,7 @@ public string Image(string url = null, var formToUse = RealStringOrNull(format); // Aspects which aren't affected by scale - var qFinal = IntOrNull(quality) - ?? IntOrNull(getSettings?.Get("Quality")) ?? 0; + var qFinal = IntOrZeroAsNull(quality) ?? IntOrZeroAsNull(getSettings?.Get("Quality")) ?? 0; string mToUse = KeepBestParam(resizeMode, getSettings?.Get("ResizeMode")); string sToUse = KeepBestParam(scaleMode, getSettings?.Get("ScaleMode")); @@ -94,10 +94,6 @@ internal abstract Tuple FigureOutBestWidthAndHeight(object width, obje internal abstract string KeepBestParam(object given, object setting); - internal abstract string RealStringOrNull(object value); - - internal abstract int? IntOrNull(object value); - #endregion } } diff --git a/Src/Sxc/ToSic.Sxc/Web/ImgResizeLinker.cs b/Src/Sxc/ToSic.Sxc/Web/ImgResizeLinker.cs index fa30a8b191..89cc82603d 100644 --- a/Src/Sxc/ToSic.Sxc/Web/ImgResizeLinker.cs +++ b/Src/Sxc/ToSic.Sxc/Web/ImgResizeLinker.cs @@ -3,6 +3,7 @@ using ToSic.Eav.Logging; using ToSic.Sxc.Data; using ToSic.Sxc.Web.Images; +using static ToSic.Sxc.Web.CleanParam; namespace ToSic.Sxc.Web { @@ -12,8 +13,6 @@ public class ImgResizeLinker: ImageLinkerBase // HasLog internal const int MaxSize = 3200; internal const int MaxQuality = 100; - //public bool Debug = false; - public ImgResizeLinker() : base($"{Constants.SxcLogName}.ImgRes") { } @@ -22,27 +21,27 @@ public ImgResizeLinker() : base($"{Constants.SxcLogName}.ImgRes") internal override Tuple FigureOutBestWidthAndHeight(object width, object height, object factor, object aspectRatio, ICanGetNameNotFinal getSettings) { // Try to pre-process parameters and prefer them - var parms = new Tuple(IntOrNull(width), IntOrNull(height)); + var parms = new Tuple(IntOrZeroAsNull(width), IntOrZeroAsNull(height)); IfDebugLogPair("Params", parms); // Pre-Clean the values - all as strings var set = new Tuple(getSettings?.Get("Width"), getSettings?.Get("Height")); if(getSettings!=null) IfDebugLogPair("Settings", set); - var safe = new Tuple(parms.Item1 ?? IntOrNull(set.Item1) ?? 0, parms.Item2 ?? IntOrNull(set.Item2) ?? 0); + var safe = new Tuple(parms.Item1 ?? IntOrZeroAsNull(set.Item1) ?? 0, parms.Item2 ?? IntOrZeroAsNull(set.Item2) ?? 0); IfDebugLogPair("Safe", safe); - var factorFinal = FloatOrNull(factor) ?? 0; - var arFinal = FloatOrNull(aspectRatio) - ?? FloatOrNull(getSettings?.Get("AspectRatio")) ?? 0; + var factorFinal = DoubleOrNullWithCalculation(factor) ?? 0; + double arFinal = DoubleOrNullWithCalculation(aspectRatio) + ?? DoubleOrNullWithCalculation(getSettings?.Get("AspectRatio")) ?? 0; if (Debug) Log.Add($"Resize Factor: {factorFinal}, Aspect Ratio: {arFinal}"); // if either param h/w was null, then do a rescaling on the param which comes from the settings // But ignore the other one! - var rescale = factorFinal != 0 && (parms.Item1 == null || parms.Item2 == null); + var rescale = (!DNearZero(factorFinal) || !DNearZero(arFinal)) && (parms.Item1 == null || parms.Item2 == null); Tuple resizedNew = rescale - ? Rescale(safe/*.Item1, safe.Item2*/, factorFinal, arFinal, parms.Item1 == null, parms.Item2 == null) + ? Rescale(safe, factorFinal, arFinal, parms.Item1 == null, parms.Item2 == null) : safe; IfDebugLogPair("Rescale", resizedNew); @@ -69,65 +68,46 @@ internal override string KeepBestParam(object given, object setting) return strSetting; } - internal override string RealStringOrNull(object value) - { - if (value == null) return null; - var strValue = value.ToString(); - return string.IsNullOrEmpty(strValue) ? null : strValue; - } - - /// - /// Check if an object - /// - /// - /// - internal override int? IntOrNull(object value) - { - if (value == null) return null; - if (value is int intVal ) return intVal; - - var floatVal = FloatOrNull(value); - if (floatVal == null) return null; - - var rounded = (int)Math.Round(floatVal.Value); - if (rounded < 1) return null; - return rounded; - } - internal float? FloatOrNull(object value) - { - if (value == null) return null; - if (value is float floatVal) return floatVal; - if (value is double dVal) return (float)dVal; - - var strValue = RealStringOrNull(value); - if (strValue == null) return null; - if (!double.TryParse(strValue, out var doubleValue)) return null; - return (float)doubleValue; - } - internal Tuple Rescale(Tuple dims, /*int width, int height,*/ float factor, float aspectRatio, bool scaleW, bool scaleH) + internal Tuple Rescale(Tuple dims, double factor, double aspectRatio, bool scaleW, bool scaleH) { var maybeLog = Debug ? Log : null; var wrapLog = maybeLog.SafeCall>(); - // Check if we have nothing to rescale + + // var useAspectRatio = aspectRatio != 0 && !(Math.Abs(aspectRatio - 1) < 0.01); + var useAspectRatio = !DNearZero(aspectRatio); // !(Math.Abs(aspectRatio) < 0.01); // ignore super-small aspect ratios or zero + + // 1. Check if we have nothing to rescale string msgWhyNoRescale = null; if (dims.Item1 == 0 && dims.Item2 == 0) msgWhyNoRescale = "w/h == 0"; - if (factor == 0f || Math.Abs(factor - 1) < 0.01) msgWhyNoRescale = "Factor is 0 or 1"; + if (DNearZero(factor) || DNearZero(factor - 1)) // Factor is 0 or 1 + { + factor = 1; // in this case we must still calculate, and should assume factor is exactly 1 + if (!useAspectRatio) msgWhyNoRescale = "Factor is 0 or 1 and no AspectRatio"; + } if (!scaleW && !scaleH) msgWhyNoRescale = "h/w shouldn't be scaled"; if (msgWhyNoRescale != null) - return wrapLog(msgWhyNoRescale + ", no changes", dims);// new Tuple(width, height)); + return wrapLog(msgWhyNoRescale + ", no changes", dims); - // Figure out height/width, as we're resizing, we respect the aspect ratio, unless there is none or height shouldn't be set + // 2. Figure out height/width, as we're resizing, we respect the aspect ratio, unless there is none or height shouldn't be set + // Width should only be calculated, if it wasn't explicitly provided (so only if coming from the settings) var newW = !scaleW ? dims.Item1 : dims.Item1 * factor; - float newH = dims.Item2; - var doScaleH = scaleH && dims.Item2 != 0; - var useAspectRatio = aspectRatio == 0 || !(Math.Abs(aspectRatio - 1) < 0.01); + + // Height should only get Aspect Ratio if the Height wasn't specifically provided + // and if useAR is true and Width is useful + var applyAspectRatio = scaleH && useAspectRatio; // && FNotNearZero(newW); + var newH = applyAspectRatio + ? newW / aspectRatio + : dims.Item2; - maybeLog.SafeAdd($"ScaleW: {scaleW}, ScaleH: {scaleH}, Really-ScaleH:{doScaleH}, Use Aspect Ratio:{useAspectRatio}"); + // Should we scale height? Only if AspectRatio wasn't applied as then the scale factor was already in there + var doScaleH = !useAspectRatio && scaleH; - if (doScaleH) newH = useAspectRatio ? newW / aspectRatio : dims.Item2 * factor; + maybeLog.SafeAdd($"ScaleW: {scaleW}, ScaleH: {scaleH}, Really-ScaleH:{doScaleH}, Use Aspect Ratio:{useAspectRatio}, Use AR on H: {applyAspectRatio}"); + + if (doScaleH) newH = dims.Item2 * factor; // new - don't check here var intW = (int)newW; @@ -183,6 +163,5 @@ internal static string CorrectFormats(string format) default: return null; } } - } }