@@ -385,7 +385,7 @@ bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) | |||||
return false; | return false; | ||||
} | } | ||||
EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) | |||||
EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) | |||||
{ | { | ||||
if (const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true)) | if (const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true)) | ||||
{ | { | ||||
@@ -399,7 +399,7 @@ EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTr | |||||
const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); | const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); | ||||
if (fallbackTypeface != nullptr && fallbackTypeface != this) | if (fallbackTypeface != nullptr && fallbackTypeface != this) | ||||
return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform); | |||||
return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform, fontHeight); | |||||
} | } | ||||
return nullptr; | return nullptr; | ||||
@@ -119,7 +119,7 @@ public: | |||||
float getStringWidth (const String&) override; | float getStringWidth (const String&) override; | ||||
void getGlyphPositions (const String&, Array <int>& glyphs, Array<float>& xOffsets) override; | void getGlyphPositions (const String&, Array <int>& glyphs, Array<float>& xOffsets) override; | ||||
bool getOutlineForGlyph (int glyphNumber, Path&) override; | bool getOutlineForGlyph (int glyphNumber, Path&) override; | ||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&) override; | |||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&, float fontHeight) override; | |||||
protected: | protected: | ||||
//============================================================================== | //============================================================================== | ||||
@@ -116,13 +116,17 @@ Typeface::Ptr Typeface::getFallbackTypeface() | |||||
return fallbackFont.getTypeface(); | return fallbackFont.getTypeface(); | ||||
} | } | ||||
EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) | |||||
EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) | |||||
{ | { | ||||
Path path; | Path path; | ||||
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) | if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) | ||||
{ | |||||
applyVerticalHintingTransform (fontHeight, path); | |||||
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), | return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), | ||||
path, transform); | path, transform); | ||||
} | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
@@ -131,84 +135,78 @@ EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransfor | |||||
struct Typeface::HintingParams | struct Typeface::HintingParams | ||||
{ | { | ||||
HintingParams (Typeface& t) | HintingParams (Typeface& t) | ||||
: top (0), middle (0), bottom (0) | |||||
: cachedSize (0), top (0), middle (0), bottom (0) | |||||
{ | { | ||||
Font font (&t); | Font font (&t); | ||||
font = font.withHeight ((float) standardHeight); | font = font.withHeight ((float) standardHeight); | ||||
top = getAverageY (font, "BDEFPRTZOQC", true); | |||||
top = getAverageY (font, "BDEFPRTZOQ", true); | |||||
middle = getAverageY (font, "acegmnopqrsuvwxy", true); | middle = getAverageY (font, "acegmnopqrsuvwxy", true); | ||||
bottom = getAverageY (font, "BDELZOC", false); | bottom = getAverageY (font, "BDELZOC", false); | ||||
} | } | ||||
AffineTransform getVerticalHintingTransform (float fontSize) noexcept | |||||
void applyVerticalHintingTransform (float fontSize, Path& path) | |||||
{ | { | ||||
if (cachedSize == fontSize) | |||||
return cachedTransform; | |||||
const float t = fontSize * top; | |||||
const float m = fontSize * middle; | |||||
const float b = fontSize * bottom; | |||||
if (b < t + 2.0f) | |||||
return AffineTransform(); | |||||
Scaling s[] = { Scaling (t, m, b, 0.0f, 0.0f), | |||||
Scaling (t, m, b, 1.0f, 0.0f), | |||||
Scaling (t, m, b, 0.0f, 1.0f), | |||||
Scaling (t, m, b, 1.0f, 1.0f) }; | |||||
if (cachedSize != fontSize) | |||||
{ | |||||
cachedSize = fontSize; | |||||
cachedScale = Scaling (top, middle, bottom, fontSize); | |||||
} | |||||
int best = 0; | |||||
if (bottom < top + 3.0f / fontSize) | |||||
return; | |||||
for (int i = 1; i < numElementsInArray (s); ++i) | |||||
if (s[i].drift < s[best].drift) | |||||
best = i; | |||||
Path result; | |||||
cachedSize = fontSize; | |||||
for (Path::Iterator i (path); i.next();) | |||||
{ | |||||
switch (i.elementType) | |||||
{ | |||||
case Path::Iterator::startNewSubPath: result.startNewSubPath (i.x1, cachedScale.apply (i.y1)); break; | |||||
case Path::Iterator::lineTo: result.lineTo (i.x1, cachedScale.apply (i.y1)); break; | |||||
case Path::Iterator::quadraticTo: result.quadraticTo (i.x1, cachedScale.apply (i.y1), | |||||
i.x2, cachedScale.apply (i.y2)); break; | |||||
case Path::Iterator::cubicTo: result.cubicTo (i.x1, cachedScale.apply (i.y1), | |||||
i.x2, cachedScale.apply (i.y2), | |||||
i.x3, cachedScale.apply (i.y3)); break; | |||||
case Path::Iterator::closePath: result.closeSubPath(); break; | |||||
default: jassertfalse; break; | |||||
} | |||||
} | |||||
AffineTransform result (s[best].getTransform()); | |||||
cachedTransform = result; | |||||
return result; | |||||
result.swapWithPath (path); | |||||
} | } | ||||
private: | private: | ||||
float cachedSize; | |||||
AffineTransform cachedTransform; | |||||
struct Scaling | struct Scaling | ||||
{ | { | ||||
Scaling (float t, float m, float b, float direction1, float direction2) noexcept | |||||
{ | |||||
float newT = std::floor (t) + direction1; | |||||
float newB = std::floor (b) + direction2; | |||||
float newM = newT + (newB - newT) * (m - t) / (b - t); | |||||
Scaling() noexcept : middle(), upperScale(), upperOffset(), lowerScale(), lowerOffset() {} | |||||
float middleOffset = newM - std::floor (newM); | |||||
float nudge = middleOffset < 0.5f ? (middleOffset * -0.2f) : ((1.0f - middleOffset) * 0.2f); | |||||
newT += nudge; | |||||
newB += nudge; | |||||
scale = (newB - newT) / (b - t); | |||||
offset = (newB / scale) - b; | |||||
Scaling (float t, float m, float b, float fontSize) noexcept : middle (m) | |||||
{ | |||||
const float newT = std::floor (fontSize * t + 0.5f) / fontSize; | |||||
const float newB = std::floor (fontSize * b + 0.5f) / fontSize; | |||||
const float newM = std::floor (fontSize * m + 0.5f) / fontSize; | |||||
drift = getDrift (t) + getDrift (m) + getDrift (b) + offset + 20.0f * std::abs (scale - 1.0f); | |||||
} | |||||
upperScale = jlimit (0.9f, 1.1f, (newM - newT) / (m - t)); | |||||
lowerScale = jlimit (0.9f, 1.1f, (newB - newM) / (b - m)); | |||||
AffineTransform getTransform() const noexcept | |||||
{ | |||||
return AffineTransform::translation (0, offset).scaled (1.0f, scale); | |||||
upperOffset = newM - m * upperScale; | |||||
lowerOffset = newB - b * lowerScale; | |||||
} | } | ||||
float getDrift (float n) const noexcept | |||||
float apply (float y) const noexcept | |||||
{ | { | ||||
n = (n + offset) * scale; | |||||
const float diff = n - std::floor (n); | |||||
return jmin (diff, 1.0f - diff); | |||||
return y < middle ? (y * upperScale + upperOffset) | |||||
: (y * lowerScale + lowerOffset); | |||||
} | } | ||||
float offset, scale, drift; | |||||
float middle, upperScale, upperOffset, lowerScale, lowerOffset; | |||||
}; | }; | ||||
float cachedSize; | |||||
Scaling cachedScale; | |||||
static float getAverageY (const Font& font, const char* chars, bool getTop) | static float getAverageY (const Font& font, const char* chars, bool getTop) | ||||
{ | { | ||||
GlyphArrangement ga; | GlyphArrangement ga; | ||||
@@ -248,15 +246,15 @@ private: | |||||
float top, middle, bottom; | float top, middle, bottom; | ||||
}; | }; | ||||
AffineTransform Typeface::getVerticalHintingTransform (float fontSize) | |||||
void Typeface::applyVerticalHintingTransform (float fontSize, Path& path) | |||||
{ | { | ||||
if (fontSize < 3.0f || fontSize > 25.0f) | |||||
return AffineTransform(); | |||||
ScopedLock sl (hintingLock); | |||||
if (fontSize > 3.0f && fontSize < 25.0f) | |||||
{ | |||||
ScopedLock sl (hintingLock); | |||||
if (hintingParams == nullptr) | |||||
hintingParams = new HintingParams (*this); | |||||
if (hintingParams == nullptr) | |||||
hintingParams = new HintingParams (*this); | |||||
return hintingParams->getVerticalHintingTransform (fontSize); | |||||
return hintingParams->applyVerticalHintingTransform (fontSize, path); | |||||
} | |||||
} | } |
@@ -117,7 +117,7 @@ public: | |||||
virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; | virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; | ||||
/** Returns a new EdgeTable that contains the path for the givem glyph, with the specified transform applied. */ | /** Returns a new EdgeTable that contains the path for the givem glyph, with the specified transform applied. */ | ||||
virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform); | |||||
virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight); | |||||
/** Returns true if the typeface uses hinting. */ | /** Returns true if the typeface uses hinting. */ | ||||
virtual bool isHinted() const { return false; } | virtual bool isHinted() const { return false; } | ||||
@@ -134,10 +134,11 @@ public: | |||||
*/ | */ | ||||
static void scanFolderForFonts (const File& folder); | static void scanFolderForFonts (const File& folder); | ||||
/** Makes an attempt at estimating a good overall transform that will scale a font of | |||||
the given size to align vertically with the pixel grid. | |||||
/** Makes an attempt at performing a good overall distortion that will scale a font of | |||||
the given size to align vertically with the pixel grid. The path should be an unscaled | |||||
(i.e. normalised to height of 1.0) path for a glyph. | |||||
*/ | */ | ||||
AffineTransform getVerticalHintingTransform (float fontHeight); | |||||
void applyVerticalHintingTransform (float fontHeight, Path& path); | |||||
protected: | protected: | ||||
//============================================================================== | //============================================================================== | ||||
@@ -284,8 +284,8 @@ public: | |||||
const float fontHeight = font.getHeight(); | const float fontHeight = font.getHeight(); | ||||
edgeTable = typeface->getEdgeTableForGlyph (glyphNumber, | edgeTable = typeface->getEdgeTableForGlyph (glyphNumber, | ||||
AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) | |||||
.followedBy (font.getTypeface()->getVerticalHintingTransform (fontHeight))); | |||||
AffineTransform::scale (fontHeight * font.getHorizontalScale(), | |||||
fontHeight), fontHeight); | |||||
} | } | ||||
Font font; | Font font; | ||||
@@ -2487,7 +2487,7 @@ public: | |||||
AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) | AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) | ||||
.followedBy (trans))); | .followedBy (trans))); | ||||
const ScopedPointer<EdgeTable> et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t)); | |||||
const ScopedPointer<EdgeTable> et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); | |||||
if (et != nullptr) | if (et != nullptr) | ||||
fillShape (new EdgeTableRegionType (*et), false); | fillShape (new EdgeTableRegionType (*et), false); | ||||
@@ -170,11 +170,11 @@ public: | |||||
heightToPointsFactor = referenceFontSize / totalHeight; | heightToPointsFactor = referenceFontSize / totalHeight; | ||||
} | } | ||||
float getAscent() const { return ascent; } | |||||
float getDescent() const { return descent; } | |||||
float getHeightToPointsFactor() const { return heightToPointsFactor; } | |||||
float getAscent() const override { return ascent; } | |||||
float getDescent() const override { return descent; } | |||||
float getHeightToPointsFactor() const override { return heightToPointsFactor; } | |||||
float getStringWidth (const String& text) | |||||
float getStringWidth (const String& text) override | |||||
{ | { | ||||
JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
const int numChars = text.length(); | const int numChars = text.length(); | ||||
@@ -193,7 +193,7 @@ public: | |||||
return x * referenceFontToUnits; | return x * referenceFontToUnits; | ||||
} | } | ||||
void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) | |||||
void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) override | |||||
{ | { | ||||
JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
const int numChars = text.length(); | const int numChars = text.length(); | ||||
@@ -218,12 +218,12 @@ public: | |||||
} | } | ||||
} | } | ||||
bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) | |||||
bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) override | |||||
{ | { | ||||
return false; | return false; | ||||
} | } | ||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t) | |||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t, float /*fontHeight*/) override | |||||
{ | { | ||||
JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
@@ -575,11 +575,11 @@ public: | |||||
CFRelease (ctFontRef); | CFRelease (ctFontRef); | ||||
} | } | ||||
float getAscent() const { return ascent; } | |||||
float getDescent() const { return 1.0f - ascent; } | |||||
float getHeightToPointsFactor() const { return fontHeightToPointsFactor; } | |||||
float getAscent() const override { return ascent; } | |||||
float getDescent() const override { return 1.0f - ascent; } | |||||
float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; } | |||||
float getStringWidth (const String& text) | |||||
float getStringWidth (const String& text) override | |||||
{ | { | ||||
float x = 0; | float x = 0; | ||||
@@ -612,7 +612,7 @@ public: | |||||
return x; | return x; | ||||
} | } | ||||
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets) | |||||
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets) override | |||||
{ | { | ||||
xOffsets.add (0); | xOffsets.add (0); | ||||
@@ -648,18 +648,7 @@ public: | |||||
} | } | ||||
} | } | ||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) | |||||
{ | |||||
Path path; | |||||
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) | |||||
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), | |||||
path, transform); | |||||
return nullptr; | |||||
} | |||||
bool getOutlineForGlyph (int glyphNumber, Path& path) | |||||
bool getOutlineForGlyph (int glyphNumber, Path& path) override | |||||
{ | { | ||||
jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty | jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty | ||||
@@ -906,11 +895,11 @@ public: | |||||
#endif | #endif | ||||
float getAscent() const { return ascent; } | |||||
float getDescent() const { return 1.0f - ascent; } | |||||
float getHeightToPointsFactor() const { return fontHeightToPointsFactor; } | |||||
float getAscent() const override { return ascent; } | |||||
float getDescent() const override { return 1.0f - ascent; } | |||||
float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; } | |||||
float getStringWidth (const String& text) | |||||
float getStringWidth (const String& text) override | |||||
{ | { | ||||
if (fontRef == 0 || text.isEmpty()) | if (fontRef == 0 || text.isEmpty()) | ||||
return 0; | return 0; | ||||
@@ -951,7 +940,7 @@ public: | |||||
return x * unitsToHeightScaleFactor; | return x * unitsToHeightScaleFactor; | ||||
} | } | ||||
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets) | |||||
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets) override | |||||
{ | { | ||||
xOffsets.add (0); | xOffsets.add (0); | ||||
@@ -1009,18 +998,7 @@ public: | |||||
#endif | #endif | ||||
} | } | ||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) | |||||
{ | |||||
Path path; | |||||
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) | |||||
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), | |||||
path, transform); | |||||
return nullptr; | |||||
} | |||||
bool getOutlineForGlyph (int glyphNumber, Path& path) | |||||
bool getOutlineForGlyph (int glyphNumber, Path& path) override | |||||
{ | { | ||||
#if JUCE_IOS | #if JUCE_IOS | ||||
return false; | return false; | ||||
@@ -237,17 +237,6 @@ public: | |||||
} | } | ||||
} | } | ||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) | |||||
{ | |||||
Path path; | |||||
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) | |||||
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), | |||||
path, transform); | |||||
return nullptr; | |||||
} | |||||
bool getOutlineForGlyph (int glyphNumber, Path& path) | bool getOutlineForGlyph (int glyphNumber, Path& path) | ||||
{ | { | ||||
jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty | jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty | ||||
@@ -89,7 +89,7 @@ | |||||
USE_FUNCTION (glUniformMatrix4fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4)) | USE_FUNCTION (glUniformMatrix4fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4)) | ||||
#else | #else | ||||
#define JUCE_GL_EXTENSION_FUNCTIONS1(USE_FUNCTION) JUCE_GL_BASIC_EXTENSION_FUNCTIONS(USE_FUNCTION, EXT_FUNCTION) | |||||
#define JUCE_GL_EXTENSION_FUNCTIONS1(USE_FUNCTION, EXT_FUNCTION) JUCE_GL_BASIC_EXTENSION_FUNCTIONS(USE_FUNCTION, EXT_FUNCTION) | |||||
#endif | #endif | ||||
#if JUCE_USE_OPENGL_FIXED_FUNCTION | #if JUCE_USE_OPENGL_FIXED_FUNCTION | ||||
@@ -1410,7 +1410,7 @@ public: | |||||
AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) | AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) | ||||
.followedBy (trans))); | .followedBy (trans))); | ||||
const ScopedPointer<EdgeTable> et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t)); | |||||
const ScopedPointer<EdgeTable> et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); | |||||
if (et != nullptr) | if (et != nullptr) | ||||
fillShape (new EdgeTableRegionType (*et), false); | fillShape (new EdgeTableRegionType (*et), false); | ||||