From ca727ec2bb6119a816e0effd6494be0077bf7731 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Tue, 10 Nov 2009 17:45:06 +0000 Subject: [PATCH] Improvements to path rendering and fix for PathStrokeType generating incorrect paths for some shapes. Added OSX10.4 compatibility for new new typeface classes. --- .../juce demo/src/demos/FontsAndTextDemo.cpp | 2 +- juce_amalgamated.cpp | 600 +++++++++++++----- juce_amalgamated.h | 50 +- src/gui/graphics/contexts/juce_EdgeTable.cpp | 217 ++++--- src/gui/graphics/contexts/juce_EdgeTable.h | 48 +- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 6 +- src/gui/graphics/fonts/juce_Font.cpp | 24 +- src/gui/graphics/geometry/juce_Path.cpp | 4 +- src/gui/graphics/geometry/juce_PathIterator.h | 2 +- .../graphics/geometry/juce_PathStrokeType.cpp | 43 +- .../mac/juce_mac_CoreGraphicsContext.mm | 2 +- src/native/mac/juce_mac_Fonts.mm | 152 ++++- 12 files changed, 795 insertions(+), 355 deletions(-) diff --git a/extras/juce demo/src/demos/FontsAndTextDemo.cpp b/extras/juce demo/src/demos/FontsAndTextDemo.cpp index 545439be82..576d5931d5 100644 --- a/extras/juce demo/src/demos/FontsAndTextDemo.cpp +++ b/extras/juce demo/src/demos/FontsAndTextDemo.cpp @@ -146,7 +146,7 @@ public: true); // resize the components' heights as well as widths // now lay out the text box and the controls below it.. - int x = verticalLayout.getItemCurrentPosition (2); + int x = verticalLayout.getItemCurrentPosition (2) + 4; textBox->setBounds (x, 0, getWidth() - x, getHeight() - 110); x += 70; sizeSlider->setBounds (x, getHeight() - 106, getWidth() - x, 22); diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 5803e8a183..bf8ce74098 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -78825,13 +78825,121 @@ BEGIN_JUCE_NAMESPACE const int juce_edgeTableDefaultEdgesPerLine = 32; -EdgeTable::EdgeTable (const int top_, const int height_) throw() +EdgeTable::EdgeTable (const int top_, const int height_, + const Path& path, const AffineTransform& transform) throw() : top (top_), height (height_), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) { - table = (int*) juce_calloc (height * lineStrideElements * sizeof (int)); + table = (int*) juce_malloc (height_ * lineStrideElements * sizeof (int)); + int* t = table; + for (int i = height_; --i >= 0;) + { + *t = 0; + t += lineStrideElements; + } + + const int topLimit = top << 8; + const int bottomLimit = height << 8; + PathFlatteningIterator iter (path, transform); + + while (iter.next()) + { + int y1 = roundFloatToInt (iter.y1 * 256.0f); + int y2 = roundFloatToInt (iter.y2 * 256.0f); + + if (y1 != y2) + { + y1 -= topLimit; + y2 -= topLimit; + + const double startX = 256.0f * iter.x1; + const int startY = y1; + const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1); + int winding = -1; + + if (y1 > y2) + { + swapVariables (y1, y2); + winding = 1; + } + + if (y1 < 0) + y1 = 0; + + if (y2 > bottomLimit) + y2 = bottomLimit; + + const int stepSize = jlimit (1, 256, 256 / (1 + (int) fabs (multiplier))); + + while (y1 < y2) + { + const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255)); + + addEdgePoint (roundDoubleToInt (startX + multiplier * (y1 - startY)), + y1 >> 8, winding * step); + + y1 += step; + } + } + } + + if (! path.isUsingNonZeroWinding()) + { + int* lineStart = table; + + for (int i = height; --i >= 0;) + { + int* line = lineStart; + lineStart += lineStrideElements; + int num = *line; + int level = 0; + int lastCorrected = 0; + + while (--num >= 0) + { + line += 2; + level += *line; + int corrected = abs (level); + if (corrected >> 8) + { + corrected &= 511; + if (corrected >> 8) + corrected = 511 - corrected; + } + + *line = corrected - lastCorrected; + lastCorrected = corrected; + } + } + } +} + +EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() + : top (rectangleToAdd.getY()), + height (jmax (1, rectangleToAdd.getHeight())), + maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) +{ + jassert (! rectangleToAdd.isEmpty()); + + table = (int*) juce_malloc (height * lineStrideElements * sizeof (int)); + *table = 0; + + const int x1 = rectangleToAdd.getX(); + const int x2 = rectangleToAdd.getRight(); + + int* t = table; + for (int i = rectangleToAdd.getHeight(); --i >= 0;) + { + t[0] = 2; + t[1] = x1; + t[2] = 256; + t[3] = x2; + t[4] = -256; + t += lineStrideElements; + } } EdgeTable::EdgeTable (const EdgeTable& other) throw() @@ -78936,94 +79044,47 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw lineStart[0]++; } -void EdgeTable::addPath (const Path& path, const AffineTransform& transform) throw() +void EdgeTable::clearLineSection (const int y, int minX, int maxX) throw() { - const int bottomLimit = height << 8; +// int* line = table + lineStrideElements * y; - PathFlatteningIterator iter (path, transform); +} - while (iter.next()) +void EdgeTable::clipToRectangle (const Rectangle& r) throw() +{ + const int rectTop = jmax (0, r.getY() - top); + const int rectBottom = jmin (height, r.getBottom() - top); + + for (int i = rectTop - 1; --i >= 0;) + table [lineStrideElements * i] = 0; + + for (int i = rectBottom; i < height; ++i) + table [lineStrideElements * i] = 0; + + for (int i = rectTop; i < rectBottom; ++i) { - int y1 = roundFloatToInt (iter.y1 * 256.0f) - (top << 8); - int y2 = roundFloatToInt (iter.y2 * 256.0f) - (top << 8); - - if (y1 != y2) - { - const int oldY1 = y1; - const double x1 = 256.0 * iter.x1; - const double x2 = 256.0 * iter.x2; - const double multiplier = (x2 - x1) / (y2 - y1); - int winding = -1; - - if (y1 > y2) - { - swapVariables (y1, y2); - winding = 1; - } - - if (y1 < 0) - y1 = 0; - - if (y2 > bottomLimit) - y2 = bottomLimit; - - const int stepSize = jlimit (1, 256, 256 / (1 + abs ((int) multiplier))); - - while (y1 < y2) - { - const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255)); - - addEdgePoint (roundDoubleToInt (x1 + multiplier * (y1 - oldY1)), - y1 >> 8, winding * step); - - y1 += step; - } - } - } - - if (! path.isUsingNonZeroWinding()) - { - int* lineStart = table; - - for (int i = height; --i >= 0;) - { - int* line = lineStart; - lineStart += lineStrideElements; - int num = *line; - int level = 0; - int lastCorrected = 0; - - while (--num >= 0) - { - line += 2; - level += *line; - int corrected = abs (level); - if (corrected >> 8) - { - corrected &= 511; - if (corrected >> 8) - corrected = 511 - corrected; - } - - *line = corrected - lastCorrected; - lastCorrected = corrected; - } - } + clearLineSection (i, -INT_MAX, r.getX()); + clearLineSection (i, r.getRight(), INT_MAX); } } -/*void EdgeTable::clipToRectangle (const Rectangle& r) throw() +void EdgeTable::excludeRectangle (const Rectangle& r) throw() +{ + const int rectTop = jmax (0, r.getY() - top); + const int rectBottom = jmin (height, r.getBottom() - top); + + for (int i = rectTop; i < rectBottom; ++i) + clearLineSection (i, r.getX(), r.getRight()); +} + +void EdgeTable::clipToEdgeTable (const EdgeTable& other) { } -void EdgeTable::intersectWith (const EdgeTable& other) +void EdgeTable::clipToImageAlpha (Image& image, int x, int y) throw() { } -void EdgeTable::generateFromImageAlpha (Image& image, int x, int y) throw() -{ -}*/ - END_JUCE_NAMESPACE /********* End of inlined file: juce_EdgeTable.cpp *********/ @@ -81995,8 +82056,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, in if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) { - EdgeTable edgeTable (0, ch); - edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); + EdgeTable edgeTable (0, ch, path, transform.translated ((float) -cx, (float) -cy)); int stride, pixelStride; uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); @@ -82123,8 +82183,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithImage (int x, int y, i { if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight())) { - EdgeTable edgeTable (0, h); - edgeTable.addPath (path, transform.translated ((float) (xOffset - x), (float) (yOffset - y))); + EdgeTable edgeTable (0, h, path, transform.translated ((float) (xOffset - x), (float) (yOffset - y))); int stride, pixelStride; uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); @@ -85928,17 +85987,21 @@ void Font::getGlyphPositions (const String& text, Array & glyphs, Array height * font->horizontalScale; const int num = xOffsets.size(); - float* const x = &(xOffsets.getReference(0)); - if (font->kerning != 0) + if (num > 0) { - 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; + float* const x = &(xOffsets.getReference(0)); + + if (font->kerning != 0) + { + 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; + } } } @@ -86069,7 +86132,7 @@ public: g.fillAlphaChannel (*bitmap [bitmapToUse], xOrigin [bitmapToUse] + (int) xFloor, - yOrigin [bitmapToUse] + (int) floorf (y)); + yOrigin [bitmapToUse] + roundFloatToInt(y)); } } @@ -89983,8 +90046,8 @@ Image* Path::createMaskBitmap (const AffineTransform& transform, Image* im = new Image (Image::SingleChannel, imagePosition.getWidth(), imagePosition.getHeight(), true); - EdgeTable edgeTable (0, imagePosition.getHeight()); - edgeTable.addPath (*this, transform.translated (-imagePosition.getX(), -imagePosition.getY())); + EdgeTable edgeTable (0, imagePosition.getHeight(), *this, + transform.translated (-imagePosition.getX(), -imagePosition.getY())); int stride, pixelStride; uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, imagePosition.getWidth(), imagePosition.getHeight(), stride, pixelStride); @@ -90473,20 +90536,47 @@ static void addEdgeAndJoint (Path& destPath, else { // curved joints - float angle = atan2f (x2 - midX, y2 - midY); + float angle1 = atan2f (x2 - midX, y2 - midY); float angle2 = atan2f (x3 - midX, y3 - midY); - - while (angle < angle2 - 0.01f) - angle2 -= float_Pi * 2.0f; + const float angleIncrement = 0.1f; destPath.lineTo (x2, y2); - while (angle > angle2) + if (fabs (angle1 - angle2) > angleIncrement) { - destPath.lineTo (midX + width * sinf (angle), - midY + width * cosf (angle)); + if (angle2 > angle1 + float_Pi + || (angle2 < angle1 && angle2 >= angle1 - float_Pi)) + { + if (angle2 > angle1) + angle2 -= float_Pi * 2.0f; - angle -= 0.1f; + jassert (angle1 <= angle2 + float_Pi); + + angle1 -= angleIncrement; + while (angle1 > angle2) + { + destPath.lineTo (midX + width * sinf (angle1), + midY + width * cosf (angle1)); + + angle1 -= angleIncrement; + } + } + else + { + if (angle1 > angle2) + angle1 -= float_Pi * 2.0f; + + jassert (angle1 >= angle2 - float_Pi); + + angle1 += angleIncrement; + while (angle1 < angle2) + { + destPath.lineTo (midX + width * sinf (angle1), + midY + width * cosf (angle1)); + + angle1 += angleIncrement; + } + } } destPath.lineTo (x3, y3); @@ -261922,6 +262012,17 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() // compiled on its own). #if JUCE_INCLUDED_FILE +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #define SUPPORT_10_4_FONTS 1 + #define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0) + + END_JUCE_NAMESPACE + @interface NSFont (PrivateHack) + - (NSGlyph) _defaultGlyphForChar: (unichar) theChar; + @end + BEGIN_JUCE_NAMESPACE +#endif + class MacTypeface : public Typeface { public: @@ -261973,6 +262074,9 @@ public: fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; #else nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; @@ -262004,18 +262108,40 @@ public: renderingTransform.c = 0.15f; } - fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); -#endif +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + if (atsFont == 0) + atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); + + fontRef = CGFontCreateWithPlatformFont ((void*) &atsFont); + + const float totalHeight = fabsf ([nsFont ascender]) + fabsf([nsFont descender]); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = 1024.0f / totalHeight; + } + else +#endif + { + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); + + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + } + +#endif } ~MacTypeface() { [nsFont release]; - CGFontRelease (fontRef); + + if (fontRef != 0) + CGFontRelease (fontRef); + delete charToGlyphMapper; } @@ -262031,50 +262157,88 @@ public: float getStringWidth (const String& text) { - if (fontRef == 0) + if (fontRef == 0 || text.isEmpty()) return 0; - Array glyphs (128); - createGlyphsForString (text, glyphs); - - if (glyphs.size() == 0) - return 0; + const int length = text.length(); + CGGlyph* const glyphs = createGlyphsForString (text, length); int x = 0; - int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); - if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) - for (int i = 0; i < glyphs.size(); ++i) - x += advances [i * 2]; +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + NSSize* const advances = (NSSize*) juce_malloc (length * sizeof (NSSize)); + [nsFont getAdvancements: advances forGlyphs: (NSGlyph*) glyphs count: length]; + + for (int i = 0; i < length; ++i) + x += advances[i].width; + + juce_free (advances); + } + else +#endif + { + int* const advances = (int*) juce_malloc (length * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) + for (int i = 0; i < length; ++i) + x += advances[i]; + + juce_free (advances); + } + + juce_free (glyphs); - juce_free (advances); return x * unitsToHeightScaleFactor; } - void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) { - if (fontRef == 0) - return; - - createGlyphsForString (text, glyphs); - xOffsets.add (0); - if (glyphs.size() == 0) + + if (fontRef == 0 || text.isEmpty()) return; - int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); + const int length = text.length(); + CGGlyph* const glyphs = createGlyphsForString (text, length); - if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) { + NSSize* const advances = (NSSize*) juce_malloc (length * sizeof (NSSize)); + [nsFont getAdvancements: advances forGlyphs: (NSGlyph*) glyphs count: length]; + int x = 0; - for (int i = 0; i < glyphs.size(); ++i) + for (int i = 0; i < length; ++i) { - x += advances [i * 2]; + x += advances[i].width; xOffsets.add (x * unitsToHeightScaleFactor); + resultGlyphs.add (((NSGlyph*) glyphs)[i]); } + + juce_free (advances); + } + else +#endif + { + int* const advances = (int*) juce_malloc (length * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) + { + int x = 0; + for (int i = 0; i < length; ++i) + { + x += advances [i]; + xOffsets.add (x * unitsToHeightScaleFactor); + resultGlyphs.add (glyphs[i]); + } + } + + juce_free (advances); } - juce_free (advances); + juce_free (glyphs); } bool getOutlineForGlyph (int glyphNumber, Path& path) @@ -262139,15 +262303,28 @@ private: AffineTransform pathTransform; #endif - void createGlyphsForString (const String& text, Array & dest) throw() + CGGlyph* createGlyphsForString (const juce_wchar* const text, const int length) throw() { +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + NSGlyph* const g = (NSGlyph*) juce_malloc (sizeof (NSGlyph) * length); + + for (int i = 0; i < length; ++i) + g[i] = (NSGlyph) [nsFont _defaultGlyphForChar: text[i]]; + + return (CGGlyph*) g; + } +#endif if (charToGlyphMapper == 0) charToGlyphMapper = new CharToGlyphMapper (fontRef); - const juce_wchar* t = (const juce_wchar*) text; + CGGlyph* const g = (CGGlyph*) juce_malloc (sizeof (CGGlyph) * length); - while (*t != 0) - dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); + for (int i = 0; i < length; ++i) + g[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text[i]); + + return g; } // Reads a CGFontRef's character map table to convert unicode into glyph numbers @@ -265578,6 +265755,17 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) // compiled on its own). #if JUCE_INCLUDED_FILE +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #define SUPPORT_10_4_FONTS 1 + #define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0) + + END_JUCE_NAMESPACE + @interface NSFont (PrivateHack) + - (NSGlyph) _defaultGlyphForChar: (unichar) theChar; + @end + BEGIN_JUCE_NAMESPACE +#endif + class MacTypeface : public Typeface { public: @@ -265629,6 +265817,9 @@ public: fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; #else nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; @@ -265660,18 +265851,40 @@ public: renderingTransform.c = 0.15f; } - fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); -#endif +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + if (atsFont == 0) + atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); + + fontRef = CGFontCreateWithPlatformFont ((void*) &atsFont); + + const float totalHeight = fabsf ([nsFont ascender]) + fabsf([nsFont descender]); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = 1024.0f / totalHeight; + } + else +#endif + { + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); + + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + } + +#endif } ~MacTypeface() { [nsFont release]; - CGFontRelease (fontRef); + + if (fontRef != 0) + CGFontRelease (fontRef); + delete charToGlyphMapper; } @@ -265687,50 +265900,88 @@ public: float getStringWidth (const String& text) { - if (fontRef == 0) + if (fontRef == 0 || text.isEmpty()) return 0; - Array glyphs (128); - createGlyphsForString (text, glyphs); - - if (glyphs.size() == 0) - return 0; + const int length = text.length(); + CGGlyph* const glyphs = createGlyphsForString (text, length); int x = 0; - int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); - if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) - for (int i = 0; i < glyphs.size(); ++i) - x += advances [i * 2]; +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + NSSize* const advances = (NSSize*) juce_malloc (length * sizeof (NSSize)); + [nsFont getAdvancements: advances forGlyphs: (NSGlyph*) glyphs count: length]; + + for (int i = 0; i < length; ++i) + x += advances[i].width; + + juce_free (advances); + } + else +#endif + { + int* const advances = (int*) juce_malloc (length * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) + for (int i = 0; i < length; ++i) + x += advances[i]; + + juce_free (advances); + } + + juce_free (glyphs); - juce_free (advances); return x * unitsToHeightScaleFactor; } - void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) { - if (fontRef == 0) - return; - - createGlyphsForString (text, glyphs); - xOffsets.add (0); - if (glyphs.size() == 0) + + if (fontRef == 0 || text.isEmpty()) return; - int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); + const int length = text.length(); + CGGlyph* const glyphs = createGlyphsForString (text, length); - if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) { + NSSize* const advances = (NSSize*) juce_malloc (length * sizeof (NSSize)); + [nsFont getAdvancements: advances forGlyphs: (NSGlyph*) glyphs count: length]; + int x = 0; - for (int i = 0; i < glyphs.size(); ++i) + for (int i = 0; i < length; ++i) { - x += advances [i * 2]; + x += advances[i].width; xOffsets.add (x * unitsToHeightScaleFactor); + resultGlyphs.add (((NSGlyph*) glyphs)[i]); } + + juce_free (advances); + } + else +#endif + { + int* const advances = (int*) juce_malloc (length * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) + { + int x = 0; + for (int i = 0; i < length; ++i) + { + x += advances [i]; + xOffsets.add (x * unitsToHeightScaleFactor); + resultGlyphs.add (glyphs[i]); + } + } + + juce_free (advances); } - juce_free (advances); + juce_free (glyphs); } bool getOutlineForGlyph (int glyphNumber, Path& path) @@ -265795,15 +266046,28 @@ private: AffineTransform pathTransform; #endif - void createGlyphsForString (const String& text, Array & dest) throw() + CGGlyph* createGlyphsForString (const juce_wchar* const text, const int length) throw() { +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + NSGlyph* const g = (NSGlyph*) juce_malloc (sizeof (NSGlyph) * length); + + for (int i = 0; i < length; ++i) + g[i] = (NSGlyph) [nsFont _defaultGlyphForChar: text[i]]; + + return (CGGlyph*) g; + } +#endif if (charToGlyphMapper == 0) charToGlyphMapper = new CharToGlyphMapper (fontRef); - const juce_wchar* t = (const juce_wchar*) text; + CGGlyph* const g = (CGGlyph*) juce_malloc (sizeof (CGGlyph) * length); - while (*t != 0) - dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); + for (int i = 0; i < length; ++i) + g[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text[i]); + + return g; } // Reads a CGFontRef's character map table to convert unicode into glyph numbers @@ -266268,7 +266532,7 @@ public: if (state->fontRef != 0) { CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, x, flipHeight - y, &g, 1); + CGContextShowGlyphsAtPoint (context, x, flipHeight - roundFloatToInt (y), &g, 1); } else { diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 845d46f8a6..681bdafdbd 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -17087,15 +17087,22 @@ class JUCE_API EdgeTable { public: - /** Creates an empty edge table ready to have paths added. + /** Creates an edge table containing a path. - A table is created with a fixed vertical size, and only sections of paths - which lie within their range will be added to the table. + A table is created with a fixed vertical range, and only sections of the path + which lie within this range will be added to the table. - @param y the lowest y co-ordinate that the table can contain + @param y the top y co-ordinate that the table can contain @param height the number of horizontal lines it contains + @param pathToAdd the path to add to the table + @param transform a transform to apply to the path being added */ - EdgeTable (const int y, const int height) throw(); + EdgeTable (const int y, const int height, + const Path& pathToAdd, const AffineTransform& transform) throw(); + + /** Creates an edge table containing a rectangle. + */ + EdgeTable (const Rectangle& rectangleToAdd) throw(); /** Creates a copy of another edge table. */ EdgeTable (const EdgeTable& other) throw(); @@ -17106,21 +17113,10 @@ public: /** Destructor. */ ~EdgeTable() throw(); - /** Adds edges to the table for a path. - - This will add horizontal lines to the edge table for any parts of the path - which lie within the vertical bounds for which this table was created. - - @param path the path to add - @param transform an optional transform to apply to the path while it's - being added - */ - void addPath (const Path& path, - const AffineTransform& transform) throw(); - - /*void clipToRectangle (const Rectangle& r) throw(); - void intersectWith (const EdgeTable& other); - void generateFromImageAlpha (Image& image, int x, int y) throw();*/ + void clipToRectangle (const Rectangle& r) throw(); + void excludeRectangle (const Rectangle& r) throw(); + void clipToEdgeTable (const EdgeTable& other); + void clipToImageAlpha (Image& image, int x, int y) throw(); /** Reduces the amount of space the table has allocated. @@ -17208,7 +17204,12 @@ public: { levelAccumulator >>= 8; if (levelAccumulator > 0) - iterationCallback.handleEdgeTablePixel (x, jmin (0xff, levelAccumulator)); + { + if (levelAccumulator >> 8) + levelAccumulator = 0xff; + + iterationCallback.handleEdgeTablePixel (x, levelAccumulator); + } } if (++x >= clipRight) @@ -17229,8 +17230,7 @@ public: const int numPix = endOfRun - x; if (numPix > 0) - iterationCallback.handleEdgeTableLine (x, numPix, - jmin (correctedLevel, 0xff)); + iterationCallback.handleEdgeTableLine (x, numPix, correctedLevel); } // save the bit at the end to be drawn next time round the loop. @@ -17261,10 +17261,10 @@ private: // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc int* table; int top, height, maxEdgesPerLine, lineStrideElements; - bool nonZeroWinding; void addEdgePoint (const int x, const int y, const int winding) throw(); void remapTableForNumEdges (const int newNumEdgesPerLine) throw(); + void clearLineSection (const int y, int minX, int maxX) throw(); }; #endif // __JUCE_EDGETABLE_JUCEHEADER__ @@ -40217,7 +40217,7 @@ public: */ PathFlatteningIterator (const Path& path, const AffineTransform& transform = AffineTransform::identity, - float tolerence = 9.0f) throw(); + float tolerence = 6.0f) throw(); /** Destructor. */ ~PathFlatteningIterator() throw(); diff --git a/src/gui/graphics/contexts/juce_EdgeTable.cpp b/src/gui/graphics/contexts/juce_EdgeTable.cpp index 188c78bfb2..4754d1d417 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.cpp +++ b/src/gui/graphics/contexts/juce_EdgeTable.cpp @@ -33,13 +33,121 @@ BEGIN_JUCE_NAMESPACE const int juce_edgeTableDefaultEdgesPerLine = 32; //============================================================================== -EdgeTable::EdgeTable (const int top_, const int height_) throw() +EdgeTable::EdgeTable (const int top_, const int height_, + const Path& path, const AffineTransform& transform) throw() : top (top_), height (height_), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) { - table = (int*) juce_calloc (height * lineStrideElements * sizeof (int)); + table = (int*) juce_malloc (height_ * lineStrideElements * sizeof (int)); + int* t = table; + for (int i = height_; --i >= 0;) + { + *t = 0; + t += lineStrideElements; + } + + const int topLimit = top << 8; + const int bottomLimit = height << 8; + PathFlatteningIterator iter (path, transform); + + while (iter.next()) + { + int y1 = roundFloatToInt (iter.y1 * 256.0f); + int y2 = roundFloatToInt (iter.y2 * 256.0f); + + if (y1 != y2) + { + y1 -= topLimit; + y2 -= topLimit; + + const double startX = 256.0f * iter.x1; + const int startY = y1; + const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1); + int winding = -1; + + if (y1 > y2) + { + swapVariables (y1, y2); + winding = 1; + } + + if (y1 < 0) + y1 = 0; + + if (y2 > bottomLimit) + y2 = bottomLimit; + + const int stepSize = jlimit (1, 256, 256 / (1 + (int) fabs (multiplier))); + + while (y1 < y2) + { + const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255)); + + addEdgePoint (roundDoubleToInt (startX + multiplier * (y1 - startY)), + y1 >> 8, winding * step); + + y1 += step; + } + } + } + + if (! path.isUsingNonZeroWinding()) + { + int* lineStart = table; + + for (int i = height; --i >= 0;) + { + int* line = lineStart; + lineStart += lineStrideElements; + int num = *line; + int level = 0; + int lastCorrected = 0; + + while (--num >= 0) + { + line += 2; + level += *line; + int corrected = abs (level); + if (corrected >> 8) + { + corrected &= 511; + if (corrected >> 8) + corrected = 511 - corrected; + } + + *line = corrected - lastCorrected; + lastCorrected = corrected; + } + } + } +} + +EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() + : top (rectangleToAdd.getY()), + height (jmax (1, rectangleToAdd.getHeight())), + maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) +{ + jassert (! rectangleToAdd.isEmpty()); + + table = (int*) juce_malloc (height * lineStrideElements * sizeof (int)); + *table = 0; + + const int x1 = rectangleToAdd.getX(); + const int x2 = rectangleToAdd.getRight(); + + int* t = table; + for (int i = rectangleToAdd.getHeight(); --i >= 0;) + { + t[0] = 2; + t[1] = x1; + t[2] = 256; + t[3] = x2; + t[4] = -256; + t += lineStrideElements; + } } EdgeTable::EdgeTable (const EdgeTable& other) throw() @@ -109,7 +217,6 @@ void EdgeTable::optimiseTable() throw() remapTableForNumEdges (maxLineElements); } -//============================================================================== void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw() { jassert (y >= 0 && y < height) @@ -146,93 +253,47 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw lineStart[0]++; } +void EdgeTable::clearLineSection (const int y, int minX, int maxX) throw() +{ +// int* line = table + lineStrideElements * y; + + +} + //============================================================================== -void EdgeTable::addPath (const Path& path, const AffineTransform& transform) throw() +void EdgeTable::clipToRectangle (const Rectangle& r) throw() { - const int bottomLimit = height << 8; + const int rectTop = jmax (0, r.getY() - top); + const int rectBottom = jmin (height, r.getBottom() - top); - PathFlatteningIterator iter (path, transform); + for (int i = rectTop - 1; --i >= 0;) + table [lineStrideElements * i] = 0; - while (iter.next()) + for (int i = rectBottom; i < height; ++i) + table [lineStrideElements * i] = 0; + + for (int i = rectTop; i < rectBottom; ++i) { - int y1 = roundFloatToInt (iter.y1 * 256.0f) - (top << 8); - int y2 = roundFloatToInt (iter.y2 * 256.0f) - (top << 8); - - if (y1 != y2) - { - const int oldY1 = y1; - const double x1 = 256.0 * iter.x1; - const double x2 = 256.0 * iter.x2; - const double multiplier = (x2 - x1) / (y2 - y1); - int winding = -1; - - if (y1 > y2) - { - swapVariables (y1, y2); - winding = 1; - } - - if (y1 < 0) - y1 = 0; - - if (y2 > bottomLimit) - y2 = bottomLimit; - - const int stepSize = jlimit (1, 256, 256 / (1 + abs ((int) multiplier))); - - while (y1 < y2) - { - const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255)); - - addEdgePoint (roundDoubleToInt (x1 + multiplier * (y1 - oldY1)), - y1 >> 8, winding * step); - - y1 += step; - } - } - } - - if (! path.isUsingNonZeroWinding()) - { - int* lineStart = table; - - for (int i = height; --i >= 0;) - { - int* line = lineStart; - lineStart += lineStrideElements; - int num = *line; - int level = 0; - int lastCorrected = 0; - - while (--num >= 0) - { - line += 2; - level += *line; - int corrected = abs (level); - if (corrected >> 8) - { - corrected &= 511; - if (corrected >> 8) - corrected = 511 - corrected; - } - - *line = corrected - lastCorrected; - lastCorrected = corrected; - } - } + clearLineSection (i, -INT_MAX, r.getX()); + clearLineSection (i, r.getRight(), INT_MAX); } } -/*void EdgeTable::clipToRectangle (const Rectangle& r) throw() +void EdgeTable::excludeRectangle (const Rectangle& r) throw() +{ + const int rectTop = jmax (0, r.getY() - top); + const int rectBottom = jmin (height, r.getBottom() - top); + + for (int i = rectTop; i < rectBottom; ++i) + clearLineSection (i, r.getX(), r.getRight()); +} + +void EdgeTable::clipToEdgeTable (const EdgeTable& other) { } -void EdgeTable::intersectWith (const EdgeTable& other) +void EdgeTable::clipToImageAlpha (Image& image, int x, int y) throw() { } -void EdgeTable::generateFromImageAlpha (Image& image, int x, int y) throw() -{ -}*/ - END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_EdgeTable.h b/src/gui/graphics/contexts/juce_EdgeTable.h index 98885f49d6..bf184b1e12 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.h +++ b/src/gui/graphics/contexts/juce_EdgeTable.h @@ -42,15 +42,22 @@ class JUCE_API EdgeTable { public: //============================================================================== - /** Creates an empty edge table ready to have paths added. + /** Creates an edge table containing a path. - A table is created with a fixed vertical size, and only sections of paths - which lie within their range will be added to the table. + A table is created with a fixed vertical range, and only sections of the path + which lie within this range will be added to the table. - @param y the lowest y co-ordinate that the table can contain + @param y the top y co-ordinate that the table can contain @param height the number of horizontal lines it contains + @param pathToAdd the path to add to the table + @param transform a transform to apply to the path being added */ - EdgeTable (const int y, const int height) throw(); + EdgeTable (const int y, const int height, + const Path& pathToAdd, const AffineTransform& transform) throw(); + + /** Creates an edge table containing a rectangle. + */ + EdgeTable (const Rectangle& rectangleToAdd) throw(); /** Creates a copy of another edge table. */ EdgeTable (const EdgeTable& other) throw(); @@ -62,21 +69,10 @@ public: ~EdgeTable() throw(); //============================================================================== - /** Adds edges to the table for a path. - - This will add horizontal lines to the edge table for any parts of the path - which lie within the vertical bounds for which this table was created. - - @param path the path to add - @param transform an optional transform to apply to the path while it's - being added - */ - void addPath (const Path& path, - const AffineTransform& transform) throw(); - - /*void clipToRectangle (const Rectangle& r) throw(); - void intersectWith (const EdgeTable& other); - void generateFromImageAlpha (Image& image, int x, int y) throw();*/ + void clipToRectangle (const Rectangle& r) throw(); + void excludeRectangle (const Rectangle& r) throw(); + void clipToEdgeTable (const EdgeTable& other); + void clipToImageAlpha (Image& image, int x, int y) throw(); /** Reduces the amount of space the table has allocated. @@ -166,7 +162,12 @@ public: { levelAccumulator >>= 8; if (levelAccumulator > 0) - iterationCallback.handleEdgeTablePixel (x, jmin (0xff, levelAccumulator)); + { + if (levelAccumulator >> 8) + levelAccumulator = 0xff; + + iterationCallback.handleEdgeTablePixel (x, levelAccumulator); + } } if (++x >= clipRight) @@ -187,8 +188,7 @@ public: const int numPix = endOfRun - x; if (numPix > 0) - iterationCallback.handleEdgeTableLine (x, numPix, - jmin (correctedLevel, 0xff)); + iterationCallback.handleEdgeTableLine (x, numPix, correctedLevel); } // save the bit at the end to be drawn next time round the loop. @@ -220,10 +220,10 @@ private: // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc int* table; int top, height, maxEdgesPerLine, lineStrideElements; - bool nonZeroWinding; void addEdgePoint (const int x, const int y, const int winding) throw(); void remapTableForNumEdges (const int newNumEdgesPerLine) throw(); + void clearLineSection (const int y, int minX, int maxX) throw(); }; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index 1928d77f73..afb3d5b3fe 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -1362,8 +1362,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, in if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) { - EdgeTable edgeTable (0, ch); - edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); + EdgeTable edgeTable (0, ch, path, transform.translated ((float) -cx, (float) -cy)); int stride, pixelStride; uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); @@ -1490,8 +1489,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithImage (int x, int y, i { if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight())) { - EdgeTable edgeTable (0, h); - edgeTable.addPath (path, transform.translated ((float) (xOffset - x), (float) (yOffset - y))); + EdgeTable edgeTable (0, h, path, transform.translated ((float) (xOffset - x), (float) (yOffset - y))); int stride, pixelStride; uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); diff --git a/src/gui/graphics/fonts/juce_Font.cpp b/src/gui/graphics/fonts/juce_Font.cpp index 853cd5c617..069582c929 100644 --- a/src/gui/graphics/fonts/juce_Font.cpp +++ b/src/gui/graphics/fonts/juce_Font.cpp @@ -309,17 +309,21 @@ void Font::getGlyphPositions (const String& text, Array & glyphs, Array height * font->horizontalScale; const int num = xOffsets.size(); - float* const x = &(xOffsets.getReference(0)); - if (font->kerning != 0) + if (num > 0) { - 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; + float* const x = &(xOffsets.getReference(0)); + + if (font->kerning != 0) + { + 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; + } } } @@ -455,7 +459,7 @@ public: g.fillAlphaChannel (*bitmap [bitmapToUse], xOrigin [bitmapToUse] + (int) xFloor, - yOrigin [bitmapToUse] + (int) floorf (y)); + yOrigin [bitmapToUse] + roundFloatToInt(y)); } } diff --git a/src/gui/graphics/geometry/juce_Path.cpp b/src/gui/graphics/geometry/juce_Path.cpp index 1cc0ddc95a..769c5bb55e 100644 --- a/src/gui/graphics/geometry/juce_Path.cpp +++ b/src/gui/graphics/geometry/juce_Path.cpp @@ -1603,8 +1603,8 @@ Image* Path::createMaskBitmap (const AffineTransform& transform, Image* im = new Image (Image::SingleChannel, imagePosition.getWidth(), imagePosition.getHeight(), true); - EdgeTable edgeTable (0, imagePosition.getHeight()); - edgeTable.addPath (*this, transform.translated (-imagePosition.getX(), -imagePosition.getY())); + EdgeTable edgeTable (0, imagePosition.getHeight(), *this, + transform.translated (-imagePosition.getX(), -imagePosition.getY())); int stride, pixelStride; uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, imagePosition.getWidth(), imagePosition.getHeight(), stride, pixelStride); diff --git a/src/gui/graphics/geometry/juce_PathIterator.h b/src/gui/graphics/geometry/juce_PathIterator.h index 51ca652fb1..045f34068d 100644 --- a/src/gui/graphics/geometry/juce_PathIterator.h +++ b/src/gui/graphics/geometry/juce_PathIterator.h @@ -56,7 +56,7 @@ public: */ PathFlatteningIterator (const Path& path, const AffineTransform& transform = AffineTransform::identity, - float tolerence = 9.0f) throw(); + float tolerence = 6.0f) throw(); /** Destructor. */ ~PathFlatteningIterator() throw(); diff --git a/src/gui/graphics/geometry/juce_PathStrokeType.cpp b/src/gui/graphics/geometry/juce_PathStrokeType.cpp index 845ac9aa0a..deece0c8be 100644 --- a/src/gui/graphics/geometry/juce_PathStrokeType.cpp +++ b/src/gui/graphics/geometry/juce_PathStrokeType.cpp @@ -239,20 +239,47 @@ static void addEdgeAndJoint (Path& destPath, else { // curved joints - float angle = atan2f (x2 - midX, y2 - midY); + float angle1 = atan2f (x2 - midX, y2 - midY); float angle2 = atan2f (x3 - midX, y3 - midY); - - while (angle < angle2 - 0.01f) - angle2 -= float_Pi * 2.0f; + const float angleIncrement = 0.1f; destPath.lineTo (x2, y2); - while (angle > angle2) + if (fabs (angle1 - angle2) > angleIncrement) { - destPath.lineTo (midX + width * sinf (angle), - midY + width * cosf (angle)); + if (angle2 > angle1 + float_Pi + || (angle2 < angle1 && angle2 >= angle1 - float_Pi)) + { + if (angle2 > angle1) + angle2 -= float_Pi * 2.0f; - angle -= 0.1f; + jassert (angle1 <= angle2 + float_Pi); + + angle1 -= angleIncrement; + while (angle1 > angle2) + { + destPath.lineTo (midX + width * sinf (angle1), + midY + width * cosf (angle1)); + + angle1 -= angleIncrement; + } + } + else + { + if (angle1 > angle2) + angle1 -= float_Pi * 2.0f; + + jassert (angle1 >= angle2 - float_Pi); + + angle1 += angleIncrement; + while (angle1 < angle2) + { + destPath.lineTo (midX + width * sinf (angle1), + midY + width * cosf (angle1)); + + angle1 += angleIncrement; + } + } } destPath.lineTo (x3, y3); diff --git a/src/native/mac/juce_mac_CoreGraphicsContext.mm b/src/native/mac/juce_mac_CoreGraphicsContext.mm index e4fbc86ce6..6bd41f455d 100644 --- a/src/native/mac/juce_mac_CoreGraphicsContext.mm +++ b/src/native/mac/juce_mac_CoreGraphicsContext.mm @@ -355,7 +355,7 @@ public: if (state->fontRef != 0) { CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, x, flipHeight - y, &g, 1); + CGContextShowGlyphsAtPoint (context, x, flipHeight - roundFloatToInt (y), &g, 1); } else { diff --git a/src/native/mac/juce_mac_Fonts.mm b/src/native/mac/juce_mac_Fonts.mm index 06e4c6c8f5..25897367c4 100644 --- a/src/native/mac/juce_mac_Fonts.mm +++ b/src/native/mac/juce_mac_Fonts.mm @@ -27,6 +27,16 @@ // compiled on its own). #if JUCE_INCLUDED_FILE +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #define SUPPORT_10_4_FONTS 1 + #define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0) + + END_JUCE_NAMESPACE + @interface NSFont (PrivateHack) + - (NSGlyph) _defaultGlyphForChar: (unichar) theChar; + @end + BEGIN_JUCE_NAMESPACE +#endif //============================================================================== class MacTypeface : public Typeface @@ -80,6 +90,9 @@ public: fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; #else nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; @@ -111,18 +124,40 @@ public: renderingTransform.c = 0.15f; } - fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); -#endif +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + if (atsFont == 0) + atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); + + fontRef = CGFontCreateWithPlatformFont ((void*) &atsFont); + + const float totalHeight = fabsf ([nsFont ascender]) + fabsf([nsFont descender]); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = 1024.0f / totalHeight; + } + else +#endif + { + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); + + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + } + +#endif } ~MacTypeface() { [nsFont release]; - CGFontRelease (fontRef); + + if (fontRef != 0) + CGFontRelease (fontRef); + delete charToGlyphMapper; } @@ -138,50 +173,88 @@ public: float getStringWidth (const String& text) { - if (fontRef == 0) + if (fontRef == 0 || text.isEmpty()) return 0; - Array glyphs (128); - createGlyphsForString (text, glyphs); - - if (glyphs.size() == 0) - return 0; + const int length = text.length(); + CGGlyph* const glyphs = createGlyphsForString (text, length); int x = 0; - int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); - if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) - for (int i = 0; i < glyphs.size(); ++i) - x += advances [i * 2]; +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + NSSize* const advances = (NSSize*) juce_malloc (length * sizeof (NSSize)); + [nsFont getAdvancements: advances forGlyphs: (NSGlyph*) glyphs count: length]; + + for (int i = 0; i < length; ++i) + x += advances[i].width; + + juce_free (advances); + } + else +#endif + { + int* const advances = (int*) juce_malloc (length * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) + for (int i = 0; i < length; ++i) + x += advances[i]; + + juce_free (advances); + } + + juce_free (glyphs); - juce_free (advances); return x * unitsToHeightScaleFactor; } - void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) { - if (fontRef == 0) - return; - - createGlyphsForString (text, glyphs); - xOffsets.add (0); - if (glyphs.size() == 0) + + if (fontRef == 0 || text.isEmpty()) return; - int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); + const int length = text.length(); + CGGlyph* const glyphs = createGlyphsForString (text, length); - if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) { + NSSize* const advances = (NSSize*) juce_malloc (length * sizeof (NSSize)); + [nsFont getAdvancements: advances forGlyphs: (NSGlyph*) glyphs count: length]; + int x = 0; - for (int i = 0; i < glyphs.size(); ++i) + for (int i = 0; i < length; ++i) { - x += advances [i * 2]; + x += advances[i].width; xOffsets.add (x * unitsToHeightScaleFactor); + resultGlyphs.add (((NSGlyph*) glyphs)[i]); } + + juce_free (advances); + } + else +#endif + { + int* const advances = (int*) juce_malloc (length * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) + { + int x = 0; + for (int i = 0; i < length; ++i) + { + x += advances [i]; + xOffsets.add (x * unitsToHeightScaleFactor); + resultGlyphs.add (glyphs[i]); + } + } + + juce_free (advances); } - juce_free (advances); + juce_free (glyphs); } bool getOutlineForGlyph (int glyphNumber, Path& path) @@ -247,15 +320,28 @@ private: AffineTransform pathTransform; #endif - void createGlyphsForString (const String& text, Array & dest) throw() + CGGlyph* createGlyphsForString (const juce_wchar* const text, const int length) throw() { +#if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) + { + NSGlyph* const g = (NSGlyph*) juce_malloc (sizeof (NSGlyph) * length); + + for (int i = 0; i < length; ++i) + g[i] = (NSGlyph) [nsFont _defaultGlyphForChar: text[i]]; + + return (CGGlyph*) g; + } +#endif if (charToGlyphMapper == 0) charToGlyphMapper = new CharToGlyphMapper (fontRef); - const juce_wchar* t = (const juce_wchar*) text; + CGGlyph* const g = (CGGlyph*) juce_malloc (sizeof (CGGlyph) * length); - while (*t != 0) - dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); + for (int i = 0; i < length; ++i) + g[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text[i]); + + return g; } // Reads a CGFontRef's character map table to convert unicode into glyph numbers