juicysfplugin/modules/juce_graphics/fonts/juce_Font.cpp

723 lines
21 KiB
C++
Raw Normal View History

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace FontValues
{
static float limitFontHeight (const float height) noexcept
{
return jlimit (0.1f, 10000.0f, height);
}
const float defaultFontHeight = 14.0f;
float minimumHorizontalScale = 0.7f;
String fallbackFont;
String fallbackFontStyle;
}
using GetTypefaceForFont = Typeface::Ptr (*)(const Font&);
GetTypefaceForFont juce_getTypefaceForFont = nullptr;
float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; }
void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; }
//==============================================================================
class TypefaceCache : private DeletedAtShutdown
{
public:
TypefaceCache()
{
setSize (10);
}
~TypefaceCache()
{
clearSingletonInstance();
}
JUCE_DECLARE_SINGLETON (TypefaceCache, false)
void setSize (const int numToCache)
{
const ScopedWriteLock sl (lock);
faces.clear();
faces.insertMultiple (-1, CachedFace(), numToCache);
}
void clear()
{
const ScopedWriteLock sl (lock);
setSize (faces.size());
defaultFace = nullptr;
}
Typeface::Ptr findTypefaceFor (const Font& font)
{
const ScopedReadLock slr (lock);
auto faceName = font.getTypefaceName();
auto faceStyle = font.getTypefaceStyle();
jassert (faceName.isNotEmpty());
for (int i = faces.size(); --i >= 0;)
{
CachedFace& face = faces.getReference(i);
if (face.typefaceName == faceName
&& face.typefaceStyle == faceStyle
&& face.typeface != nullptr
&& face.typeface->isSuitableForFont (font))
{
face.lastUsageCount = ++counter;
return face.typeface;
}
}
const ScopedWriteLock slw (lock);
int replaceIndex = 0;
auto bestLastUsageCount = std::numeric_limits<size_t>::max();
for (int i = faces.size(); --i >= 0;)
{
auto lu = faces.getReference(i).lastUsageCount;
if (bestLastUsageCount > lu)
{
bestLastUsageCount = lu;
replaceIndex = i;
}
}
auto& face = faces.getReference (replaceIndex);
face.typefaceName = faceName;
face.typefaceStyle = faceStyle;
face.lastUsageCount = ++counter;
if (juce_getTypefaceForFont == nullptr)
face.typeface = Font::getDefaultTypefaceForFont (font);
else
face.typeface = juce_getTypefaceForFont (font);
jassert (face.typeface != nullptr); // the look and feel must return a typeface!
if (defaultFace == nullptr && font == Font())
defaultFace = face.typeface;
return face.typeface;
}
Typeface::Ptr defaultFace;
private:
struct CachedFace
{
CachedFace() noexcept {}
// Although it seems a bit wacky to store the name here, it's because it may be a
// placeholder rather than a real one, e.g. "<Sans-Serif>" vs the actual typeface name.
// Since the typeface itself doesn't know that it may have this alias, the name under
// which it was fetched needs to be stored separately.
String typefaceName, typefaceStyle;
size_t lastUsageCount = 0;
Typeface::Ptr typeface;
};
ReadWriteLock lock;
Array<CachedFace> faces;
size_t counter = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache)
};
JUCE_IMPLEMENT_SINGLETON (TypefaceCache)
void Typeface::setTypefaceCacheSize (int numFontsToCache)
{
TypefaceCache::getInstance()->setSize (numFontsToCache);
}
void (*clearOpenGLGlyphCache)() = nullptr;
void Typeface::clearTypefaceCache()
{
TypefaceCache::getInstance()->clear();
RenderingHelpers::SoftwareRendererSavedState::clearGlyphCache();
if (clearOpenGLGlyphCache != nullptr)
clearOpenGLGlyphCache();
}
//==============================================================================
class Font::SharedFontInternal : public ReferenceCountedObject
{
public:
SharedFontInternal() noexcept
: typeface (TypefaceCache::getInstance()->defaultFace),
typefaceName (Font::getDefaultSansSerifFontName()),
typefaceStyle (Font::getDefaultStyle()),
height (FontValues::defaultFontHeight)
{
}
SharedFontInternal (int styleFlags, float fontHeight) noexcept
: typefaceName (Font::getDefaultSansSerifFontName()),
typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)),
height (fontHeight),
underline ((styleFlags & underlined) != 0)
{
if (styleFlags == plain)
typeface = TypefaceCache::getInstance()->defaultFace;
}
SharedFontInternal (const String& name, int styleFlags, float fontHeight) noexcept
: typefaceName (name),
typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)),
height (fontHeight),
underline ((styleFlags & underlined) != 0)
{
if (styleFlags == plain && typefaceName.isEmpty())
typeface = TypefaceCache::getInstance()->defaultFace;
}
SharedFontInternal (const String& name, const String& style, float fontHeight) noexcept
: typefaceName (name), typefaceStyle (style), height (fontHeight)
{
if (typefaceName.isEmpty())
typefaceName = Font::getDefaultSansSerifFontName();
}
explicit SharedFontInternal (const Typeface::Ptr& face) noexcept
: typeface (face),
typefaceName (face->getName()),
typefaceStyle (face->getStyle()),
height (FontValues::defaultFontHeight)
{
jassert (typefaceName.isNotEmpty());
}
SharedFontInternal (const SharedFontInternal& other) noexcept
: ReferenceCountedObject(),
typeface (other.typeface),
typefaceName (other.typefaceName),
typefaceStyle (other.typefaceStyle),
height (other.height),
horizontalScale (other.horizontalScale),
kerning (other.kerning),
ascent (other.ascent),
underline (other.underline)
{
}
bool operator== (const SharedFontInternal& other) const noexcept
{
return height == other.height
&& underline == other.underline
&& horizontalScale == other.horizontalScale
&& kerning == other.kerning
&& typefaceName == other.typefaceName
&& typefaceStyle == other.typefaceStyle;
}
Typeface::Ptr typeface;
String typefaceName, typefaceStyle;
float height, horizontalScale = 1.0f, kerning = 0, ascent = 0;
bool underline = false;
};
//==============================================================================
Font::Font() : font (new SharedFontInternal()) {}
Font::Font (const Typeface::Ptr& typeface) : font (new SharedFontInternal (typeface)) {}
Font::Font (const Font& other) noexcept : font (other.font) {}
Font::Font (float fontHeight, int styleFlags)
: font (new SharedFontInternal (styleFlags, FontValues::limitFontHeight (fontHeight)))
{
}
Font::Font (const String& typefaceName, float fontHeight, int styleFlags)
: font (new SharedFontInternal (typefaceName, styleFlags, FontValues::limitFontHeight (fontHeight)))
{
}
Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight)
: font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight)))
{
}
Font& Font::operator= (const Font& other) noexcept
{
font = other.font;
return *this;
}
Font::Font (Font&& other) noexcept
: font (std::move (other.font))
{
}
Font& Font::operator= (Font&& other) noexcept
{
font = std::move (other.font);
return *this;
}
Font::~Font() noexcept
{
}
bool Font::operator== (const Font& other) const noexcept
{
return font == other.font
|| *font == *other.font;
}
bool Font::operator!= (const Font& other) const noexcept
{
return ! operator== (other);
}
void Font::dupeInternalIfShared()
{
if (font->getReferenceCount() > 1)
font = *new SharedFontInternal (*font);
}
void Font::checkTypefaceSuitability()
{
if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this))
font->typeface = nullptr;
}
//==============================================================================
struct FontPlaceholderNames
{
String sans { "<Sans-Serif>" },
serif { "<Serif>" },
mono { "<Monospaced>" },
regular { "<Regular>" };
};
static const FontPlaceholderNames& getFontPlaceholderNames()
{
static FontPlaceholderNames names;
return names;
}
#if JUCE_MSVC
// This is a workaround for the lack of thread-safety in MSVC's handling of function-local
// statics - if multiple threads all try to create the first Font object at the same time,
// it can cause a race-condition in creating these placeholder strings.
struct FontNamePreloader { FontNamePreloader() { getFontPlaceholderNames(); } };
static FontNamePreloader fnp;
#endif
const String& Font::getDefaultSansSerifFontName() { return getFontPlaceholderNames().sans; }
const String& Font::getDefaultSerifFontName() { return getFontPlaceholderNames().serif; }
const String& Font::getDefaultMonospacedFontName() { return getFontPlaceholderNames().mono; }
const String& Font::getDefaultStyle() { return getFontPlaceholderNames().regular; }
const String& Font::getTypefaceName() const noexcept { return font->typefaceName; }
const String& Font::getTypefaceStyle() const noexcept { return font->typefaceStyle; }
void Font::setTypefaceName (const String& faceName)
{
if (faceName != font->typefaceName)
{
jassert (faceName.isNotEmpty());
dupeInternalIfShared();
font->typefaceName = faceName;
font->typeface = nullptr;
font->ascent = 0;
}
}
void Font::setTypefaceStyle (const String& typefaceStyle)
{
if (typefaceStyle != font->typefaceStyle)
{
dupeInternalIfShared();
font->typefaceStyle = typefaceStyle;
font->typeface = nullptr;
font->ascent = 0;
}
}
Font Font::withTypefaceStyle (const String& newStyle) const
{
Font f (*this);
f.setTypefaceStyle (newStyle);
return f;
}
StringArray Font::getAvailableStyles() const
{
return findAllTypefaceStyles (getTypeface()->getName());
}
Typeface* Font::getTypeface() const
{
if (font->typeface == nullptr)
{
font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this);
jassert (font->typeface != nullptr);
}
return font->typeface.get();
}
//==============================================================================
const String& Font::getFallbackFontName()
{
return FontValues::fallbackFont;
}
void Font::setFallbackFontName (const String& name)
{
FontValues::fallbackFont = name;
#if JUCE_MAC || JUCE_IOS
jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
#endif
}
const String& Font::getFallbackFontStyle()
{
return FontValues::fallbackFontStyle;
}
void Font::setFallbackFontStyle (const String& style)
{
FontValues::fallbackFontStyle = style;
#if JUCE_MAC || JUCE_IOS
jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
#endif
}
//==============================================================================
Font Font::withHeight (const float newHeight) const
{
Font f (*this);
f.setHeight (newHeight);
return f;
}
float Font::getHeightToPointsFactor() const
{
return getTypeface()->getHeightToPointsFactor();
}
Font Font::withPointHeight (float heightInPoints) const
{
Font f (*this);
f.setHeight (heightInPoints / getHeightToPointsFactor());
return f;
}
void Font::setHeight (float newHeight)
{
newHeight = FontValues::limitFontHeight (newHeight);
if (font->height != newHeight)
{
dupeInternalIfShared();
font->height = newHeight;
checkTypefaceSuitability();
}
}
void Font::setHeightWithoutChangingWidth (float newHeight)
{
newHeight = FontValues::limitFontHeight (newHeight);
if (font->height != newHeight)
{
dupeInternalIfShared();
font->horizontalScale *= (font->height / newHeight);
font->height = newHeight;
checkTypefaceSuitability();
}
}
int Font::getStyleFlags() const noexcept
{
int styleFlags = font->underline ? underlined : plain;
if (isBold()) styleFlags |= bold;
if (isItalic()) styleFlags |= italic;
return styleFlags;
}
Font Font::withStyle (const int newFlags) const
{
Font f (*this);
f.setStyleFlags (newFlags);
return f;
}
void Font::setStyleFlags (const int newFlags)
{
if (getStyleFlags() != newFlags)
{
dupeInternalIfShared();
font->typeface = nullptr;
font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags);
font->underline = (newFlags & underlined) != 0;
font->ascent = 0;
}
}
void Font::setSizeAndStyle (float newHeight,
const int newStyleFlags,
const float newHorizontalScale,
const float newKerningAmount)
{
newHeight = FontValues::limitFontHeight (newHeight);
if (font->height != newHeight
|| font->horizontalScale != newHorizontalScale
|| font->kerning != newKerningAmount)
{
dupeInternalIfShared();
font->height = newHeight;
font->horizontalScale = newHorizontalScale;
font->kerning = newKerningAmount;
checkTypefaceSuitability();
}
setStyleFlags (newStyleFlags);
}
void Font::setSizeAndStyle (float newHeight,
const String& newStyle,
const float newHorizontalScale,
const float newKerningAmount)
{
newHeight = FontValues::limitFontHeight (newHeight);
if (font->height != newHeight
|| font->horizontalScale != newHorizontalScale
|| font->kerning != newKerningAmount)
{
dupeInternalIfShared();
font->height = newHeight;
font->horizontalScale = newHorizontalScale;
font->kerning = newKerningAmount;
checkTypefaceSuitability();
}
setTypefaceStyle (newStyle);
}
Font Font::withHorizontalScale (const float newHorizontalScale) const
{
Font f (*this);
f.setHorizontalScale (newHorizontalScale);
return f;
}
void Font::setHorizontalScale (const float scaleFactor)
{
dupeInternalIfShared();
font->horizontalScale = scaleFactor;
checkTypefaceSuitability();
}
float Font::getHorizontalScale() const noexcept
{
return font->horizontalScale;
}
float Font::getExtraKerningFactor() const noexcept
{
return font->kerning;
}
Font Font::withExtraKerningFactor (const float extraKerning) const
{
Font f (*this);
f.setExtraKerningFactor (extraKerning);
return f;
}
void Font::setExtraKerningFactor (const float extraKerning)
{
dupeInternalIfShared();
font->kerning = extraKerning;
checkTypefaceSuitability();
}
Font Font::boldened() const { return withStyle (getStyleFlags() | bold); }
Font Font::italicised() const { return withStyle (getStyleFlags() | italic); }
bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->typefaceStyle); }
bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->typefaceStyle); }
bool Font::isUnderlined() const noexcept { return font->underline; }
void Font::setBold (const bool shouldBeBold)
{
auto flags = getStyleFlags();
setStyleFlags (shouldBeBold ? (flags | bold)
: (flags & ~bold));
}
void Font::setItalic (const bool shouldBeItalic)
{
auto flags = getStyleFlags();
setStyleFlags (shouldBeItalic ? (flags | italic)
: (flags & ~italic));
}
void Font::setUnderline (const bool shouldBeUnderlined)
{
dupeInternalIfShared();
font->underline = shouldBeUnderlined;
checkTypefaceSuitability();
}
float Font::getAscent() const
{
if (font->ascent == 0.0f)
font->ascent = getTypeface()->getAscent();
return font->height * font->ascent;
}
float Font::getHeight() const noexcept { return font->height; }
float Font::getDescent() const { return font->height - getAscent(); }
float Font::getHeightInPoints() const { return getHeight() * getHeightToPointsFactor(); }
float Font::getAscentInPoints() const { return getAscent() * getHeightToPointsFactor(); }
float Font::getDescentInPoints() const { return getDescent() * getHeightToPointsFactor(); }
int Font::getStringWidth (const String& text) const
{
return (int) std::ceil (getStringWidthFloat (text));
}
float Font::getStringWidthFloat (const String& text) const
{
// This call isn't thread-safe when there's a message thread running
jassert (MessageManager::getInstanceWithoutCreating() == nullptr
|| MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
auto w = getTypeface()->getStringWidth (text);
if (font->kerning != 0.0f)
w += font->kerning * text.length();
return w * font->height * font->horizontalScale;
}
void Font::getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) const
{
// This call isn't thread-safe when there's a message thread running
jassert (MessageManager::getInstanceWithoutCreating() == nullptr
|| MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
getTypeface()->getGlyphPositions (text, glyphs, xOffsets);
if (auto num = xOffsets.size())
{
auto scale = font->height * font->horizontalScale;
auto* x = xOffsets.getRawDataPointer();
if (font->kerning != 0.0f)
{
for (int i = 0; i < num; ++i)
x[i] = (x[i] + i * font->kerning) * scale;
}
else
{
for (int i = 0; i < num; ++i)
x[i] *= scale;
}
}
}
void Font::findFonts (Array<Font>& destArray)
{
for (auto& name : findAllTypefaceNames())
{
auto styles = findAllTypefaceStyles (name);
String style ("Regular");
if (! styles.contains (style, true))
style = styles[0];
destArray.add (Font (name, style, FontValues::defaultFontHeight));
}
}
//==============================================================================
String Font::toString() const
{
String s;
if (getTypefaceName() != getDefaultSansSerifFontName())
s << getTypefaceName() << "; ";
s << String (getHeight(), 1);
if (getTypefaceStyle() != getDefaultStyle())
s << ' ' << getTypefaceStyle();
return s;
}
Font Font::fromString (const String& fontDescription)
{
const int separator = fontDescription.indexOfChar (';');
String name;
if (separator > 0)
name = fontDescription.substring (0, separator).trim();
if (name.isEmpty())
name = getDefaultSansSerifFontName();
String sizeAndStyle (fontDescription.substring (separator + 1).trimStart());
float height = sizeAndStyle.getFloatValue();
if (height <= 0)
height = 10.0f;
const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false));
return Font (name, style, height);
}
} // namespace juce