| @@ -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); | |||
| @@ -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,93 +79044,46 @@ 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; | |||
| PathFlatteningIterator iter (path, transform); | |||
| // int* line = table + lineStrideElements * y; | |||
| while (iter.next()) | |||
| { | |||
| 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)); | |||
| void EdgeTable::clipToRectangle (const Rectangle& r) throw() | |||
| { | |||
| const int rectTop = jmax (0, r.getY() - top); | |||
| const int rectBottom = jmin (height, r.getBottom() - top); | |||
| addEdgePoint (roundDoubleToInt (x1 + multiplier * (y1 - oldY1)), | |||
| y1 >> 8, winding * step); | |||
| for (int i = rectTop - 1; --i >= 0;) | |||
| table [lineStrideElements * i] = 0; | |||
| y1 += step; | |||
| } | |||
| } | |||
| } | |||
| for (int i = rectBottom; i < height; ++i) | |||
| table [lineStrideElements * i] = 0; | |||
| if (! path.isUsingNonZeroWinding()) | |||
| for (int i = rectTop; i < rectBottom; ++i) | |||
| { | |||
| 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::intersectWith (const EdgeTable& other) | |||
| void EdgeTable::clipToEdgeTable (const EdgeTable& other) | |||
| { | |||
| } | |||
| void EdgeTable::generateFromImageAlpha (Image& image, int x, int y) throw() | |||
| void EdgeTable::clipToImageAlpha (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 <int>& glyphs, Array <fl | |||
| const float scale = font->height * font->horizontalScale; | |||
| const int num = xOffsets.size(); | |||
| 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 | |||
| if (num > 0) | |||
| { | |||
| 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; | |||
| jassert (angle1 <= angle2 + float_Pi); | |||
| angle1 -= angleIncrement; | |||
| while (angle1 > angle2) | |||
| { | |||
| destPath.lineTo (midX + width * sinf (angle1), | |||
| midY + width * cosf (angle1)); | |||
| angle -= 0.1f; | |||
| 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]); | |||
| #if SUPPORT_10_4_FONTS | |||
| if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) | |||
| { | |||
| ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); | |||
| 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; | |||
| 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 <int> 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 <int>& glyphs, Array <float>& xOffsets) | |||
| void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& 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 <int>& 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); | |||
| for (int i = 0; i < length; ++i) | |||
| g[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text[i]); | |||
| while (*t != 0) | |||
| dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); | |||
| 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]); | |||
| #if SUPPORT_10_4_FONTS | |||
| if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) | |||
| { | |||
| ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); | |||
| 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; | |||
| 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 <int> 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 <int>& glyphs, Array <float>& xOffsets) | |||
| void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& 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 <int>& 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); | |||
| for (int i = 0; i < length; ++i) | |||
| g[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text[i]); | |||
| while (*t != 0) | |||
| dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); | |||
| 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 | |||
| { | |||
| @@ -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(); | |||
| @@ -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::addPath (const Path& path, const AffineTransform& transform) throw() | |||
| void EdgeTable::clearLineSection (const int y, int minX, int maxX) throw() | |||
| { | |||
| const int bottomLimit = height << 8; | |||
| PathFlatteningIterator iter (path, transform); | |||
| // int* line = table + lineStrideElements * y; | |||
| while (iter.next()) | |||
| { | |||
| 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)); | |||
| //============================================================================== | |||
| void EdgeTable::clipToRectangle (const Rectangle& r) throw() | |||
| { | |||
| const int rectTop = jmax (0, r.getY() - top); | |||
| const int rectBottom = jmin (height, r.getBottom() - top); | |||
| addEdgePoint (roundDoubleToInt (x1 + multiplier * (y1 - oldY1)), | |||
| y1 >> 8, winding * step); | |||
| for (int i = rectTop - 1; --i >= 0;) | |||
| table [lineStrideElements * i] = 0; | |||
| y1 += step; | |||
| } | |||
| } | |||
| } | |||
| for (int i = rectBottom; i < height; ++i) | |||
| table [lineStrideElements * i] = 0; | |||
| if (! path.isUsingNonZeroWinding()) | |||
| for (int i = rectTop; i < rectBottom; ++i) | |||
| { | |||
| 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::intersectWith (const EdgeTable& other) | |||
| void EdgeTable::clipToEdgeTable (const EdgeTable& other) | |||
| { | |||
| } | |||
| void EdgeTable::generateFromImageAlpha (Image& image, int x, int y) throw() | |||
| void EdgeTable::clipToImageAlpha (Image& image, int x, int y) throw() | |||
| { | |||
| }*/ | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -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(); | |||
| }; | |||
| @@ -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); | |||
| @@ -309,17 +309,21 @@ void Font::getGlyphPositions (const String& text, Array <int>& glyphs, Array <fl | |||
| const float scale = font->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)); | |||
| } | |||
| } | |||
| @@ -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); | |||
| @@ -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(); | |||
| @@ -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)); | |||
| angle -= 0.1f; | |||
| if (angle2 > angle1 + float_Pi | |||
| || (angle2 < angle1 && angle2 >= angle1 - float_Pi)) | |||
| { | |||
| if (angle2 > angle1) | |||
| angle2 -= 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; | |||
| } | |||
| } | |||
| 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); | |||
| @@ -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 | |||
| { | |||
| @@ -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]); | |||
| #if SUPPORT_10_4_FONTS | |||
| if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) | |||
| { | |||
| ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); | |||
| 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; | |||
| 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 <int> 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 <int>& glyphs, Array <float>& xOffsets) | |||
| void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& 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)); | |||
| juce_free (advances); | |||
| 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 (glyphs); | |||
| } | |||
| bool getOutlineForGlyph (int glyphNumber, Path& path) | |||
| @@ -247,15 +320,28 @@ private: | |||
| AffineTransform pathTransform; | |||
| #endif | |||
| void createGlyphsForString (const String& text, Array <int>& 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); | |||
| for (int i = 0; i < length; ++i) | |||
| g[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text[i]); | |||
| while (*t != 0) | |||
| dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); | |||
| return g; | |||
| } | |||
| // Reads a CGFontRef's character map table to convert unicode into glyph numbers | |||