1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 #include <o3tl/safeint.hxx>
13 #include <sal/macros.h>
14 #include <sal/types.h>
20 #include <type_traits>
27 mm100
= 0, // 1/100th mm
28 mm10
, // 1/10 mm, corresponds to MapUnit::Map10thMM
33 emu
, // English Metric Unit: 1/360000 cm, 1/914400 in
34 twip
, // "Twentieth of a point" aka "dxa": 1/20 pt
36 pc
, // Pica: 1/6 in, corresponds to FieldUnit::PICA and MeasureUnit::PICA
37 in1000
, // 1/1000 in, corresponds to MapUnit::Map1000thInch
38 in100
, // 1/100 in, corresponds to MapUnit::Map100thInch
39 in10
, // 1/10 in, corresponds to MapUnit::Map10thInch
43 master
, // PPT Master Unit: 1/576 in
44 px
, // "pixel" unit: 15 twip (96 ppi), corresponds to MeasureUnit::PIXEL
45 ch
, // "char" unit: 210 twip (14 px), corresponds to FieldUnit::CHAR
46 line
, // "line" unit: 312 twip, corresponds to FieldUnit::LINE
47 count
, // <== add new units above this last entry
51 // If other categories of units would be needed (like time), a separate scoped enum
52 // should be created, respective conversion array prepared in detail namespace, and
53 // respective md(NewUnit, NewUnit) overload introduced, which would allow using
54 // o3tl::convert(), o3tl::convertSaturate() and o3tl::getConversionMulDiv() with the
55 // new category in a type-safe way, without mixing unrelated units.
61 // A special function to avoid compiler warning comparing signed and unsigned values
62 template <typename I
> constexpr bool isBetween(I n
, sal_Int64 min
, sal_Int64 max
)
64 assert(max
> 0 && min
< 0);
65 if constexpr (std::is_signed_v
<I
>)
66 return n
>= min
&& n
<= max
;
68 return n
<= sal_uInt64(max
);
71 // Ensure correct rounding for both positive and negative integers
72 template <typename I
, std::enable_if_t
<std::is_integral_v
<I
>, int> = 0>
73 constexpr sal_Int64
MulDiv(I n
, sal_Int64 m
, sal_Int64 d
)
75 assert(m
> 0 && d
> 0);
76 assert(isBetween(n
, (SAL_MIN_INT64
+ d
/ 2) / m
, (SAL_MAX_INT64
- d
/ 2) / m
));
77 return (n
>= 0 ? (n
* m
+ d
/ 2) : (n
* m
- d
/ 2)) / d
;
79 template <typename F
, std::enable_if_t
<std::is_floating_point_v
<F
>, int> = 0>
80 constexpr double MulDiv(F f
, sal_Int64 m
, sal_Int64 d
)
82 assert(m
> 0 && d
> 0);
83 return f
* (double(m
) / d
);
86 template <typename I
, std::enable_if_t
<std::is_integral_v
<I
>, int> = 0>
87 constexpr sal_Int64
MulDiv(I n
, sal_Int64 m
, sal_Int64 d
, bool& bOverflow
, sal_Int64 nDefault
)
89 if (!isBetween(n
, (SAL_MIN_INT64
+ d
/ 2) / m
, (SAL_MAX_INT64
- d
/ 2) / m
))
95 return MulDiv(n
, m
, d
);
98 template <typename I
, std::enable_if_t
<std::is_integral_v
<I
>, int> = 0>
99 constexpr sal_Int64
MulDivSaturate(I n
, sal_Int64 m
, sal_Int64 d
)
101 if (sal_Int64 d_2
= d
/ 2; !isBetween(n
, (SAL_MIN_INT64
+ d_2
) / m
, (SAL_MAX_INT64
- d_2
) / m
))
105 if (m
> d
&& std::make_unsigned_t
<I
>(n
) > sal_uInt64(SAL_MAX_INT64
/ m
* d
- d_2
))
106 return SAL_MAX_INT64
; // saturate
107 return saturating_add
<sal_uInt64
>(n
, d_2
) / d
* m
; // divide before multiplication
109 else if constexpr (std::is_signed_v
<I
>) // n < 0; don't compile for unsigned n
111 if (m
> d
&& n
< SAL_MIN_INT64
/ m
* d
+ d_2
)
112 return SAL_MIN_INT64
; // saturate
113 return saturating_sub
<sal_Int64
>(n
, d_2
) / d
* m
; // divide before multiplication
116 return MulDiv(n
, m
, d
);
119 template <class M
, class N
> constexpr std::common_type_t
<M
, N
> asserting_gcd(M m
, N n
)
121 auto ret
= std::gcd(m
, n
);
126 // Packs integral multiplier and divisor for conversion from one unit to another
129 sal_Int64 m
; // multiplier
130 sal_Int64 d
; // divisor
131 constexpr m_and_d(sal_Int64 _m
, sal_Int64 _d
)
132 : m(_m
/ asserting_gcd(_m
, _d
)) // make sure to use smallest quotients here because
133 , d(_d
/ asserting_gcd(_m
, _d
)) // they will be multiplied when building final table
135 assert(_m
> 0 && _d
> 0);
139 // Resulting static array N x N of all quotients to convert between all units. The
140 // quotients are minimal to allow largest range of converted numbers without overflow.
141 // Maybe o3tl::enumarray could be used here, but it's not constexpr yet.
142 template <int N
> constexpr auto prepareMDArray(const m_and_d (&mdBase
)[N
])
144 std::array
<std::array
<sal_Int64
, N
>, N
> a
{};
145 for (int i
= 0; i
< N
; ++i
)
148 for (int j
= 0; j
< i
; ++j
)
150 assert(mdBase
[i
].m
< SAL_MAX_INT64
/ mdBase
[j
].d
);
151 assert(mdBase
[i
].d
< SAL_MAX_INT64
/ mdBase
[j
].m
);
152 const sal_Int64 m
= mdBase
[i
].m
* mdBase
[j
].d
, d
= mdBase
[i
].d
* mdBase
[j
].m
;
153 const sal_Int64 g
= asserting_gcd(m
, d
);
161 // A generic template used for fundamental arithmetic types
162 template <typename U
> constexpr sal_Int64
md(U i
, U
/*j*/) { return i
; }
164 // Length units implementation
166 // Array of conversion quotients for mm, used to build final conversion table. Entries
167 // are { multiplier, divider } to convert respective unit *to* mm. Order of elements
168 // corresponds to order in o3tl::Length enum (Length::count and Length::invalid omitted).
169 constexpr m_and_d mdBaseLen
[] = {
170 { 1, 100 }, // mm100 => mm
171 { 1, 10 }, // mm10 => mm
172 { 1, 1 }, // mm => mm
173 { 10, 1 }, // cm => mm
174 { 1000, 1 }, // m => mm
175 { 1000000, 1 }, // km => mm
176 { 1, 36000 }, // emu => mm
177 { 254, 10 * 1440 }, // twip => mm
178 { 254, 10 * 72 }, // pt => mm
179 { 254, 10 * 6 }, // pc => mm
180 { 254, 10000 }, // in1000 => mm
181 { 254, 1000 }, // in100 => mm
182 { 254, 100 }, // in10 => mm
183 { 254, 10 }, // in => mm
184 { 254 * 12, 10 }, // ft => mm
185 { 254 * 12 * 5280, 10 }, // mi => mm
186 { 254, 10 * 576 }, // master => mm
187 { 254 * 15, 10 * 1440 }, // px => mm
188 { 254 * 210, 10 * 1440 }, // ch => mm
189 { 254 * 312, 10 * 1440 }, // line => mm
191 static_assert(std::size(mdBaseLen
) == static_cast<int>(Length::count
),
192 "mdBaseL must have an entry for each unit in o3tl::Length");
194 // The resulting multipliers and divisors array
195 constexpr auto aLengthMDArray
= prepareMDArray(mdBaseLen
);
197 // an overload taking Length
198 constexpr sal_Int64
md(Length i
, Length j
)
200 const int nI
= static_cast<int>(i
), nJ
= static_cast<int>(j
);
201 assert(nI
>= 0 && o3tl::make_unsigned(nI
) < aLengthMDArray
.size());
202 assert(nJ
>= 0 && o3tl::make_unsigned(nJ
) < aLengthMDArray
.size());
203 return aLengthMDArray
[nI
][nJ
];
206 // here might go overloads of md() taking other units ...
209 // Unchecked conversion. Takes a number value, multiplier and divisor
210 template <typename N
> constexpr auto convert(N n
, sal_Int64 mul
, sal_Int64 div
)
212 return detail::MulDiv(n
, mul
, div
);
215 // Unchecked conversion. Takes a number value and units defined in this header
216 template <typename N
, typename U
> constexpr auto convert(N n
, U from
, U to
)
218 return convert(n
, detail::md(from
, to
), detail::md(to
, from
));
221 // Convert to twips - for convenience as we do this a lot
222 template <typename N
> constexpr auto toTwips(N number
, Length from
)
224 return convert(number
, from
, Length::twip
);
227 // Returns nDefault if intermediate multiplication overflows sal_Int64 (only for integral types).
228 // On return, bOverflow indicates if overflow happened. nDefault is returned when overflow occurs.
229 template <typename N
, typename U
>
230 constexpr auto convert(N n
, U from
, U to
, bool& bOverflow
, sal_Int64 nDefault
= 0)
232 return detail::MulDiv(n
, detail::md(from
, to
), detail::md(to
, from
), bOverflow
, nDefault
);
235 // Conversion with saturation (only for integral types). For too large input returns SAL_MAX_INT64.
236 // When intermediate multiplication would overflow, but the end result is in sal_Int64 range, the
237 // precision is decreased because of inversion of multiplication and division.
238 template <typename N
, typename U
> constexpr auto convertSaturate(N n
, U from
, U to
)
240 return detail::MulDivSaturate(n
, detail::md(from
, to
), detail::md(to
, from
));
243 // Conversion with saturation (only for integral types), optimized for return types smaller than
244 // sal_Int64. In this case, it's easier to clamp input values to known bounds, than to do some
245 // preprocessing to handle too large input values, just to clamp the result anyway. Use it like:
247 // sal_Int32 n = convertNarrowing<sal_Int32, o3tl::Length::mm100, o3tl::Length::emu>(m);
248 template <typename Out
, auto from
, auto to
, typename N
,
250 std::is_integral_v
<N
> && std::is_integral_v
<Out
> && sizeof(Out
) < sizeof(sal_Int64
),
252 constexpr Out
convertNarrowing(N n
)
254 constexpr sal_Int64 nMin
= convertSaturate(std::numeric_limits
<Out
>::min(), to
, from
);
255 constexpr sal_Int64 nMax
= convertSaturate(std::numeric_limits
<Out
>::max(), to
, from
);
256 if (static_cast<sal_Int64
>(n
) > nMax
)
257 return std::numeric_limits
<Out
>::max();
258 if (static_cast<sal_Int64
>(n
) < nMin
)
259 return std::numeric_limits
<Out
>::min();
260 return convert(n
, from
, to
);
263 // Return a pair { multiplier, divisor } for a given conversion
264 template <typename U
> constexpr std::pair
<sal_Int64
, sal_Int64
> getConversionMulDiv(U from
, U to
)
266 return { detail::md(from
, to
), detail::md(to
, from
) };
270 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */