| /* |
| * Copyright (C) 2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #include "TextFlags.h" |
| #include <algorithm> |
| #include <tuple> |
| #include <wtf/Hasher.h> |
| #include <wtf/text/TextStream.h> |
| |
| namespace WebCore { |
| |
| // Unclamped, unchecked, signed fixed-point number representing a value used for font variations. |
| // Sixteen bits in total, one sign bit, two fractional bits, smallest positive value is 0.25, |
| // maximum value is 8191.75, and minimum value is -8192. |
| class FontSelectionValue { |
| public: |
| using BackingType = int16_t; |
| |
| FontSelectionValue() = default; |
| |
| // Explicit because it won't work correctly for values outside the representable range. |
| explicit constexpr FontSelectionValue(int); |
| |
| // Explicit because it won't work correctly for values outside the representable range and because precision can be lost. |
| explicit constexpr FontSelectionValue(float); |
| |
| // Precision can be lost, but value will be clamped to the representable range. |
| static constexpr FontSelectionValue clampFloat(float); |
| |
| // Since floats have 23 mantissa bits, every value can be represented losslessly. |
| constexpr operator float() const; |
| |
| static constexpr FontSelectionValue maximumValue(); |
| static constexpr FontSelectionValue minimumValue(); |
| |
| friend constexpr FontSelectionValue operator+(FontSelectionValue, FontSelectionValue); |
| friend constexpr FontSelectionValue operator-(FontSelectionValue, FontSelectionValue); |
| friend constexpr FontSelectionValue operator*(FontSelectionValue, FontSelectionValue); |
| friend constexpr FontSelectionValue operator/(FontSelectionValue, FontSelectionValue); |
| friend constexpr FontSelectionValue operator-(FontSelectionValue); |
| |
| constexpr BackingType rawValue() const { return m_backing; } |
| |
| template<class Encoder> |
| void encode(Encoder&) const; |
| |
| template<class Decoder> |
| static std::optional<FontSelectionValue> decode(Decoder&); |
| |
| private: |
| enum class RawTag { RawTag }; |
| constexpr FontSelectionValue(int, RawTag); |
| |
| static constexpr int fractionalEntropy = 4; |
| BackingType m_backing { 0 }; |
| }; |
| |
| template<class Encoder> |
| void FontSelectionValue::encode(Encoder& encoder) const |
| { |
| encoder << m_backing; |
| } |
| |
| template<class Decoder> |
| std::optional<FontSelectionValue> FontSelectionValue::decode(Decoder& decoder) |
| { |
| std::optional<FontSelectionValue::BackingType> backing; |
| decoder >> backing; |
| if (!backing) |
| return std::nullopt; |
| |
| FontSelectionValue result; |
| result.m_backing = *backing; |
| return result; |
| } |
| |
| constexpr FontSelectionValue::FontSelectionValue(int x) |
| : m_backing(x * fractionalEntropy) |
| { |
| // FIXME: Should we assert the passed in value was in range? |
| } |
| |
| constexpr FontSelectionValue::FontSelectionValue(float x) |
| : m_backing(x * fractionalEntropy) |
| { |
| // FIXME: Should we assert the passed in value was in range? |
| } |
| |
| constexpr FontSelectionValue::operator float() const |
| { |
| return m_backing / static_cast<float>(fractionalEntropy); |
| } |
| |
| constexpr FontSelectionValue FontSelectionValue::maximumValue() |
| { |
| return { std::numeric_limits<BackingType>::max(), RawTag::RawTag }; |
| } |
| |
| constexpr FontSelectionValue FontSelectionValue::minimumValue() |
| { |
| return { std::numeric_limits<BackingType>::min(), RawTag::RawTag }; |
| } |
| |
| constexpr FontSelectionValue FontSelectionValue::clampFloat(float value) |
| { |
| return FontSelectionValue { std::max<float>(minimumValue(), std::min<float>(value, maximumValue())) }; |
| } |
| |
| constexpr FontSelectionValue::FontSelectionValue(int rawValue, RawTag) |
| : m_backing(rawValue) |
| { |
| } |
| |
| constexpr FontSelectionValue operator+(FontSelectionValue a, FontSelectionValue b) |
| { |
| return { a.m_backing + b.m_backing, FontSelectionValue::RawTag::RawTag }; |
| } |
| |
| constexpr FontSelectionValue operator-(FontSelectionValue a, FontSelectionValue b) |
| { |
| return { a.m_backing - b.m_backing, FontSelectionValue::RawTag::RawTag }; |
| } |
| |
| constexpr FontSelectionValue operator*(FontSelectionValue a, FontSelectionValue b) |
| { |
| return { a.m_backing * b.m_backing / FontSelectionValue::fractionalEntropy, FontSelectionValue::RawTag::RawTag }; |
| } |
| |
| constexpr FontSelectionValue operator/(FontSelectionValue a, FontSelectionValue b) |
| { |
| return { a.m_backing * FontSelectionValue::fractionalEntropy / b.m_backing, FontSelectionValue::RawTag::RawTag }; |
| } |
| |
| constexpr FontSelectionValue operator-(FontSelectionValue value) |
| { |
| return { -value.m_backing, FontSelectionValue::RawTag::RawTag }; |
| } |
| |
| constexpr bool operator==(FontSelectionValue a, FontSelectionValue b) |
| { |
| return a.rawValue() == b.rawValue(); |
| } |
| |
| constexpr bool operator!=(FontSelectionValue a, FontSelectionValue b) |
| { |
| return a.rawValue() != b.rawValue(); |
| } |
| |
| constexpr bool operator<(FontSelectionValue a, FontSelectionValue b) |
| { |
| return a.rawValue() < b.rawValue(); |
| } |
| |
| constexpr bool operator<=(FontSelectionValue a, FontSelectionValue b) |
| { |
| return a.rawValue() <= b.rawValue(); |
| } |
| |
| constexpr bool operator>(FontSelectionValue a, FontSelectionValue b) |
| { |
| return a.rawValue() > b.rawValue(); |
| } |
| |
| constexpr bool operator>=(FontSelectionValue a, FontSelectionValue b) |
| { |
| return a.rawValue() >= b.rawValue(); |
| } |
| |
| constexpr FontSelectionValue italicThreshold() |
| { |
| return FontSelectionValue { 20 }; |
| } |
| |
| constexpr bool isItalic(std::optional<FontSelectionValue> fontWeight) |
| { |
| return fontWeight && fontWeight.value() >= italicThreshold(); |
| } |
| |
| constexpr FontSelectionValue normalItalicValue() |
| { |
| return FontSelectionValue { 0 }; |
| } |
| |
| constexpr FontSelectionValue italicValue() |
| { |
| return FontSelectionValue { 20 }; |
| } |
| |
| constexpr FontSelectionValue boldThreshold() |
| { |
| return FontSelectionValue { 600 }; |
| } |
| |
| constexpr FontSelectionValue boldWeightValue() |
| { |
| return FontSelectionValue { 700 }; |
| } |
| |
| constexpr FontSelectionValue normalWeightValue() |
| { |
| return FontSelectionValue { 400 }; |
| } |
| |
| constexpr FontSelectionValue lightWeightValue() |
| { |
| return FontSelectionValue { 200 }; |
| } |
| |
| constexpr bool isFontWeightBold(FontSelectionValue fontWeight) |
| { |
| return fontWeight >= boldThreshold(); |
| } |
| |
| constexpr FontSelectionValue lowerWeightSearchThreshold() |
| { |
| return FontSelectionValue { 400 }; |
| } |
| |
| constexpr FontSelectionValue upperWeightSearchThreshold() |
| { |
| return FontSelectionValue { 500 }; |
| } |
| |
| constexpr FontSelectionValue ultraCondensedStretchValue() |
| { |
| return FontSelectionValue { 50 }; |
| } |
| |
| constexpr FontSelectionValue extraCondensedStretchValue() |
| { |
| return FontSelectionValue { 62.5f }; |
| } |
| |
| constexpr FontSelectionValue condensedStretchValue() |
| { |
| return FontSelectionValue { 75 }; |
| } |
| |
| constexpr FontSelectionValue semiCondensedStretchValue() |
| { |
| return FontSelectionValue { 87.5f }; |
| } |
| |
| constexpr FontSelectionValue normalStretchValue() |
| { |
| return FontSelectionValue { 100 }; |
| } |
| |
| constexpr FontSelectionValue semiExpandedStretchValue() |
| { |
| return FontSelectionValue { 112.5f }; |
| } |
| |
| constexpr FontSelectionValue expandedStretchValue() |
| { |
| return FontSelectionValue { 125 }; |
| } |
| |
| constexpr FontSelectionValue extraExpandedStretchValue() |
| { |
| return FontSelectionValue { 150 }; |
| } |
| |
| constexpr FontSelectionValue ultraExpandedStretchValue() |
| { |
| return FontSelectionValue { 200 }; |
| } |
| |
| inline void add(Hasher& hasher, const FontSelectionValue& value) |
| { |
| add(hasher, value.rawValue()); |
| } |
| |
| // [Inclusive, Inclusive] |
| struct FontSelectionRange { |
| using Value = FontSelectionValue; |
| |
| constexpr FontSelectionRange(Value minimum, Value maximum) |
| : minimum(minimum) |
| , maximum(maximum) |
| { |
| } |
| |
| explicit constexpr FontSelectionRange(Value value) |
| : minimum(value) |
| , maximum(value) |
| { |
| } |
| |
| constexpr bool operator==(const FontSelectionRange& other) const |
| { |
| return WTF::tie(minimum, maximum) == WTF::tie(other.minimum, other.maximum); |
| } |
| |
| constexpr bool isValid() const |
| { |
| return minimum <= maximum; |
| } |
| |
| void expand(const FontSelectionRange& other) |
| { |
| ASSERT(other.isValid()); |
| if (!isValid()) |
| *this = other; |
| else { |
| minimum = std::min(minimum, other.minimum); |
| maximum = std::max(maximum, other.maximum); |
| } |
| ASSERT(isValid()); |
| } |
| |
| constexpr bool includes(Value target) const |
| { |
| return target >= minimum && target <= maximum; |
| } |
| |
| template<class Encoder> |
| void encode(Encoder&) const; |
| |
| template<class Decoder> |
| static std::optional<FontSelectionRange> decode(Decoder&); |
| |
| Value minimum { 1 }; |
| Value maximum { 0 }; |
| }; |
| |
| template<class Encoder> |
| void FontSelectionRange::encode(Encoder& encoder) const |
| { |
| encoder << minimum; |
| encoder << maximum; |
| } |
| |
| template<class Decoder> |
| std::optional<FontSelectionRange> FontSelectionRange::decode(Decoder& decoder) |
| { |
| std::optional<FontSelectionRange::Value> minimum; |
| decoder >> minimum; |
| if (!minimum) |
| return std::nullopt; |
| |
| std::optional<FontSelectionRange::Value> maximum; |
| decoder >> maximum; |
| if (!maximum) |
| return std::nullopt; |
| |
| return {{ *minimum, *maximum }}; |
| } |
| |
| inline void add(Hasher& hasher, const FontSelectionRange& range) |
| { |
| add(hasher, range.minimum, range.maximum); |
| } |
| |
| struct FontSelectionRequest { |
| using Value = FontSelectionValue; |
| |
| Value weight; |
| Value width; |
| |
| // FIXME: We are using an optional here to be able to distinguish between an explicit |
| // or implicit slope (for "italic" and "oblique") and the "normal" value which has no |
| // slope. The "italic" and "oblique" values can be distinguished by looking at the |
| // "fontStyleAxis" on the FontDescription. We should come up with a tri-state member |
| // so that it's a lot clearer whether we're dealing with a "normal", "italic" or explicit |
| // "oblique" font style. See webkit.org/b/187774. |
| std::optional<Value> slope; |
| |
| std::tuple<Value, Value, std::optional<Value>> tied() const |
| { |
| return WTF::tie(weight, width, slope); |
| } |
| }; |
| |
| inline TextStream& operator<<(TextStream& ts, const FontSelectionValue& fontSelectionValue) |
| { |
| ts << TextStream::FormatNumberRespectingIntegers(fontSelectionValue.rawValue()); |
| return ts; |
| } |
| |
| inline TextStream& operator<<(TextStream& ts, const std::optional<FontSelectionValue>& optionalFontSelectionValue) |
| { |
| ts << optionalFontSelectionValue.value_or(normalItalicValue()); |
| return ts; |
| } |
| |
| inline bool operator==(const FontSelectionRequest& a, const FontSelectionRequest& b) |
| { |
| return a.tied() == b.tied(); |
| } |
| |
| inline bool operator!=(const FontSelectionRequest& a, const FontSelectionRequest& b) |
| { |
| return !(a == b); |
| } |
| |
| inline void add(Hasher& hasher, const FontSelectionRequest& request) |
| { |
| add(hasher, request.tied()); |
| } |
| |
| struct FontSelectionCapabilities { |
| using Range = FontSelectionRange; |
| |
| constexpr std::tuple<Range, Range, Range> tied() const |
| { |
| return WTF::tie(weight, width, slope); |
| } |
| |
| void expand(const FontSelectionCapabilities& capabilities) |
| { |
| weight.expand(capabilities.weight); |
| width.expand(capabilities.width); |
| slope.expand(capabilities.slope); |
| } |
| |
| Range weight { normalWeightValue() }; |
| Range width { normalStretchValue() }; |
| Range slope { normalItalicValue() }; |
| }; |
| |
| constexpr bool operator==(const FontSelectionCapabilities& a, const FontSelectionCapabilities& b) |
| { |
| return a.tied() == b.tied(); |
| } |
| |
| constexpr bool operator!=(const FontSelectionCapabilities& a, const FontSelectionCapabilities& b) |
| { |
| return !(a == b); |
| } |
| |
| struct FontSelectionSpecifiedCapabilities { |
| using Capabilities = FontSelectionCapabilities; |
| using Range = FontSelectionRange; |
| using OptionalRange = std::optional<Range>; |
| |
| constexpr Capabilities computeFontSelectionCapabilities() const |
| { |
| return { computeWeight(), computeWidth(), computeSlope() }; |
| } |
| |
| constexpr std::tuple<OptionalRange&, OptionalRange&, OptionalRange&> tied() |
| { |
| return WTF::tie(weight, width, slope); |
| } |
| |
| constexpr std::tuple<const OptionalRange&, const OptionalRange&, const OptionalRange&> tied() const |
| { |
| return WTF::tie(weight, width, slope); |
| } |
| |
| FontSelectionSpecifiedCapabilities& operator=(const Capabilities& other) |
| { |
| tied() = other.tied(); |
| return *this; |
| } |
| |
| constexpr Range computeWeight() const |
| { |
| return weight.value_or(Range { normalWeightValue() }); |
| } |
| |
| constexpr Range computeWidth() const |
| { |
| return width.value_or(Range { normalStretchValue() }); |
| } |
| |
| constexpr Range computeSlope() const |
| { |
| return slope.value_or(Range { normalItalicValue() }); |
| } |
| |
| template<class Encoder> |
| void encode(Encoder&) const; |
| |
| template<class Decoder> |
| static std::optional<FontSelectionSpecifiedCapabilities> decode(Decoder&); |
| |
| OptionalRange weight; |
| OptionalRange width; |
| OptionalRange slope; |
| }; |
| |
| template<class Encoder> |
| void FontSelectionSpecifiedCapabilities::encode(Encoder& encoder) const |
| { |
| encoder << weight; |
| encoder << width; |
| encoder << slope; |
| } |
| |
| template<class Decoder> |
| std::optional<FontSelectionSpecifiedCapabilities> FontSelectionSpecifiedCapabilities::decode(Decoder& decoder) |
| { |
| std::optional<OptionalRange> weight; |
| decoder >> weight; |
| if (!weight) |
| return std::nullopt; |
| |
| std::optional<OptionalRange> width; |
| decoder >> width; |
| if (!width) |
| return std::nullopt; |
| |
| std::optional<OptionalRange> slope; |
| decoder >> slope; |
| if (!slope) |
| return std::nullopt; |
| |
| return {{ *weight, *width, *slope }}; |
| } |
| |
| constexpr bool operator==(const FontSelectionSpecifiedCapabilities& a, const FontSelectionSpecifiedCapabilities& b) |
| { |
| return a.tied() == b.tied(); |
| } |
| |
| constexpr bool operator!=(const FontSelectionSpecifiedCapabilities& a, const FontSelectionSpecifiedCapabilities& b) |
| { |
| return !(a == b); |
| } |
| |
| class FontSelectionAlgorithm { |
| public: |
| using Capabilities = FontSelectionCapabilities; |
| |
| FontSelectionAlgorithm() = delete; |
| FontSelectionAlgorithm(FontSelectionRequest, const Vector<Capabilities>&, std::optional<Capabilities> capabilitiesBounds = std::nullopt); |
| |
| struct DistanceResult { |
| FontSelectionValue distance; |
| FontSelectionValue value; |
| }; |
| DistanceResult stretchDistance(Capabilities) const; |
| DistanceResult styleDistance(Capabilities) const; |
| DistanceResult weightDistance(Capabilities) const; |
| |
| size_t indexOfBestCapabilities(); |
| |
| private: |
| using DistanceFunction = DistanceResult (FontSelectionAlgorithm::*)(Capabilities) const; |
| using CapabilitiesRange = FontSelectionRange Capabilities::*; |
| FontSelectionValue bestValue(const bool eliminated[], DistanceFunction) const; |
| void filterCapability(bool eliminated[], DistanceFunction, CapabilitiesRange); |
| |
| FontSelectionRequest m_request; |
| Capabilities m_capabilitiesBounds; |
| const Vector<Capabilities>& m_capabilities; |
| }; |
| |
| } |