/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* font specific types shared by both thebes and layout */ #ifndef GFX_FONT_PROPERTY_TYPES_H #define GFX_FONT_PROPERTY_TYPES_H #include #include #include #include #include #include #include #include "mozilla/Assertions.h" #include "mozilla/TextUtils.h" #include "nsString.h" /* * This file is separate from gfxFont.h so that layout can include it * without bringing in gfxFont.h and everything it includes. */ namespace mozilla { /** * Generic template for font property type classes that use a fixed-point * internal representation. * Template parameters: * InternalType - the integer type to use as the internal representation (e.g. * uint16_t) * * NOTE that T must NOT be plain /int/, as that would result in * ambiguity between constructors from /int/ and /T/, which mean * different things. * FractionBits - number of bits to use for the fractional part * Min, Max - [inclusive] limits to the range of values that may be stored * Values are constructed from and exposed as floating-point, but stored * internally as fixed point, so there will be a quantization effect on * fractional values, depending on the number of fractional bits used. * Using (16-bit) fixed-point types rather than floats for these style * attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle; * it will also tend to reduce the number of distinct font instances that * get created, particularly when styles are animated or set to arbitrary * values (e.g. by sliders in the UI), which should reduce pressure on * graphics resources and improve cache hit rates. */ template class FontPropertyValue { public: // Initialize to the minimum value by default. constexpr FontPropertyValue() : FontPropertyValue(Min) {} explicit FontPropertyValue(const FontPropertyValue& aOther) = default; FontPropertyValue& operator=(const FontPropertyValue& aOther) = default; bool operator==(const FontPropertyValue& aOther) const { return mValue == aOther.mValue; } bool operator!=(const FontPropertyValue& aOther) const { return mValue != aOther.mValue; } bool operator<(const FontPropertyValue& aOther) const { return mValue < aOther.mValue; } bool operator>(const FontPropertyValue& aOther) const { return mValue > aOther.mValue; } bool operator<=(const FontPropertyValue& aOther) const { return mValue <= aOther.mValue; } bool operator>=(const FontPropertyValue& aOther) const { return mValue >= aOther.mValue; } // The difference between two values, returned as a raw floating-point number // (which might not be a valid property value in its own right). float operator-(const FontPropertyValue& aOther) const { return (mValue - aOther.mValue) * kInverseScale; } /// Return the raw internal representation, for purposes of hashing. /// (Do not try to interpret the numeric value of this.) uint16_t ForHash() const { return uint16_t(mValue); } static constexpr const float kMin = float(Min); static constexpr const float kMax = float(Max); protected: // Construct from a floating-point or integer value, checking that it is // within the allowed range and converting to fixed-point representation. explicit constexpr FontPropertyValue(float aValue) : mValue(std::round(aValue * kScale)) { MOZ_ASSERT(aValue >= kMin && aValue <= kMax); } explicit constexpr FontPropertyValue(int aValue) : mValue(static_cast(aValue * kScale)) { MOZ_ASSERT(aValue >= Min && aValue <= Max); } // Construct directly from a fixed-point value of type T, with no check; // note that there may be special "flag" values that are outside the normal // min/max range (e.g. for font-style:italic, distinct from oblique angle). explicit constexpr FontPropertyValue(InternalType aValue) : mValue(aValue) {} // This is protected as it may not be the most appropriate accessor for a // given instance to expose. It's up to each individual property to provide // public accessors that forward to this as required. float ToFloat() const { return mValue * kInverseScale; } int ToIntRounded() const { return (mValue + kPointFive) >> FractionBits; } static constexpr int kScale = 1 << FractionBits; static constexpr float kInverseScale = 1.0f / kScale; static const unsigned kFractionBits = FractionBits; // Constant representing 0.5 in the internal representation (note this // assumes that kFractionBits is greater than zero!) static const InternalType kPointFive = 1u << (kFractionBits - 1); InternalType mValue; }; /** * font-weight: range 1..1000, fractional values permitted; keywords * 'normal', 'bold' aliased to 400, 700 respectively; relative keywords * 'lighter', 'bolder' (not currently handled here). * * We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375) */ class FontWeight final : public FontPropertyValue { public: constexpr FontWeight() = default; explicit constexpr FontWeight(float aValue) : FontPropertyValue(aValue) {} /** * CSS font weights can have fractional values, but this constructor exists * for convenience when writing constants such as FontWeight(700) in code. */ explicit constexpr FontWeight(int aValue) : FontPropertyValue(aValue) {} static constexpr FontWeight Normal() { return FontWeight(kNormal); } static constexpr FontWeight Thin() { return FontWeight(kThin); } static constexpr FontWeight Bold() { return FontWeight(kBold); } bool IsNormal() const { return mValue == kNormal; } bool IsBold() const { return mValue >= kBoldThreshold; } float ToFloat() const { return FontPropertyValue::ToFloat(); } int ToIntRounded() const { return FontPropertyValue::ToIntRounded(); } typedef uint16_t InternalType; private: friend class WeightRange; explicit constexpr FontWeight(InternalType aValue) : FontPropertyValue(aValue) {} static const InternalType kNormal = 400u << kFractionBits; static const InternalType kBold = 700u << kFractionBits; static const InternalType kBoldThreshold = 600u << kFractionBits; static const InternalType kThin = 100u << kFractionBits; static const InternalType kExtraBold = 900u << kFractionBits; }; /** * font-stretch is represented as a percentage relative to 'normal'. * * css-fonts says the value must be >= 0%, and normal is 100%. Keywords * from ultra-condensed to ultra-expanded are aliased to percentages * from 50% to 200%; values outside that range are unlikely to be common, * but could occur. * * Like font-weight, we use an unsigned 10.6 fixed-point value (range * 0.0 - 1023.984375). * * We arbitrarily limit here to 1000%. (If that becomes a problem, we * could reduce the number of fractional bits and increase the limit.) */ class FontStretch final : public FontPropertyValue { public: constexpr FontStretch() = default; explicit constexpr FontStretch(float aPercent) : FontPropertyValue(aPercent) {} static constexpr FontStretch Normal() { return FontStretch(kNormal); } static constexpr FontStretch UltraCondensed() { return FontStretch(kUltraCondensed); } static constexpr FontStretch ExtraCondensed() { return FontStretch(kExtraCondensed); } static constexpr FontStretch Condensed() { return FontStretch(kCondensed); } static constexpr FontStretch SemiCondensed() { return FontStretch(kSemiCondensed); } static constexpr FontStretch SemiExpanded() { return FontStretch(kSemiExpanded); } static constexpr FontStretch Expanded() { return FontStretch(kExpanded); } static constexpr FontStretch ExtraExpanded() { return FontStretch(kExtraExpanded); } static constexpr FontStretch UltraExpanded() { return FontStretch(kUltraExpanded); } // The style system represents percentages in the 0.0..1.0 range, and // FontStretch does it in the 0.0..100.0 range. // // TODO(emilio): We should consider changing this class to deal with the same // range as the style system. static FontStretch FromStyle(float aStylePercentage) { return FontStretch(std::min(aStylePercentage * 100.0f, float(kMax))); } bool IsNormal() const { return mValue == kNormal; } float Percentage() const { return ToFloat(); } typedef uint16_t InternalType; private: friend class StretchRange; explicit constexpr FontStretch(InternalType aValue) : FontPropertyValue(aValue) {} static const InternalType kUltraCondensed = 50u << kFractionBits; static const InternalType kExtraCondensed = (62u << kFractionBits) + kPointFive; static const InternalType kCondensed = 75u << kFractionBits; static const InternalType kSemiCondensed = (87u << kFractionBits) + kPointFive; static const InternalType kNormal = 100u << kFractionBits; static const InternalType kSemiExpanded = (112u << kFractionBits) + kPointFive; static const InternalType kExpanded = 125u << kFractionBits; static const InternalType kExtraExpanded = 150u << kFractionBits; static const InternalType kUltraExpanded = 200u << kFractionBits; }; /** * font-style: normal | italic | oblique ? * values of below -90 or above 90 not permitted * - Use a signed 8.8 fixed-point value * (representable range -128.0 - 127.99609375) * - Define min value (-128.0) as meaning 'normal' * - Define max value (127.99609375) as 'italic' * - Other values represent 'oblique ' * - Note that 'oblique 0deg' is distinct from 'normal' (should it be?) */ class FontSlantStyle final : public FontPropertyValue { public: const static constexpr float kDefaultAngle = 14.0; constexpr FontSlantStyle() = default; static constexpr FontSlantStyle Normal() { return FontSlantStyle(kNormal); } static constexpr FontSlantStyle Italic() { return FontSlantStyle(kItalic); } static constexpr FontSlantStyle Oblique(float aAngle = kDefaultAngle) { return FontSlantStyle(aAngle); } // Create from a string as generated by ToString. This is for internal use // when serializing/deserializing entries for the startupcache, and is not // intended to parse arbitrary (untrusted) strings. static FontSlantStyle FromString(const char* aString) { if (strcmp(aString, "normal") == 0) { return Normal(); } if (strcmp(aString, "italic") == 0) { return Italic(); } if (mozilla::IsAsciiDigit(aString[0]) && strstr(aString, "deg")) { float angle = strtof(aString, nullptr); return Oblique(angle); } // Not recognized as an oblique angle; maybe it's from a startup-cache // created by an older version. The style field there used a simple 0/1 // for normal/italic respectively. return aString[0] == '0' ? Normal() : Italic(); } bool IsNormal() const { return mValue == kNormal; } bool IsItalic() const { return mValue == kItalic; } bool IsOblique() const { return mValue != kItalic && mValue != kNormal; } float ObliqueAngle() const { // It's not meaningful to get the oblique angle from a style that is // actually 'normal' or 'italic'. MOZ_ASSERT(IsOblique()); return ToFloat(); } /** * Write a string representation of the value to aOutString. * * NOTE that this APPENDS to the output string, it does not replace * any existing contents. */ void ToString(nsACString& aOutString) const { if (IsNormal()) { aOutString.Append("normal"); } else if (IsItalic()) { aOutString.Append("italic"); } else { aOutString.AppendPrintf("%gdeg", ObliqueAngle()); } } typedef int16_t InternalType; private: friend class SlantStyleRange; explicit constexpr FontSlantStyle(InternalType aConstant) : FontPropertyValue(aConstant) {} explicit constexpr FontSlantStyle(float aAngle) : FontPropertyValue(aAngle) {} static const InternalType kNormal = INT16_MIN; static const InternalType kItalic = INT16_MAX; }; /** * Convenience type to hold a pair representing a range of values. * * The min and max are both inclusive, so when min == max the range represents * a single value (not an empty range). */ template class FontPropertyRange { // This implementation assumes the underlying property type is a 16-bit value // (see FromScalar and AsScalar below). static_assert(sizeof(T) == 2, "FontPropertyValue should be a 16-bit type!"); public: /** * Construct a range from given minimum and maximum values (inclusive). */ FontPropertyRange(T aMin, T aMax) : mValues(aMin, aMax) { MOZ_ASSERT(aMin <= aMax); } /** * Construct a range representing a single value (min==max). */ explicit FontPropertyRange(T aValue) : mValues(aValue, aValue) {} explicit FontPropertyRange(const FontPropertyRange& aOther) = default; FontPropertyRange& operator=(const FontPropertyRange& aOther) = default; T Min() const { return mValues.first; } T Max() const { return mValues.second; } /** * Clamp the given value to this range. * * (We can't use mozilla::Clamp here because it only accepts integral types.) */ T Clamp(T aValue) const { return aValue <= Min() ? Min() : (aValue >= Max() ? Max() : aValue); } /** * Return whether the range consists of a single unique value. */ bool IsSingle() const { return Min() == Max(); } bool operator==(const FontPropertyRange& aOther) const { return mValues == aOther.mValues; } bool operator!=(const FontPropertyRange& aOther) const { return mValues != aOther.mValues; } /** * Conversion of the property range to/from a single 32-bit scalar value, * suitable for IPC serialization, hashing, caching. * * No assumptions should be made about the numeric value of the scalar. * * This depends on the underlying property type being a 16-bit value! */ typedef uint32_t ScalarType; ScalarType AsScalar() const { return (mValues.first.ForHash() << 16) | mValues.second.ForHash(); } /* * FIXME: * FromScalar is defined in each individual subclass, because I can't * persuade the compiler to accept a definition here in the template. :\ * static FontPropertyRange FromScalar(ScalarType aScalar) { return FontPropertyRange(T(typename T::InternalType(aScalar >> 16)), T(typename T::InternalType(aScalar & 0xffff))); } */ protected: std::pair mValues; }; class WeightRange : public FontPropertyRange { public: WeightRange(FontWeight aMin, FontWeight aMax) : FontPropertyRange(aMin, aMax) {} explicit WeightRange(FontWeight aWeight) : FontPropertyRange(aWeight) {} WeightRange(const WeightRange& aOther) = default; void ToString(nsACString& aOutString, const char* aDelim = "..") const { aOutString.AppendFloat(Min().ToFloat()); if (!IsSingle()) { aOutString.Append(aDelim); aOutString.AppendFloat(Max().ToFloat()); } } static WeightRange FromScalar(ScalarType aScalar) { return WeightRange(FontWeight(FontWeight::InternalType(aScalar >> 16)), FontWeight(FontWeight::InternalType(aScalar & 0xffff))); } }; class StretchRange : public FontPropertyRange { public: StretchRange(FontStretch aMin, FontStretch aMax) : FontPropertyRange(aMin, aMax) {} explicit StretchRange(FontStretch aStretch) : FontPropertyRange(aStretch) {} StretchRange(const StretchRange& aOther) = default; void ToString(nsACString& aOutString, const char* aDelim = "..") const { aOutString.AppendFloat(Min().Percentage()); if (!IsSingle()) { aOutString.Append(aDelim); aOutString.AppendFloat(Max().Percentage()); } } static StretchRange FromScalar(ScalarType aScalar) { return StretchRange( FontStretch(FontStretch::InternalType(aScalar >> 16)), FontStretch(FontStretch::InternalType(aScalar & 0xffff))); } }; class SlantStyleRange : public FontPropertyRange { public: SlantStyleRange(FontSlantStyle aMin, FontSlantStyle aMax) : FontPropertyRange(aMin, aMax) {} explicit SlantStyleRange(FontSlantStyle aStyle) : FontPropertyRange(aStyle) {} SlantStyleRange(const SlantStyleRange& aOther) = default; void ToString(nsACString& aOutString, const char* aDelim = "..") const { Min().ToString(aOutString); if (!IsSingle()) { aOutString.Append(aDelim); Max().ToString(aOutString); } } static SlantStyleRange FromScalar(ScalarType aScalar) { return SlantStyleRange( FontSlantStyle(FontSlantStyle::InternalType(aScalar >> 16)), FontSlantStyle(FontSlantStyle::InternalType(aScalar & 0xffff))); } }; } // namespace mozilla #endif // GFX_FONT_PROPERTY_TYPES_H