diff --git a/modules/juce_graphics/fonts/juce_CustomTypeface.cpp b/modules/juce_graphics/fonts/juce_CustomTypeface.cpp index c9d6e8de32..a2adb04296 100644 --- a/modules/juce_graphics/fonts/juce_CustomTypeface.cpp +++ b/modules/juce_graphics/fonts/juce_CustomTypeface.cpp @@ -385,7 +385,7 @@ bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) 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)) { @@ -399,7 +399,7 @@ EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTr const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) - return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform); + return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform, fontHeight); } return nullptr; diff --git a/modules/juce_graphics/fonts/juce_CustomTypeface.h b/modules/juce_graphics/fonts/juce_CustomTypeface.h index 8e7e020dd5..fa1bb790b0 100644 --- a/modules/juce_graphics/fonts/juce_CustomTypeface.h +++ b/modules/juce_graphics/fonts/juce_CustomTypeface.h @@ -119,7 +119,7 @@ public: float getStringWidth (const String&) override; void getGlyphPositions (const String&, Array & glyphs, Array& xOffsets) override; bool getOutlineForGlyph (int glyphNumber, Path&) override; - EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&) override; + EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&, float fontHeight) override; protected: //============================================================================== diff --git a/modules/juce_graphics/fonts/juce_Typeface.cpp b/modules/juce_graphics/fonts/juce_Typeface.cpp index 517d547702..79366cd572 100644 --- a/modules/juce_graphics/fonts/juce_Typeface.cpp +++ b/modules/juce_graphics/fonts/juce_Typeface.cpp @@ -116,13 +116,17 @@ Typeface::Ptr Typeface::getFallbackTypeface() return fallbackFont.getTypeface(); } -EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) +EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) { Path path; if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) + { + applyVerticalHintingTransform (fontHeight, path); + return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), path, transform); + } return nullptr; } @@ -131,84 +135,78 @@ EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransfor struct Typeface::HintingParams { HintingParams (Typeface& t) - : top (0), middle (0), bottom (0) + : cachedSize (0), top (0), middle (0), bottom (0) { Font font (&t); font = font.withHeight ((float) standardHeight); - top = getAverageY (font, "BDEFPRTZOQC", true); + top = getAverageY (font, "BDEFPRTZOQ", true); middle = getAverageY (font, "acegmnopqrsuvwxy", true); 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: - float cachedSize; - AffineTransform cachedTransform; - 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) { GlyphArrangement ga; @@ -248,15 +246,15 @@ private: 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); + } } diff --git a/modules/juce_graphics/fonts/juce_Typeface.h b/modules/juce_graphics/fonts/juce_Typeface.h index ed42a48210..a392fb705d 100644 --- a/modules/juce_graphics/fonts/juce_Typeface.h +++ b/modules/juce_graphics/fonts/juce_Typeface.h @@ -117,7 +117,7 @@ public: 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. */ - 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. */ virtual bool isHinted() const { return false; } @@ -134,10 +134,11 @@ public: */ 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: //============================================================================== diff --git a/modules/juce_graphics/native/juce_RenderingHelpers.h b/modules/juce_graphics/native/juce_RenderingHelpers.h index cb8e9d6cc8..04c43a5151 100644 --- a/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -284,8 +284,8 @@ public: const float fontHeight = font.getHeight(); edgeTable = typeface->getEdgeTableForGlyph (glyphNumber, - AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) - .followedBy (font.getTypeface()->getVerticalHintingTransform (fontHeight))); + AffineTransform::scale (fontHeight * font.getHorizontalScale(), + fontHeight), fontHeight); } Font font; @@ -2487,7 +2487,7 @@ public: AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) .followedBy (trans))); - const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t)); + const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); if (et != nullptr) fillShape (new EdgeTableRegionType (*et), false); diff --git a/modules/juce_graphics/native/juce_android_Fonts.cpp b/modules/juce_graphics/native/juce_android_Fonts.cpp index 837dd6b680..29702a20b5 100644 --- a/modules/juce_graphics/native/juce_android_Fonts.cpp +++ b/modules/juce_graphics/native/juce_android_Fonts.cpp @@ -170,11 +170,11 @@ public: 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(); const int numChars = text.length(); @@ -193,7 +193,7 @@ public: return x * referenceFontToUnits; } - void getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) + void getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) override { JNIEnv* env = getEnv(); 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; } - EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t) + EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t, float /*fontHeight*/) override { JNIEnv* env = getEnv(); diff --git a/modules/juce_graphics/native/juce_mac_Fonts.mm b/modules/juce_graphics/native/juce_mac_Fonts.mm index 2dfa209a88..d54d7d6f41 100644 --- a/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -575,11 +575,11 @@ public: 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; @@ -612,7 +612,7 @@ public: return x; } - void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) override { 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 @@ -906,11 +895,11 @@ public: #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()) return 0; @@ -951,7 +940,7 @@ public: return x * unitsToHeightScaleFactor; } - void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) override { xOffsets.add (0); @@ -1009,18 +998,7 @@ public: #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 return false; diff --git a/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp b/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp index 8cd8ddc289..74b767c1a7 100644 --- a/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp +++ b/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp @@ -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) { jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty diff --git a/modules/juce_opengl/native/juce_OpenGLExtensions.h b/modules/juce_opengl/native/juce_OpenGLExtensions.h index e88221f6bc..ee0930f9b5 100644 --- a/modules/juce_opengl/native/juce_OpenGLExtensions.h +++ b/modules/juce_opengl/native/juce_OpenGLExtensions.h @@ -89,7 +89,7 @@ USE_FUNCTION (glUniformMatrix4fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4)) #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 #if JUCE_USE_OPENGL_FIXED_FUNCTION diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 4711317cbb..5c921dea54 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -1410,7 +1410,7 @@ public: AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) .followedBy (trans))); - const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t)); + const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); if (et != nullptr) fillShape (new EdgeTableRegionType (*et), false);