summaryrefslogtreecommitdiffstats
path: root/include/tools
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2021-02-05 19:35:14 +0300
committerMike Kaganski <mike.kaganski@collabora.com>2021-02-06 17:05:44 +0100
commit956c09ca7d690471f62e8e2e14ad04fefcebf7e7 (patch)
treec681ace388370acf5842c73d45e9e9d8880777af /include/tools
parentUpdate git submodules (diff)
downloadcore-956c09ca7d690471f62e8e2e14ad04fefcebf7e7.tar.gz
core-956c09ca7d690471f62e8e2e14ad04fefcebf7e7.zip
Use more conversion functions from <tools/UnitConversion.hxx>
This unifies conversion functions to provide both floating-point and integral overloads, with correct rounding for the latter. Also sanitizing code it templatized to allow reuse if needed. Change-Id: Ibe1c9fe4d5baa226c600445779dbaf7dc41a02cb Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110487 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'include/tools')
-rw-r--r--include/tools/UnitConversion.hxx66
1 files changed, 40 insertions, 26 deletions
diff --git a/include/tools/UnitConversion.hxx b/include/tools/UnitConversion.hxx
index 57f8e39119f0..7aac3f36ebd0 100644
--- a/include/tools/UnitConversion.hxx
+++ b/include/tools/UnitConversion.hxx
@@ -12,46 +12,60 @@
#include <sal/types.h>
#include <cassert>
-#include <limits>
+#include <type_traits>
-constexpr sal_Int64 convertTwipToMm100(sal_Int64 n)
+template <typename I> constexpr bool isBetween(I n, sal_Int64 min, sal_Int64 max)
{
- assert(n < std::numeric_limits<sal_Int64>::max() / 127
- && n > std::numeric_limits<sal_Int64>::min() / 127);
- return (n >= 0) ? (n * 127 + 36) / 72 : (n * 127 - 36) / 72;
+ assert(max > 0 && min < 0);
+ if constexpr (std::is_signed_v<I>)
+ return n >= min && n <= max;
+ else
+ return n <= sal_uInt64(max);
}
-constexpr sal_Int64 convertMm100ToTwip(sal_Int64 n)
+constexpr int actualMul(int m, int d) { return (m % d == 0) ? m / d : (d % m == 0) ? 1 : m; }
+constexpr int actualDiv(int m, int d) { return (m % d == 0) ? 1 : (d % m == 0) ? d / m : d; }
+
+// Ensure correct rounding for both positive and negative integers
+template <int mul, int div, typename I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
+constexpr sal_Int64 MulDiv(I n)
{
- assert(n < std::numeric_limits<sal_Int64>::max() / 72
- && n > std::numeric_limits<sal_Int64>::min() / 72);
- return (n >= 0) ? (n * 72 + 63) / 127 : (n * 72 - 63) / 127;
+ static_assert(mul > 0 && div > 0);
+ constexpr int m = actualMul(mul, div), d = actualDiv(mul, div);
+ assert(isBetween(n, (SAL_MIN_INT64 + d / 2) / m, (SAL_MAX_INT64 - d / 2) / m));
+ return (n >= 0 ? (sal_Int64(n) * m + d / 2) : (sal_Int64(n) * m - d / 2)) / d;
}
-
-constexpr sal_Int64 sanitiseMm100ToTwip(sal_Int64 n)
+template <int mul, int div, typename F, std::enable_if_t<std::is_floating_point_v<F>, int> = 0>
+constexpr double MulDiv(F f)
{
- if (n >= std::numeric_limits<sal_Int64>::max() / 72
- || n <= std::numeric_limits<sal_Int64>::min() / 72)
- return n / 127 * 72; // do without correction; can not overflow here
- else
- return convertMm100ToTwip(n);
+ static_assert(mul > 0 && div > 0);
+ return f * (double(mul) / div);
}
-constexpr sal_Int64 convertPointToTwip(sal_Int64 nNumber) { return nNumber * 20; }
-
-constexpr sal_Int64 convertPointToMm100(sal_Int64 nNumber)
+template <int mul, int div, typename I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
+constexpr sal_Int64 sanitizeMulDiv(I n)
{
- return convertTwipToMm100(convertPointToTwip(nNumber));
+ constexpr int m = actualMul(mul, div), d = actualDiv(mul, div);
+ if constexpr (m > d)
+ if (!isBetween(n, SAL_MIN_INT64 / m * d + d / 2, SAL_MAX_INT64 / m * d - d / 2))
+ return n > 0 ? SAL_MAX_INT64 : SAL_MIN_INT64; // saturate
+ if (!isBetween(n, (SAL_MIN_INT64 + d / 2) / m, (SAL_MAX_INT64 - d / 2) / m))
+ return (n >= 0 ? n + d / 2 : n - d / 2) / d * m; // divide before multiplication
+ return MulDiv<mul, div>(n);
}
-constexpr double convertPointToTwip(double fNumber) { return fNumber * 20.0; }
+template <typename N> constexpr auto convertTwipToMm100(N n) { return MulDiv<127, 72>(n); }
+template <typename N> constexpr auto convertMm100ToTwip(N n) { return MulDiv<72, 127>(n); }
+
+constexpr sal_Int64 sanitiseMm100ToTwip(sal_Int64 n) { return sanitizeMulDiv<72, 127>(n); }
-constexpr double convertPointToMm100(double fNumber) { return fNumber * (2540.0 / 72.0); }
+template <typename N> constexpr auto convertPointToTwip(N n) { return MulDiv<20, 1>(n); }
-// Convert PPT's "master unit" (1/576 inch) to mm/100
-constexpr sal_Int64 convertMasterUnitToMm100(sal_Int64 n) { return n * (2540.0 / 576.0); }
+template <typename N> constexpr auto convertPointToMm100(N n) { return MulDiv<2540, 72>(n); }
+template <typename N> constexpr auto convertMm100ToPoint(N n) { return MulDiv<72, 2540>(n); }
-// Convert mm/100 to PPT's "master unit"
-constexpr sal_Int64 convertMm100ToMasterUnit(sal_Int64 n) { return n / (2540.0 / 576.0); }
+// PPT's "master unit" (1/576 inch) <=> mm/100
+template <typename N> constexpr auto convertMasterUnitToMm100(N n) { return MulDiv<2540, 576>(n); }
+template <typename N> constexpr auto convertMm100ToMasterUnit(N n) { return MulDiv<576, 2540>(n); }
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */