From 405d934e6839ed41aa6caf7ccedc68f3c4557e11 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Mon, 9 Nov 2009 12:00:18 +0000 Subject: [PATCH] Massive, long-overdue spring-cleaning and refactoring of the LowLevelGraphicsContext, Typeface and Font classes. Functions such as glyph rendering can now be handled directly by a native graphics context (and glyph rendering is now implemented natively on the Mac). The Typeface class is now an abstract base class with much minimal functionality, and platform-specific subclasses are used for font loading. A new class CustomTypeface lets you load and save typefaces in the old juce binary typeface format. For most people, these changes probably won't require you to alter your code, but you might need to change a few classnames in your code if you're using typefaces directly. --- build/macosx/Juce.xcodeproj/project.pbxproj | 6 - extras/juce demo/build/linux/JuceDemo.make | 34 +- .../build/mac/Jucer.xcodeproj/project.pbxproj | 8 - juce_amalgamated.cpp | 5045 +++++++++-------- juce_amalgamated.h | 703 ++- .../lookandfeel/juce_LookAndFeel.cpp | 12 +- .../graphics/brushes/juce_GradientBrush.cpp | 11 +- src/gui/graphics/brushes/juce_ImageBrush.cpp | 9 +- .../brushes/juce_SolidColourBrush.cpp | 22 +- src/gui/graphics/contexts/juce_Graphics.cpp | 229 +- src/gui/graphics/contexts/juce_Graphics.h | 4 +- .../contexts/juce_LowLevelGraphicsContext.h | 34 +- ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 183 +- .../juce_LowLevelGraphicsPostScriptRenderer.h | 44 +- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 534 +- .../juce_LowLevelGraphicsSoftwareRenderer.h | 71 +- src/gui/graphics/fonts/juce_Font.cpp | 622 +- src/gui/graphics/fonts/juce_Font.h | 122 +- .../graphics/fonts/juce_GlyphArrangement.cpp | 792 +-- .../graphics/fonts/juce_GlyphArrangement.h | 25 +- src/gui/graphics/fonts/juce_Typeface.cpp | 674 +-- src/gui/graphics/fonts/juce_Typeface.h | 455 +- src/native/juce_mac_NativeCode.mm | 4 +- src/native/linux/juce_linux_Fonts.cpp | 169 +- src/native/mac/juce_iphone_Fonts.mm | 352 -- .../mac/juce_mac_CoreGraphicsContext.mm | 289 +- src/native/mac/juce_mac_Fonts.mm | 530 +- src/native/windows/juce_win32_Fonts.cpp | 344 +- 28 files changed, 5522 insertions(+), 5805 deletions(-) delete mode 100644 src/native/mac/juce_iphone_Fonts.mm diff --git a/build/macosx/Juce.xcodeproj/project.pbxproj b/build/macosx/Juce.xcodeproj/project.pbxproj index 506e35952e..7458987b73 100644 --- a/build/macosx/Juce.xcodeproj/project.pbxproj +++ b/build/macosx/Juce.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 840F80BC092B399D005E7B4E /* juce.h in Headers */ = {isa = PBXBuildFile; fileRef = 840F80BB092B399D005E7B4E /* juce.h */; }; 84816E5710809D07008FEC33 /* juce_iphone_Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5110809D07008FEC33 /* juce_iphone_Audio.cpp */; }; - 84816E5810809D07008FEC33 /* juce_iphone_Fonts.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5210809D07008FEC33 /* juce_iphone_Fonts.mm */; }; 84816E5910809D07008FEC33 /* juce_iphone_MessageManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5310809D07008FEC33 /* juce_iphone_MessageManager.mm */; }; 84816E5A10809D07008FEC33 /* juce_iphone_MiscUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5410809D07008FEC33 /* juce_iphone_MiscUtilities.mm */; }; 84816E5C10809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5610809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm */; }; @@ -448,7 +447,6 @@ 8481700E10809E00008FEC33 /* juce_InterprocessConnectionServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E959104036B3006A1807 /* juce_InterprocessConnectionServer.cpp */; }; 8481700F10809E00008FEC33 /* juce_InterProcessLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E9DA104036D6006A1807 /* juce_InterProcessLock.cpp */; }; 8481701010809E00008FEC33 /* juce_iphone_Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5110809D07008FEC33 /* juce_iphone_Audio.cpp */; }; - 8481701110809E00008FEC33 /* juce_iphone_Fonts.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5210809D07008FEC33 /* juce_iphone_Fonts.mm */; }; 8481701210809E00008FEC33 /* juce_iphone_MessageManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5310809D07008FEC33 /* juce_iphone_MessageManager.mm */; }; 8481701310809E00008FEC33 /* juce_iphone_MiscUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5410809D07008FEC33 /* juce_iphone_MiscUtilities.mm */; }; 8481701410809E00008FEC33 /* juce_iphone_UIViewComponentPeer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84816E5610809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm */; }; @@ -1218,7 +1216,6 @@ 8456EC6908A2A6F00087C412 /* JUCE changelist.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = "JUCE changelist.txt"; path = "../../docs/JUCE changelist.txt"; sourceTree = SOURCE_ROOT; }; 84816E3510809B4F008FEC33 /* libjucedebug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjucedebug.a; sourceTree = BUILT_PRODUCTS_DIR; }; 84816E5110809D07008FEC33 /* juce_iphone_Audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = juce_iphone_Audio.cpp; sourceTree = ""; }; - 84816E5210809D07008FEC33 /* juce_iphone_Fonts.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_iphone_Fonts.mm; sourceTree = ""; }; 84816E5310809D07008FEC33 /* juce_iphone_MessageManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_iphone_MessageManager.mm; sourceTree = ""; }; 84816E5410809D07008FEC33 /* juce_iphone_MiscUtilities.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_iphone_MiscUtilities.mm; sourceTree = ""; }; 84816E5610809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_iphone_UIViewComponentPeer.mm; sourceTree = ""; }; @@ -1917,7 +1914,6 @@ 8484E9D6103C95A6008B7C6C /* juce_posix_SharedCode.h */, 8484E9D7103C95A6008B7C6C /* juce_posix_NamedPipe.cpp */, 84816E5110809D07008FEC33 /* juce_iphone_Audio.cpp */, - 84816E5210809D07008FEC33 /* juce_iphone_Fonts.mm */, 84816E5310809D07008FEC33 /* juce_iphone_MessageManager.mm */, 84816E5410809D07008FEC33 /* juce_iphone_MiscUtilities.mm */, 84816E5610809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm */, @@ -3731,7 +3727,6 @@ 8481700E10809E00008FEC33 /* juce_InterprocessConnectionServer.cpp in Sources */, 8481700F10809E00008FEC33 /* juce_InterProcessLock.cpp in Sources */, 8481701010809E00008FEC33 /* juce_iphone_Audio.cpp in Sources */, - 8481701110809E00008FEC33 /* juce_iphone_Fonts.mm in Sources */, 8481701210809E00008FEC33 /* juce_iphone_MessageManager.mm in Sources */, 8481701310809E00008FEC33 /* juce_iphone_MiscUtilities.mm in Sources */, 8481701410809E00008FEC33 /* juce_iphone_UIViewComponentPeer.mm in Sources */, @@ -4179,7 +4174,6 @@ 84F1ECE91040370A006A1807 /* juce_JPEGLoader.cpp in Sources */, 84F1ECEA1040370A006A1807 /* juce_PNGLoader.cpp in Sources */, 84816E5710809D07008FEC33 /* juce_iphone_Audio.cpp in Sources */, - 84816E5810809D07008FEC33 /* juce_iphone_Fonts.mm in Sources */, 84816E5910809D07008FEC33 /* juce_iphone_MessageManager.mm in Sources */, 84816E5A10809D07008FEC33 /* juce_iphone_MiscUtilities.mm in Sources */, 84816E5C10809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm in Sources */, diff --git a/extras/juce demo/build/linux/JuceDemo.make b/extras/juce demo/build/linux/JuceDemo.make index 9e4e36c1f7..ebb2ea0774 100644 --- a/extras/juce demo/build/linux/JuceDemo.make +++ b/extras/juce demo/build/linux/JuceDemo.make @@ -39,10 +39,10 @@ ifeq ($(CONFIG),Release) endif OBJECTS := \ - $(OBJDIR)/ApplicationStartup.o \ $(OBJDIR)/BinaryData.o \ - $(OBJDIR)/MainDemoWindow.o \ + $(OBJDIR)/ApplicationStartup.o \ $(OBJDIR)/juce_LibrarySource.o \ + $(OBJDIR)/MainDemoWindow.o \ $(OBJDIR)/AudioDemoLatencyPage.o \ $(OBJDIR)/AudioDemoPlaybackPage.o \ $(OBJDIR)/AudioDemoRecordPage.o \ @@ -54,13 +54,14 @@ OBJECTS := \ $(OBJDIR)/FontsAndTextDemo.o \ $(OBJDIR)/InterprocessCommsDemo.o \ $(OBJDIR)/OpenGLDemo.o \ - $(OBJDIR)/PathsAndTransformsDemo.o \ $(OBJDIR)/QuickTimeDemo.o \ $(OBJDIR)/TableDemo.o \ $(OBJDIR)/ThreadingDemo.o \ $(OBJDIR)/TreeViewDemo.o \ $(OBJDIR)/WebBrowserDemo.o \ $(OBJDIR)/WidgetsDemo.o \ + $(OBJDIR)/CodeEditorDemo.o \ + $(OBJDIR)/PathsAndTransformsDemo.o \ MKDIR_TYPE := msdos CMD := $(subst \,\\,$(ComSpec)$(COMSPEC)) @@ -102,17 +103,12 @@ else -@if exist $(subst /,\,$(OBJDIR)) rmdir /s /q $(subst /,\,$(OBJDIR)) endif -$(OBJDIR)/ApplicationStartup.o: ../../src/ApplicationStartup.cpp - -@$(CMD_MKOBJDIR) - @echo $(notdir $<) - @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" - $(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" -$(OBJDIR)/MainDemoWindow.o: ../../src/MainDemoWindow.cpp +$(OBJDIR)/ApplicationStartup.o: ../../src/ApplicationStartup.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" @@ -122,6 +118,11 @@ $(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" +$(OBJDIR)/MainDemoWindow.o: ../../src/MainDemoWindow.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + $(OBJDIR)/AudioDemoLatencyPage.o: ../../src/demos/AudioDemoLatencyPage.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @@ -177,11 +178,6 @@ $(OBJDIR)/OpenGLDemo.o: ../../src/demos/OpenGLDemo.cpp @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" -$(OBJDIR)/PathsAndTransformsDemo.o: ../../src/demos/PathsAndTransformsDemo.cpp - -@$(CMD_MKOBJDIR) - @echo $(notdir $<) - @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" - $(OBJDIR)/QuickTimeDemo.o: ../../src/demos/QuickTimeDemo.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @@ -212,5 +208,15 @@ $(OBJDIR)/WidgetsDemo.o: ../../src/demos/WidgetsDemo.cpp @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" +$(OBJDIR)/CodeEditorDemo.o: ../../src/demos/CodeEditorDemo.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + +$(OBJDIR)/PathsAndTransformsDemo.o: ../../src/demos/PathsAndTransformsDemo.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + -include $(OBJECTS:%.o=%.d) diff --git a/extras/the jucer/build/mac/Jucer.xcodeproj/project.pbxproj b/extras/the jucer/build/mac/Jucer.xcodeproj/project.pbxproj index f325e44a25..8ba61a9420 100644 --- a/extras/the jucer/build/mac/Jucer.xcodeproj/project.pbxproj +++ b/extras/the jucer/build/mac/Jucer.xcodeproj/project.pbxproj @@ -526,17 +526,11 @@ C0E91AC608A95435008D54AB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; PRODUCT_NAME = Jucer; - SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; WRAPPER_EXTENSION = app; }; name = Debug; @@ -548,12 +542,10 @@ ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; DEAD_CODE_STRIPPING = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_MODEL_TUNING = G5; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; ONLY_LINK_ESSENTIAL_SYMBOLS = YES; PRODUCT_NAME = Jucer; - SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; STRIP_INSTALLED_PRODUCT = YES; WRAPPER_EXTENSION = app; }; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index f56f69492b..7373863c09 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -44524,6 +44524,15 @@ void CodeEditorComponent::paint (Graphics& g) } } +void CodeEditorComponent::setScrollbarThickness (const int thickness) throw() +{ + if (scrollbarThickness != thickness) + { + scrollbarThickness = thickness; + resized(); + } +} + void CodeEditorComponent::handleAsyncUpdate() { rebuildLineTokens(); @@ -56425,6 +56434,11 @@ bool FileBrowserComponent::currentFileIsValid() const return false; } +const File FileBrowserComponent::getHighlightedFile() const throw() +{ + return fileListComponent->getSelectedFile(); +} + const File FileBrowserComponent::getRoot() const { return currentRoot; @@ -63177,7 +63191,7 @@ LookAndFeel::LookAndFeel() setColour (standardColours [i], Colour (standardColours [i + 1])); if (defaultSansName.isEmpty()) - Typeface::getDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName); + Font::getPlatformDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName); defaultSans = defaultSansName; defaultSerif = defaultSerifName; @@ -63261,14 +63275,16 @@ const Typeface::Ptr LookAndFeel::getTypefaceForFont (const Font& font) { String faceName (font.getTypefaceName()); - if (faceName == Typeface::defaultTypefaceNameSans) + if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultSans; - else if (faceName == Typeface::defaultTypefaceNameSerif) + else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultSerif; - else if (faceName == Typeface::defaultTypefaceNameMono) + else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultFixed; - return new Typeface (faceName, font.isBold(), font.isItalic()); + Font f (font); + f.setTypefaceName (faceName); + return Typeface::createSystemTypefaceFor (f); } void LookAndFeel::setDefaultSansSerifTypefaceName (const String& newName) @@ -71985,7 +72001,8 @@ public: : owner (owner_), h (h_), s (s_), v (v_), lastHue (0.0f), - edge (edgeSize) + edge (edgeSize), + colours (0) { addAndMakeVisible (marker = new ColourSpaceMarker()); setMouseCursor (MouseCursor::CrosshairCursor); @@ -71994,32 +72011,40 @@ public: ~ColourSpaceView() { deleteAllChildren(); + delete colours; } void paint (Graphics& g) { - const float hue = h; - - const float xScale = 1.0f / (getWidth() - edge * 2); - const float yScale = 1.0f / (getHeight() - edge * 2); - - const Rectangle clip (g.getClipBounds()); - const int x1 = jmax (clip.getX(), edge) & ~1; - const int x2 = jmin (clip.getRight(), getWidth() - edge) | 1; - const int y1 = jmax (clip.getY(), edge) & ~1; - const int y2 = jmin (clip.getBottom(), getHeight() - edge) | 1; - - for (int y = y1; y < y2; y += 2) + if (colours == 0) { - const float v = jlimit (0.0f, 1.0f, 1.0f - (y - edge) * yScale); + const int width = getWidth() / 2; + const int height = getHeight() / 2; + colours = new Image (Image::RGB, width, height, false); - for (int x = x1; x < x2; x += 2) + int ls, ps; + char* data = (char*) colours->lockPixelDataReadWrite (0, 0, width, height, ls, ps); + + for (int y = 0; y < height; ++y) { - const float s = jlimit (0.0f, 1.0f, (x - edge) * xScale); - g.setColour (Colour (hue, s, v, 1.0f)); - g.fillRect (x, y, 2, 2); + const float v = 1.0f - y / (float) height; + + for (int x = 0; x < width; ++x) + { + const float s = x / (float) width; + const Colour col (h, s, v, 1.0f); + + PixelRGB* const pix = (PixelRGB*) (data + ls * y + ps * x); + pix->set (col.getPixelARGB()); + } } + + colours->releasePixelDataReadWrite (data); } + + g.setOpacity (1.0f); + g.drawImage (colours, edge, edge, getWidth() - edge * 2, getHeight() - edge * 2, + 0, 0, colours->getWidth(), colours->getHeight()); } void mouseDown (const MouseEvent& e) @@ -72040,20 +72065,29 @@ public: if (lastHue != h) { lastHue = h; + deleteAndZero (colours); repaint(); } - resized(); + updateMarker(); } void resized() + { + deleteAndZero (colours); + updateMarker(); + } + +private: + Image* colours; + + void updateMarker() const throw() { marker->setBounds (roundFloatToInt ((getWidth() - edge * 2) * s), roundFloatToInt ((getHeight() - edge * 2) * (1.0f - v)), edge * 2, edge * 2); } -private: ColourSpaceView (const ColourSpaceView&); const ColourSpaceView& operator= (const ColourSpaceView&); }; @@ -77496,13 +77530,15 @@ bool GradientBrush::isInvisible() const throw() void GradientBrush::paintPath (LowLevelGraphicsContext& context, const Path& path, const AffineTransform& transform) throw() { - context.fillPathWithGradient (path, transform, gradient, EdgeTable::Oversampling_4times); + context.setGradient (gradient); + context.fillPath (path, transform, EdgeTable::Oversampling_4times); } void GradientBrush::paintRectangle (LowLevelGraphicsContext& context, int x, int y, int w, int h) throw() { - context.fillRectWithGradient (x, y, w, h, gradient); + context.setGradient (gradient); + context.fillRect (x, y, w, h, false); } void GradientBrush::paintAlphaChannel (LowLevelGraphicsContext& context, @@ -77512,7 +77548,10 @@ void GradientBrush::paintAlphaChannel (LowLevelGraphicsContext& context, context.saveState(); if (context.reduceClipRegion (x, y, w, h)) - context.fillAlphaChannelWithGradient (alphaChannelImage, imageX, imageY, gradient); + { + context.setGradient (gradient); + context.fillAlphaChannel (alphaChannelImage, imageX, imageY); + } context.restoreState(); } @@ -77613,7 +77652,8 @@ void ImageBrush::paintRectangle (LowLevelGraphicsContext& context, while (x < right) { - context.blendImage (*image, x, y, iw, ih, 0, 0, opacity); + context.setOpacity (opacity); + context.blendImage (*image, x, y, iw, ih, 0, 0); x += iw; } @@ -77630,6 +77670,7 @@ void ImageBrush::paintPath (LowLevelGraphicsContext& context, if (image != 0) { Rectangle clip (context.getClipBounds()); + context.setOpacity (opacity); { float x, y, w, h; @@ -77658,7 +77699,7 @@ void ImageBrush::paintPath (LowLevelGraphicsContext& context, while (x < right) { - context.fillPathWithImage (path, transform, *image, x, y, opacity, EdgeTable::Oversampling_4times); + context.fillPathWithImage (path, transform, *image, x, y, EdgeTable::Oversampling_4times); x += iw; } @@ -77675,6 +77716,7 @@ void ImageBrush::paintAlphaChannel (LowLevelGraphicsContext& context, if (image != 0 && context.reduceClipRegion (x, y, w, h)) { + context.setOpacity (opacity); const Rectangle clip (context.getClipBounds()); x = clip.getX(); y = clip.getY(); @@ -77695,7 +77737,7 @@ void ImageBrush::paintAlphaChannel (LowLevelGraphicsContext& context, { context.fillAlphaChannelWithImage (alphaChannelImage, imageX, imageY, *image, - x, y, opacity); + x, y); x += iw; } @@ -77749,15 +77791,15 @@ bool SolidColourBrush::isInvisible() const throw() void SolidColourBrush::paintPath (LowLevelGraphicsContext& context, const Path& path, const AffineTransform& transform) throw() { - if (! colour.isTransparent()) - context.fillPathWithColour (path, transform, colour, EdgeTable::Oversampling_4times); + context.setColour (colour); + context.fillPath (path, transform, EdgeTable::Oversampling_4times); } void SolidColourBrush::paintRectangle (LowLevelGraphicsContext& context, int x, int y, int w, int h) throw() { - if (! colour.isTransparent()) - context.fillRectWithColour (x, y, w, h, colour, false); + context.setColour (colour); + context.fillRect (x, y, w, h, false); } void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, @@ -77769,7 +77811,10 @@ void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, context.saveState(); if (context.reduceClipRegion (x, y, w, h)) - context.fillAlphaChannelWithColour (alphaChannelImage, imageX, imageY, colour); + { + context.setColour (colour); + context.fillAlphaChannel (alphaChannelImage, imageX, imageY); + } context.restoreState(); } @@ -77778,19 +77823,22 @@ void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, void SolidColourBrush::paintVerticalLine (LowLevelGraphicsContext& context, int x, float y1, float y2) throw() { - context.drawVerticalLine (x, y1, y2, colour); + context.setColour (colour); + context.drawVerticalLine (x, y1, y2); } void SolidColourBrush::paintHorizontalLine (LowLevelGraphicsContext& context, int y, float x1, float x2) throw() { - context.drawHorizontalLine (y, x1, x2, colour); + context.setColour (colour); + context.drawHorizontalLine (y, x1, x2); } void SolidColourBrush::paintLine (LowLevelGraphicsContext& context, float x1, float y1, float x2, float y2) throw() { - context.drawLine (x1, y1, x2, y2, colour); + context.setColour (colour); + context.drawLine (x1, y1, x2, y2); } END_JUCE_NAMESPACE @@ -79015,6 +79063,7 @@ Graphics::Graphics (Image& imageToDrawOnto) throw() state (new GraphicsState()), saveStatePending (false) { + resetToDefaultState(); } Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() @@ -79023,6 +79072,7 @@ Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() state (new GraphicsState()), saveStatePending (false) { + resetToDefaultState(); } Graphics::~Graphics() throw() @@ -79035,9 +79085,10 @@ Graphics::~Graphics() throw() void Graphics::resetToDefaultState() throw() { - setColour (Colours::black); - state->font.resetToDefaultState(); - state->quality = defaultQuality; + saveStateIfPending(); + context->setColour (Colours::black); + context->setFont (Font()); + context->setInterpolationQuality (defaultQuality); } bool Graphics::isVectorDevice() const throw() @@ -79136,32 +79187,52 @@ bool Graphics::clipRegionIntersects (const int x, const int y, void Graphics::setColour (const Colour& newColour) throw() { saveStateIfPending(); - state->colour = newColour; + deleteAndZero (state->brush); + context->setColour (newColour); } void Graphics::setOpacity (const float newOpacity) throw() { saveStateIfPending(); - state->colour = state->colour.withAlpha (newOpacity); + context->setOpacity (newOpacity); } void Graphics::setBrush (const Brush* const newBrush) throw() { saveStateIfPending(); delete state->brush; + state->brush = 0; if (newBrush != 0) - state->brush = newBrush->createCopy(); - else - state->brush = 0; + { + const SolidColourBrush* cb = dynamic_cast (newBrush); + + if (cb != 0) + { + setColour (cb->getColour()); + } + else + { + const GradientBrush* gb = dynamic_cast (newBrush); + + if (gb != 0) + { + setGradientFill (gb->getGradient()); + } + else + { + state->brush = newBrush->createCopy(); + } + } + } } void Graphics::setGradientFill (const ColourGradient& gradient) throw() { saveStateIfPending(); - delete state->brush; - state->brush = new GradientBrush (gradient); + deleteAndZero (state->brush); + context->setGradient (gradient); } void Graphics::setTiledImageFill (Image& imageToUse, @@ -79175,17 +79246,13 @@ void Graphics::setTiledImageFill (Image& imageToUse, } Graphics::GraphicsState::GraphicsState() throw() - : colour (Colours::black), - brush (0), - quality (defaultQuality) + : brush (0) { } Graphics::GraphicsState::GraphicsState (const GraphicsState& other) throw() - : colour (other.colour), - brush (other.brush != 0 ? other.brush->createCopy() : 0), - font (other.font), - quality (other.quality) + : brush (other.brush != 0 ? other.brush->createCopy() : 0), + font (other.font) { } @@ -79198,13 +79265,15 @@ void Graphics::setFont (const Font& newFont) throw() { saveStateIfPending(); state->font = newFont; + context->setFont (newFont); } void Graphics::setFont (const float newFontHeight, const int newFontStyleFlags) throw() { saveStateIfPending(); - state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0.0f); + state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0); + context->setFont (state->font); } void Graphics::drawSingleLineText (const String& text, @@ -79305,8 +79374,10 @@ void Graphics::fillRect (int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, width, height); + if (state->brush == 0) + context->fillRect (x, y, width, height, false); + else + state->brush->paintRectangle (*context, x, y, width, height); } void Graphics::fillRect (const Rectangle& r) const throw() @@ -79334,8 +79405,10 @@ void Graphics::setPixel (int x, int y) const throw() { if (context->clipRegionIntersects (x, y, 1, 1)) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, 1, 1); + if (state->brush == 0) + context->fillRect (x, y, 1, 1, false); + else + state->brush->paintRectangle (*context, x, y, 1, 1); } } @@ -79350,8 +79423,10 @@ void Graphics::fillAll (const Colour& colourToUse) const throw() { const Rectangle clip (context->getClipBounds()); - context->fillRectWithColour (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(), - colourToUse, false); + context->saveState(); + context->setColour (colourToUse); + context->fillRect (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(), false); + context->restoreState(); } } @@ -79360,8 +79435,10 @@ void Graphics::fillPath (const Path& path, { if ((! context->isClipEmpty()) && ! path.isEmpty()) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintPath (*context, path, transform); + if (state->brush == 0) + context->fillPath (path, transform, EdgeTable::Oversampling_4times); + else + state->brush->paintPath (*context, path, transform); } } @@ -79369,7 +79446,7 @@ void Graphics::strokePath (const Path& path, const PathStrokeType& strokeType, const AffineTransform& transform) const throw() { - if ((! state->colour.isTransparent()) || state->brush != 0) +// if ((! state->colour.isTransparent()) || state->brush != 0) { Path stroke; strokeType.createStrokedPath (stroke, path, transform); @@ -79386,13 +79463,21 @@ void Graphics::drawRect (const int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - SolidColourBrush colourBrush (state->colour); - Brush& b = (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush); - - b.paintRectangle (*context, x, y, width, lineThickness); - b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness); + if (state->brush == 0) + { + context->fillRect (x, y, width, lineThickness, false); + context->fillRect (x, y + lineThickness, lineThickness, height - lineThickness * 2, false); + context->fillRect (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2, false); + context->fillRect (x, y + height - lineThickness, width, lineThickness, false); + } + else + { + Brush& b = *(state->brush); + b.paintRectangle (*context, x, y, width, lineThickness); + b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2); + b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2); + b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness); + } } void Graphics::drawRect (const float x, @@ -79435,7 +79520,9 @@ void Graphics::drawBevel (const int x, if (clipRegionIntersects (x, y, width, height)) { - const float oldOpacity = state->colour.getFloatAlpha(); + context->saveState(); + + const float oldOpacity = 1.0f;//xxx state->colour.getFloatAlpha(); const float ramp = oldOpacity / bevelThickness; for (int i = bevelThickness; --i >= 0;) @@ -79443,11 +79530,17 @@ void Graphics::drawBevel (const int x, const float op = useGradient ? ramp * (sharpEdgeOnOutside ? bevelThickness - i : i) : oldOpacity; - context->fillRectWithColour (x + i, y + i, width - i * 2, 1, topLeftColour.withMultipliedAlpha (op), false); - context->fillRectWithColour (x + i, y + i + 1, 1, height - i * 2 - 2, topLeftColour.withMultipliedAlpha (op * 0.75f), false); - context->fillRectWithColour (x + i, y + height - i - 1, width - i * 2, 1, bottomRightColour.withMultipliedAlpha (op), false); - context->fillRectWithColour (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, bottomRightColour.withMultipliedAlpha (op * 0.75f), false); + context->setColour (topLeftColour.withMultipliedAlpha (op)); + context->fillRect (x + i, y + i, width - i * 2, 1, false); + context->setColour (topLeftColour.withMultipliedAlpha (op * 0.75f)); + context->fillRect (x + i, y + i + 1, 1, height - i * 2 - 2, false); + context->setColour (bottomRightColour.withMultipliedAlpha (op)); + context->fillRect (x + i, y + height - i - 1, width - i * 2, 1, false); + context->setColour (bottomRightColour.withMultipliedAlpha (op * 0.75f)); + context->fillRect (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, false); } + + context->restoreState(); } } @@ -79553,9 +79646,12 @@ void Graphics::fillCheckerBoard (int x, int y, if (checkWidth > 0 && checkHeight > 0) { + context->saveState(); + if (colour1 == colour2) { - context->fillRectWithColour (x, y, width, height, colour1, false); + context->setColour (colour1); + context->fillRect (x, y, width, height, false); } else { @@ -79570,29 +79666,37 @@ void Graphics::fillCheckerBoard (int x, int y, int cx = cy; for (int xx = x; xx < right; xx += checkWidth) - context->fillRectWithColour (xx, y, - jmin (checkWidth, right - xx), - jmin (checkHeight, bottom - y), - ((cx++ & 1) == 0) ? colour1 : colour2, - false); + { + context->setColour (((cx++ & 1) == 0) ? colour1 : colour2); + context->fillRect (xx, y, + jmin (checkWidth, right - xx), + jmin (checkHeight, bottom - y), + false); + } ++cy; y += checkHeight; } } + + context->restoreState(); } } void Graphics::drawVerticalLine (const int x, float top, float bottom) const throw() { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintVerticalLine (*context, x, top, bottom); + if (state->brush == 0) + context->drawVerticalLine (x, top, bottom); + else + state->brush->paintVerticalLine (*context, x, top, bottom); } void Graphics::drawHorizontalLine (const int y, float left, float right) const throw() { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintHorizontalLine (*context, y, left, right); + if (state->brush == 0) + context->drawHorizontalLine (y, left, right); + else + state->brush->paintHorizontalLine (*context, y, left, right); } void Graphics::drawLine (float x1, float y1, @@ -79600,8 +79704,10 @@ void Graphics::drawLine (float x1, float y1, { if (! context->isClipEmpty()) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintLine (*context, x1, y1, x2, y2); + if (state->brush == 0) + context->drawLine (x1, y1, x2, y2); + else + state->brush->paintLine (*context, x1, y1, x2, y2); } } @@ -79673,7 +79779,7 @@ void Graphics::drawDashedLine (const float startX, void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality) throw() { saveStateIfPending(); - state->quality = newQuality; + context->setInterpolationQuality (newQuality); } void Graphics::drawImageAt (const Image* const imageToDraw, @@ -79779,18 +79885,26 @@ void Graphics::drawImage (const Image* const imageToDraw, if (fillAlphaChannelWithCurrentBrush) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush) - .paintAlphaChannel (*context, *imageToDraw, - dx - sx, dy - sy, - dx, dy, - dw, dh); + if (state->brush == 0) + { + context->saveState(); + + if (context->reduceClipRegion (dx, dy, dw, dh)) + context->fillAlphaChannel (*imageToDraw, dx - sx, dy - sy); + + context->restoreState(); + } + else + { + state->brush->paintAlphaChannel (*context, *imageToDraw, + dx - sx, dy - sy, + dx, dy, dw, dh); + } } else { context->blendImage (*imageToDraw, - dx, dy, dw, dh, sx, sy, - state->colour.getFloatAlpha()); + dx, dy, dw, dh, sx, sy); } } else @@ -79815,7 +79929,7 @@ void Graphics::drawImage (const Image* const imageToDraw, { Image temp (imageToDraw->getFormat(), tw, th, true); Graphics g (temp); - g.setImageResamplingQuality (state->quality); +//xxx g.setImageResamplingQuality (state->quality); g.setOrigin (dx - tx, dy - ty); g.drawImage (imageToDraw, @@ -79823,9 +79937,19 @@ void Graphics::drawImage (const Image* const imageToDraw, sx, sy, sw, sh, false); - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush) - .paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + if (state->brush == 0) + { + context->saveState(); + + if (context->reduceClipRegion (tx, ty, tw, th)) + context->fillAlphaChannel (temp, tx, ty); + + context->restoreState(); + } + else + { + state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + } } } } @@ -79838,9 +79962,7 @@ void Graphics::drawImage (const Image* const imageToDraw, .scaled (dw / (float) sw, dh / (float) sh) .translated ((float) dx, - (float) dy), - state->colour.getFloatAlpha(), - state->quality); + (float) dy)); } } } @@ -79883,7 +80005,7 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, { Image temp (imageToDraw->getFormat(), tw, th, true); Graphics g (temp); - g.setImageResamplingQuality (state->quality); +//xxx g.setImageResamplingQuality (state->quality); g.drawImageTransformed (imageToDraw, sourceClipX, @@ -79893,8 +80015,19 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, transform.translated ((float) -tx, (float) -ty), false); - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + if (state->brush == 0) + { + context->saveState(); + + if (context->reduceClipRegion (tx, ty, tw, th)) + context->fillAlphaChannel (temp, tx, ty); + + context->restoreState(); + } + else + { + state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + } } } else @@ -79904,9 +80037,7 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, sourceClipY, sourceClipWidth, sourceClipHeight, - transform, - state->colour.getFloatAlpha(), - state->quality); + transform); } } } @@ -80001,7 +80132,9 @@ LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputSt totalHeight (totalHeight_), xOffset (0), yOffset (0), - needToClip (true) + needToClip (true), + colour (Colours::black), + gradient (0) { clip = new RectangleList (Rectangle (0, 0, totalWidth_, totalHeight_)); @@ -80041,6 +80174,7 @@ LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputSt LowLevelGraphicsPostScriptRenderer::~LowLevelGraphicsPostScriptRenderer() { delete clip; + delete gradient; } bool LowLevelGraphicsPostScriptRenderer::isVectorDevice() const @@ -80092,21 +80226,28 @@ bool LowLevelGraphicsPostScriptRenderer::isClipEmpty() const } LowLevelGraphicsPostScriptRenderer::SavedState::SavedState (RectangleList* const clip_, - const int xOffset_, const int yOffset_) + const int xOffset_, const int yOffset_, + const Colour& colour_, ColourGradient* const gradient_, + const Font& font_) : clip (clip_), xOffset (xOffset_), - yOffset (yOffset_) + yOffset (yOffset_), + colour (colour_), + gradient (gradient_), + font (font_) { } LowLevelGraphicsPostScriptRenderer::SavedState::~SavedState() { delete clip; + delete gradient; } void LowLevelGraphicsPostScriptRenderer::saveState() { - stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset)); + stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset, colour, + gradient != 0 ? new ColourGradient (*gradient) : 0, font)); } void LowLevelGraphicsPostScriptRenderer::restoreState() @@ -80115,13 +80256,14 @@ void LowLevelGraphicsPostScriptRenderer::restoreState() if (top != 0) { - clip->swapWith (*top->clip); - + swapVariables (clip, top->clip); + swapVariables (gradient, top->gradient); + colour = top->colour; xOffset = top->xOffset; yOffset = top->yOffset; + font = top->font; stateStack.removeLast(); - needToClip = true; } else @@ -80261,76 +80403,93 @@ void LowLevelGraphicsPostScriptRenderer::writeTransform (const AffineTransform& << trans.mat12 << " ] concat "; } -void LowLevelGraphicsPostScriptRenderer::fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool /*replaceExistingContents*/) +void LowLevelGraphicsPostScriptRenderer::setColour (const Colour& colour_) { - writeClip(); - writeColour (colour); - - x += xOffset; - y += yOffset; - - out << x << ' ' << -(y + h) << ' ' << w << ' ' << h << " rectfill\n"; + colour = colour_; + deleteAndZero (gradient); } -void LowLevelGraphicsPostScriptRenderer::fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) +void LowLevelGraphicsPostScriptRenderer::setGradient (const ColourGradient& gradient_) { - Path p; - p.addRectangle ((float) x, (float) y, (float) w, (float) h); - - fillPathWithGradient (p, AffineTransform::identity, gradient, EdgeTable::Oversampling_256times); + delete gradient; + gradient = new ColourGradient (gradient_); } -void LowLevelGraphicsPostScriptRenderer::fillPathWithColour (const Path& path, const AffineTransform& t, - const Colour& colour, EdgeTable::OversamplingLevel /*quality*/) +void LowLevelGraphicsPostScriptRenderer::setOpacity (float opacity) { - writeClip(); - - Path p (path); - p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); - writePath (p); - - writeColour (colour); - - out << "fill\n"; } -void LowLevelGraphicsPostScriptRenderer::fillPathWithGradient (const Path& path, const AffineTransform& t, const ColourGradient& gradient, EdgeTable::OversamplingLevel /*quality*/) +void LowLevelGraphicsPostScriptRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) { - // this doesn't work correctly yet - it could be improved to handle solid gradients, but - // postscript can't do semi-transparent ones. - notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file - - writeClip(); - out << "gsave "; +} +void LowLevelGraphicsPostScriptRenderer::fillRect (int x, int y, int w, int h, const bool /*replaceExistingContents*/) +{ + if (gradient == 0) { + writeClip(); + writeColour (colour); + + x += xOffset; + y += yOffset; + + out << x << ' ' << -(y + h) << ' ' << w << ' ' << h << " rectfill\n"; + } + else + { + Path p; + p.addRectangle ((float) x, (float) y, (float) w, (float) h); + fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_256times); + } + +} + +void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t, + EdgeTable::OversamplingLevel /*quality*/) +{ + if (gradient == 0) + { + writeClip(); + Path p (path); p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); writePath (p); - out << "clip\n"; + + writeColour (colour); + + out << "fill\n"; } + else + { + // this doesn't work correctly yet - it could be improved to handle solid gradients, but + // postscript can't do semi-transparent ones. + notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file - int numColours = 256; - PixelARGB* const colours = gradient.createLookupTable (numColours); + writeClip(); + out << "gsave "; - for (int i = numColours; --i >= 0;) - colours[i].unpremultiply(); + { + Path p (path); + p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); + writePath (p); + out << "clip\n"; + } - const Rectangle bounds (clip->getBounds()); + const Rectangle bounds (clip->getBounds()); - // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the - // time-being, this just fills it with the average colour.. - writeColour (Colour (colours [numColours / 2].getARGB())); - out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n"; + // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the + // time-being, this just fills it with the average colour.. + writeColour (gradient->getColourAtPosition (0.5)); + out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n"; - juce_free (colours); - out << "grestore\n"; + out << "grestore\n"; + } } void LowLevelGraphicsPostScriptRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, const Image& sourceImage, int imageX, int imageY, - float opacity, EdgeTable::OversamplingLevel /*quality*/) + EdgeTable::OversamplingLevel /*quality*/) { writeClip(); @@ -80340,34 +80499,28 @@ void LowLevelGraphicsPostScriptRenderer::fillPathWithImage (const Path& path, co writePath (p); out << "clip\n"; - blendImage (sourceImage, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight(), 0, 0, opacity); + blendImage (sourceImage, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight(), 0, 0); out << "grestore\n"; } -void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithColour (const Image& /*clipImage*/, int x, int y, const Colour& colour) +void LowLevelGraphicsPostScriptRenderer::fillAlphaChannel (const Image& /*clipImage*/, int x, int y) { x += xOffset; y += yOffset; writeClip(); - writeColour (colour); - notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file -} - -void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithGradient (const Image& /*alphaChannelImage*/, int imageX, int imageY, const ColourGradient& /*gradient*/) -{ - imageX += xOffset; - imageY += yOffset; - - writeClip(); + if (gradient == 0) + { + writeColour (colour); + } notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file } void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithImage (const Image& /*alphaImage*/, int alphaImageX, int alphaImageY, - const Image& /*fillerImage*/, int fillerImageX, int fillerImageY, float /*opacity*/) + const Image& /*fillerImage*/, int fillerImageX, int fillerImageY) { alphaImageX += xOffset; alphaImageY += yOffset; @@ -80380,12 +80533,11 @@ void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithImage (const Image& notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file } -void LowLevelGraphicsPostScriptRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) +void LowLevelGraphicsPostScriptRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { blendImageWarping (sourceImage, sx, sy, dw, dh, - AffineTransform::translation ((float) dx, (float) dy), - opacity, Graphics::highResamplingQuality); + AffineTransform::translation ((float) dx, (float) dy)); } void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, @@ -80453,9 +80605,7 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, void LowLevelGraphicsPostScriptRenderer::blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t, - float /*opacity*/, - const Graphics::ResamplingQuality /*quality*/) + const AffineTransform& t) { const int w = jmin (sourceImage.getWidth(), srcClipX + srcClipW); const int h = jmin (sourceImage.getHeight(), srcClipY + srcClipH); @@ -80497,22 +80647,36 @@ void LowLevelGraphicsPostScriptRenderer::blendImageWarping (const Image& sourceI needToClip = true; } -void LowLevelGraphicsPostScriptRenderer::drawLine (double x1, double y1, double x2, double y2, const Colour& colour) +void LowLevelGraphicsPostScriptRenderer::drawLine (double x1, double y1, double x2, double y2) { Path p; p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f); - - fillPathWithColour (p, AffineTransform::identity, colour, EdgeTable::Oversampling_256times); + fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_256times); } -void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, double top, double bottom, const Colour& col) +void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, double top, double bottom) { - drawLine (x, top, x, bottom, col); + drawLine (x, top, x, bottom); } -void LowLevelGraphicsPostScriptRenderer::drawHorizontalLine (const int y, double left, double right, const Colour& col) +void LowLevelGraphicsPostScriptRenderer::drawHorizontalLine (const int y, double left, double right) { - drawLine (left, y, right, y, col); + drawLine (left, y, right, y); +} + +void LowLevelGraphicsPostScriptRenderer::setFont (const Font& newFont) +{ + font = newFont; +} + +void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, float x, float y) +{ + drawGlyph (glyphNumber, AffineTransform::translation (x, y)); +} + +void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) +{ + font.renderGlyphIndirectly (*this, glyphNumber, transform); } END_JUCE_NAMESPACE @@ -81385,7 +81549,7 @@ static void transformedImageRender (Image& destImage, if (((unsigned int) iy) < (unsigned int) srcClipHeight) { const SrcPixelType* const src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - dest->set (*src); + dest->blend (*src); } } @@ -81542,7 +81706,9 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image : image (image_), xOffset (0), yOffset (0), - stateStack (20) + stateStack (20), + colour (0xff000000), + gradient (0) { clip = new RectangleList (Rectangle (0, 0, image_.getWidth(), image_.getHeight())); } @@ -81550,6 +81716,7 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() { delete clip; + delete gradient; } bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const @@ -81597,21 +81764,30 @@ bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const } LowLevelGraphicsSoftwareRenderer::SavedState::SavedState (RectangleList* const clip_, - const int xOffset_, const int yOffset_) + const int xOffset_, const int yOffset_, + const Font& font_, const Colour& colour_, ColourGradient* gradient_, + Graphics::ResamplingQuality interpolationQuality_) : clip (clip_), xOffset (xOffset_), - yOffset (yOffset_) + yOffset (yOffset_), + font (font_), + colour (colour_), + gradient (gradient_), + interpolationQuality (interpolationQuality_) { } LowLevelGraphicsSoftwareRenderer::SavedState::~SavedState() { delete clip; + delete gradient; } void LowLevelGraphicsSoftwareRenderer::saveState() { - stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset)); + stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset, + font, colour, gradient != 0 ? new ColourGradient (*gradient) : 0, + interpolationQuality)); } void LowLevelGraphicsSoftwareRenderer::restoreState() @@ -81620,10 +81796,13 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() if (top != 0) { - clip->swapWith (*top->clip); - + swapVariables (clip, top->clip); xOffset = top->xOffset; yOffset = top->yOffset; + font = top->font; + colour = top->colour; + swapVariables (gradient, top->gradient); + interpolationQuality = top->interpolationQuality; stateStack.removeLast(); } @@ -81633,14 +81812,49 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() } } -void LowLevelGraphicsSoftwareRenderer::fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) +void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour_) { - x += xOffset; - y += yOffset; + deleteAndZero (gradient); + colour = colour_; +} - for (RectangleList::Iterator i (*clip); i.next();) +void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient_) +{ + delete gradient; + gradient = new ColourGradient (gradient_); +} + +void LowLevelGraphicsSoftwareRenderer::setOpacity (float opacity) +{ + colour = colour.withAlpha (opacity); +} + +void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) +{ + interpolationQuality = quality; +} + +void LowLevelGraphicsSoftwareRenderer::fillRect (int x, int y, int w, int h, const bool replaceExistingContents) +{ + if (gradient != 0) { - clippedFillRectWithColour (*i.getRectangle(), x, y, w, h, colour, replaceExistingContents); + if (replaceExistingContents && ! gradient->isOpaque()) + { + for (RectangleList::Iterator i (*clip); i.next();) + clippedFillRectWithColour (*i.getRectangle(), x + xOffset, y + yOffset, w, h, Colours::transparentBlack, true); + } + + Path p; + p.addRectangle ((float) x, (float) y, (float) w, (float) h); + fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_none); + } + else + { + x += xOffset; + y += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + clippedFillRectWithColour (*i.getRectangle(), x, y, w, h, colour, replaceExistingContents); } } @@ -81675,13 +81889,6 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillRectWithColour (const Rectangl } } -void LowLevelGraphicsSoftwareRenderer::fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) -{ - Path p; - p.addRectangle ((float) x, (float) y, (float) w, (float) h); - fillPathWithGradient (p, AffineTransform::identity, gradient, EdgeTable::Oversampling_none); -} - bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, int& x, int& y, int& w, int& h) const @@ -81700,19 +81907,19 @@ bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int return Rectangle::intersectRectangles (x, y, w, h, clipX, clipY, clipW, clipH); } -void LowLevelGraphicsSoftwareRenderer::fillPathWithColour (const Path& path, const AffineTransform& t, - const Colour& colour, EdgeTable::OversamplingLevel quality) +void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality) { for (RectangleList::Iterator i (*clip); i.next();) { const Rectangle& r = *i.getRectangle(); - clippedFillPathWithColour (r.getX(), r.getY(), r.getWidth(), r.getHeight(), path, t, colour, quality); + clippedFillPath (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + path, transform, quality); } } -void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithColour (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& t, - const Colour& colour, EdgeTable::OversamplingLevel quality) +void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, + const AffineTransform& t, EdgeTable::OversamplingLevel quality) { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); int cx, cy, cw, ch; @@ -81720,139 +81927,114 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithColour (int clipX, int if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) { EdgeTable edgeTable (0, ch, quality); - edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); int stride, pixelStride; uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); - if (image.getFormat() == Image::RGB) + if (gradient != 0) { - jassert (pixelStride == 3); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (pixelStride == 1); - AlphaBitmapRenderer renderer (pixels, stride); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } + ColourGradient g2 (*gradient); - image.releasePixelDataReadWrite (pixels); - } -} + const bool isIdentity = g2.transform.isIdentity(); + if (isIdentity) + { + g2.x1 += xOffset - cx; + g2.x2 += xOffset - cx; + g2.y1 += yOffset - cy; + g2.y2 += yOffset - cy; + } + else + { + g2.transform = g2.transform.translated ((float) (xOffset - cx), + (float) (yOffset - cy)); + } -void LowLevelGraphicsSoftwareRenderer::fillPathWithGradient (const Path& path, const AffineTransform& t, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) -{ - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); + int numLookupEntries; + PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); + jassert (numLookupEntries > 0); - clippedFillPathWithGradient (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, t, gradient, quality); - } -} + if (image.getFormat() == Image::RGB) + { + jassert (pixelStride == 3); -void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithGradient (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& t, - const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) -{ - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - int cx, cy, cw, ch; + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else if (image.getFormat() == Image::ARGB) + { + jassert (pixelStride == 4); - if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) - { - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else if (image.getFormat() == Image::SingleChannel) + { + jassertfalse // not done! + } - ColourGradient g2 (gradient); - - const bool isIdentity = g2.transform.isIdentity(); - if (isIdentity) - { - g2.x1 += xOffset - cx; - g2.x2 += xOffset - cx; - g2.y1 += yOffset - cy; - g2.y2 += yOffset - cy; + juce_free (lookupTable); } else { - g2.transform = g2.transform.translated ((float) (xOffset - cx), - (float) (yOffset - cy)); - } - - int numLookupEntries; - PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); - jassert (numLookupEntries > 0); - - EdgeTable edgeTable (0, ch, quality); - - edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); - - if (image.getFormat() == Image::RGB) - { - jassert (pixelStride == 3); - - if (g2.isRadial) + if (image.getFormat() == Image::RGB) { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } + jassert (pixelStride == 3); + SolidColourEdgeTableRenderer renderer (pixels, stride, colour); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); } - else + else if (image.getFormat() == Image::ARGB) { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + jassert (pixelStride == 4); + SolidColourEdgeTableRenderer renderer (pixels, stride, colour); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (pixelStride == 1); + AlphaBitmapRenderer renderer (pixels, stride); edgeTable.iterate (renderer, 0, 0, cw, ch, 0); } } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - } - else if (image.getFormat() == Image::SingleChannel) - { - jassertfalse // not done! - } - - juce_free (lookupTable); image.releasePixelDataReadWrite (pixels); } } void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& sourceImage, int imageX, int imageY, float opacity, EdgeTable::OversamplingLevel quality) + const Image& sourceImage, int imageX, int imageY, EdgeTable::OversamplingLevel quality) { imageX += xOffset; imageY += yOffset; @@ -81862,7 +82044,8 @@ void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, cons const Rectangle& r = *i.getRectangle(); clippedFillPathWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, transform, sourceImage, imageX, imageY, opacity, quality); + path, transform, sourceImage, imageX, imageY, + colour.getFloatAlpha(), quality); } } @@ -81934,7 +82117,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithImage (int x, int y, i } } -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithColour (const Image& clipImage, int x, int y, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::fillAlphaChannel (const Image& clipImage, int x, int y) { x += xOffset; y += yOffset; @@ -81943,114 +82126,101 @@ void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithColour (const Image& { const Rectangle& r = *i.getRectangle(); - clippedFillAlphaChannelWithColour (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - clipImage, x, y, colour); + clippedFillAlphaChannel (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + clipImage, x, y); } } -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithColour (int clipX, int clipY, int clipW, int clipH, const Image& clipImage, int x, int y, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& clipImage, int x, int y) { - int w = clipImage.getWidth(); - int h = clipImage.getHeight(); - int sx = 0; - int sy = 0; - - if (x < clipX) + if (gradient != 0) { - sx = clipX - x; - w -= clipX - x; - x = clipX; + if (Rectangle::intersectRectangles (clipX, clipY, clipW, clipH, x, y, clipImage.getWidth(), clipImage.getHeight())) + { + ColourGradient g2 (*gradient); + g2.x1 += xOffset - clipX; + g2.x2 += xOffset - clipX; + g2.y1 += yOffset - clipY; + g2.y2 += yOffset - clipY; + + Image temp (g2.isOpaque() ? Image::RGB : Image::ARGB, clipW, clipH, true); + LowLevelGraphicsSoftwareRenderer tempG (temp); + tempG.setGradient (g2); + tempG.fillRect (0, 0, clipW, clipH, false); + + clippedFillAlphaChannelWithImage (clipX, clipY, clipW, clipH, + clipImage, x, y, + temp, clipX, clipY, 1.0f); + } } - - if (y < clipY) + else { - sy = clipY - y; - h -= clipY - y; - y = clipY; - } + int w = clipImage.getWidth(); + int h = clipImage.getHeight(); + int sx = 0; + int sy = 0; - if (x + w > clipX + clipW) - w = clipX + clipW - x; + if (x < clipX) + { + sx = clipX - x; + w -= clipX - x; + x = clipX; + } - if (y + h > clipY + clipH) - h = clipY + clipH - y; + if (y < clipY) + { + sy = clipY - y; + h -= clipY - y; + y = clipY; + } - if (w > 0 && h > 0) - { - int stride, alphaStride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); + if (x + w > clipX + clipW) + w = clipX + clipW - x; - const uint8* const alphaValues - = clipImage.lockPixelDataReadOnly (sx, sy, w, h, alphaStride, pixelStride); + if (y + h > clipY + clipH) + h = clipY + clipH - y; + + if (w > 0 && h > 0) + { + int stride, alphaStride, pixelStride; + uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); + + const uint8* const alphaValues + = clipImage.lockPixelDataReadOnly (sx, sy, w, h, alphaStride, pixelStride); #if JUCE_BIG_ENDIAN - const uint8* const alphas = alphaValues; + const uint8* const alphas = alphaValues; #else - const uint8* const alphas = alphaValues + (clipImage.getFormat() == Image::ARGB ? 3 : 0); + const uint8* const alphas = alphaValues + (clipImage.getFormat() == Image::ARGB ? 3 : 0); #endif - if (image.getFormat() == Image::RGB) - { - blendAlphaMapRGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); + if (image.getFormat() == Image::RGB) + { + blendAlphaMapRGB (pixels, stride, + alphas, w, h, + pixelStride, alphaStride, + colour); + } + else if (image.getFormat() == Image::ARGB) + { + blendAlphaMapARGB (pixels, stride, + alphas, w, h, + pixelStride, alphaStride, + colour); + } + else + { + jassertfalse // not done! + } + + clipImage.releasePixelDataReadOnly (alphaValues); + image.releasePixelDataReadWrite (pixels); } - else if (image.getFormat() == Image::ARGB) - { - blendAlphaMapARGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); - } - else - { - jassertfalse // not done! - } - - clipImage.releasePixelDataReadOnly (alphaValues); - image.releasePixelDataReadWrite (pixels); - } -} - -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithGradient (const Image& alphaChannelImage, int imageX, int imageY, const ColourGradient& gradient) -{ - imageX += xOffset; - imageY += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedFillAlphaChannelWithGradient (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - alphaChannelImage, imageX, imageY, gradient); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithGradient (int x, int y, int w, int h, - const Image& alphaChannelImage, - int imageX, int imageY, const ColourGradient& gradient) -{ - if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, alphaChannelImage.getWidth(), alphaChannelImage.getHeight())) - { - ColourGradient g2 (gradient); - g2.x1 += xOffset - x; - g2.x2 += xOffset - x; - g2.y1 += yOffset - y; - g2.y2 += yOffset - y; - - Image temp (g2.isOpaque() ? Image::RGB : Image::ARGB, w, h, true); - LowLevelGraphicsSoftwareRenderer tempG (temp); - tempG.fillRectWithGradient (0, 0, w, h, g2); - - clippedFillAlphaChannelWithImage (x, y, w, h, - alphaChannelImage, imageX, imageY, - temp, x, y, 1.0f); } } void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float opacity) + const Image& fillerImage, int fillerImageX, int fillerImageY) { alphaImageX += xOffset; alphaImageY += yOffset; @@ -82064,7 +82234,8 @@ void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& a clippedFillAlphaChannelWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), alphaImage, alphaImageX, alphaImageY, - fillerImage, fillerImageX, fillerImageY, opacity); + fillerImage, fillerImageX, fillerImageY, + colour.getFloatAlpha()); } } @@ -82133,7 +82304,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithImage (int x, } } -void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) +void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { dx += xOffset; dy += yOffset; @@ -82143,12 +82314,12 @@ void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int const Rectangle& r = *i.getRectangle(); clippedBlendImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - sourceImage, dx, dy, dw, dh, sx, sy, opacity); + sourceImage, dx, dy, dw, dh, sx, sy); } } void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, int clipW, int clipH, - const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) + const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { if (dx < clipX) { @@ -82173,7 +82344,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, if (dw <= 0 || dh <= 0) return; - const uint8 alpha = (uint8) jlimit (0, 0xff, roundDoubleToInt (opacity * 256.0f)); + const uint8 alpha = (uint8) colour.getAlpha(); if (alpha == 0) return; @@ -82233,9 +82404,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t, - float opacity, - const Graphics::ResamplingQuality quality) + const AffineTransform& t) { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); @@ -82245,18 +82414,16 @@ void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceIma clippedBlendImageWarping (r.getX(), r.getY(), r.getWidth(), r.getHeight(), sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, - transform, opacity, quality); + transform); } } void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, int destClipY, int destClipW, int destClipH, const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float opacity, - const Graphics::ResamplingQuality quality) + const AffineTransform& transform) { - if (opacity > 0 && destClipW > 0 && destClipH > 0 && ! transform.isSingularity()) + if ((! colour.isTransparent()) && destClipW > 0 && destClipH > 0 && ! transform.isSingularity()) { Rectangle::intersectRectangles (srcClipX, srcClipY, srcClipW, srcClipH, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); @@ -82278,7 +82445,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, 1 + roundDoubleToInt (imW), 1 + roundDoubleToInt (imH))) { - const uint8 alpha = (uint8) jlimit (0, 0xff, roundDoubleToInt (opacity * 256.0f)); + const uint8 alpha = (uint8) colour.getAlpha(); float srcX1 = (float) destClipX; float srcY1 = (float) destClipY; @@ -82305,7 +82472,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelARGB*)0, (PixelARGB*)0); + alpha, interpolationQuality, (PixelARGB*)0, (PixelARGB*)0); } else if (sourceImage.getFormat() == Image::RGB) { @@ -82313,7 +82480,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelARGB*)0, (PixelRGB*)0); + alpha, interpolationQuality, (PixelARGB*)0, (PixelRGB*)0); } else { @@ -82328,7 +82495,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelRGB*)0, (PixelARGB*)0); + alpha, interpolationQuality, (PixelRGB*)0, (PixelARGB*)0); } else if (sourceImage.getFormat() == Image::RGB) { @@ -82336,7 +82503,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelRGB*)0, (PixelRGB*)0); + alpha, interpolationQuality, (PixelRGB*)0, (PixelRGB*)0); } else { @@ -82351,7 +82518,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, } } -void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2) { x1 += xOffset; y1 += yOffset; @@ -82363,11 +82530,11 @@ void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2 const Rectangle& r = *i.getRectangle(); clippedDrawLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x1, y1, x2, y2, colour); + x1, y1, x2, y2); } } -void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2) { if (clipW > 0 && clipH > 0) { @@ -82376,14 +82543,14 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in if (y2 < y1) swapVariables (y1, y2); - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (x1), y1, y2, colour); + clippedDrawVerticalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (x1), y1, y2); } else if (y1 == y2) { if (x2 < x1) swapVariables (x1, x2); - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (y1), x1, x2, colour); + clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (y1), x1, x2); } else { @@ -82403,7 +82570,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in while (y < endY) { const double x = x1 + gradient * (y - startY); - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, y, x, x + 1.0, colour); + clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, y, x, x + 1.0); ++y; } } @@ -82419,7 +82586,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in while (x < endX) { const double y = y1 + gradient * (x - startX); - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, x, y, y + 1.0, colour); + clippedDrawVerticalLine (clipX, clipY, clipW, clipH, x, y, y + 1.0); ++x; } } @@ -82427,19 +82594,19 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in } } -void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom, const Colour& col) +void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom) { for (RectangleList::Iterator i (*clip); i.next();) { const Rectangle& r = *i.getRectangle(); clippedDrawVerticalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x + xOffset, top + yOffset, bottom + yOffset, col); + x + xOffset, top + yOffset, bottom + yOffset); } } void LowLevelGraphicsSoftwareRenderer::clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, - const int x, double top, double bottom, const Colour& col) + const int x, double top, double bottom) { jassert (top <= bottom); @@ -82455,23 +82622,23 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawVerticalLine (int clipX, int c bottom = clipY + clipH; if (bottom > top) - drawVertical (x, top, bottom, col); + drawVertical (x, top, bottom); } } -void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right, const Colour& col) +void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right) { for (RectangleList::Iterator i (*clip); i.next();) { const Rectangle& r = *i.getRectangle(); clippedDrawHorizontalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - y + yOffset, left + xOffset, right + xOffset, col); + y + yOffset, left + xOffset, right + xOffset); } } void LowLevelGraphicsSoftwareRenderer::clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, - const int y, double left, double right, const Colour& col) + const int y, double left, double right) { jassert (left <= right); @@ -82487,14 +82654,13 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawHorizontalLine (int clipX, int right = clipX + clipW; if (right > left) - drawHorizontal (y, left, right, col); + drawHorizontal (y, left, right); } } void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, const double top, - const double bottom, - const Colour& col) + const double bottom) { int wholeStart = (int) top; const int wholeEnd = (int) bottom; @@ -82509,7 +82675,7 @@ void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, uint8* const dstPixels = image.lockPixelDataReadWrite (x, wholeStart, 1, totalPixels, lineStride, dstPixelStride); uint8* dest = dstPixels; - PixelARGB colour (col.getPixelARGB()); + PixelARGB colour (this->colour.getPixelARGB()); if (wholeEnd == wholeStart) { @@ -82595,8 +82761,7 @@ void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, const double top, - const double bottom, - const Colour& col) + const double bottom) { int wholeStart = (int) top; const int wholeEnd = (int) bottom; @@ -82611,7 +82776,7 @@ void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, uint8* const dstPixels = image.lockPixelDataReadWrite (wholeStart, y, totalPixels, 1, lineStride, dstPixelStride); uint8* dest = dstPixels; - PixelARGB colour (col.getPixelARGB()); + PixelARGB colour (this->colour.getPixelARGB()); if (wholeEnd == wholeStart) { @@ -82695,6 +82860,21 @@ void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, image.releasePixelDataReadWrite (dstPixels); } +void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) +{ + font = newFont; +} + +void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, float x, float y) +{ + font.renderGlyphIndirectly (*this, glyphNumber, x, y); +} + +void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) +{ + font.renderGlyphIndirectly (*this, glyphNumber, transform); +} + END_JUCE_NAMESPACE /********* End of inlined file: juce_LowLevelGraphicsSoftwareRenderer.cpp *********/ @@ -85416,51 +85596,26 @@ static const float minFontHeight = 0.1f; static const float maxFontHeight = 10000.0f; static const float defaultFontHeight = 14.0f; -Font::Font() throw() - : typefaceName (Typeface::defaultTypefaceNameSans), - height (defaultFontHeight), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (Font::plain) -{ -} +static const tchar* const juce_defaultFontNameSans = T(""); +static const tchar* const juce_defaultFontNameSerif = T(""); +static const tchar* const juce_defaultFontNameMono = T(""); -void Font::resetToDefaultState() throw() -{ - typefaceName = Typeface::defaultTypefaceNameSans; - height = defaultFontHeight; - horizontalScale = 1.0f; - kerning = 0; - ascent = 0; - styleFlags = Font::plain; - typeface = 0; -} +void clearUpDefaultFontNames() throw(); // in juce_LookAndFeel.cpp -Font::Font (const float fontHeight, - const int styleFlags_) throw() - : typefaceName (Typeface::defaultTypefaceNameSans), - height (jlimit (minFontHeight, maxFontHeight, fontHeight)), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (styleFlags_) -{ -} - -Font::Font (const String& typefaceName_, - const float fontHeight, - const int styleFlags_) throw() +Font::SharedFontInternal::SharedFontInternal (const String& typefaceName_, const float height_, const float horizontalScale_, + const float kerning_, const float ascent_, const int styleFlags_, + Typeface* const typeface_) throw() : typefaceName (typefaceName_), - height (jlimit (minFontHeight, maxFontHeight, fontHeight)), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (styleFlags_) + height (height_), + horizontalScale (horizontalScale_), + kerning (kerning_), + ascent (ascent_), + styleFlags (styleFlags_), + typeface (typeface_) { } -Font::Font (const Font& other) throw() +Font::SharedFontInternal::SharedFontInternal (const SharedFontInternal& other) throw() : typefaceName (other.typefaceName), height (other.height), horizontalScale (other.horizontalScale), @@ -85471,19 +85626,34 @@ Font::Font (const Font& other) throw() { } +Font::Font() throw() + : font (new SharedFontInternal (juce_defaultFontNameSans, defaultFontHeight, + 1.0f, 0, 0, Font::plain, 0)) +{ +} + +Font::Font (const float fontHeight, const int styleFlags_) throw() + : font (new SharedFontInternal (juce_defaultFontNameSans, jlimit (minFontHeight, maxFontHeight, fontHeight), + 1.0f, 0, 0, styleFlags_, 0)) +{ +} + +Font::Font (const String& typefaceName_, + const float fontHeight, + const int styleFlags_) throw() + : font (new SharedFontInternal (typefaceName_, jlimit (minFontHeight, maxFontHeight, fontHeight), + 1.0f, 0, 0, styleFlags_, 0)) +{ +} + +Font::Font (const Font& other) throw() + : font (other.font) +{ +} + const Font& Font::operator= (const Font& other) throw() { - if (this != &other) - { - typefaceName = other.typefaceName; - height = other.height; - styleFlags = other.styleFlags; - horizontalScale = other.horizontalScale; - kerning = other.kerning; - ascent = other.ascent; - typeface = other.typeface; - } - + font = other.font; return *this; } @@ -85491,26 +85661,20 @@ Font::~Font() throw() { } -Font::Font (const Typeface& face) throw() - : height (11.0f), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (plain) +Font::Font (const Typeface::Ptr& typeface) throw() + : font (new SharedFontInternal (typeface->getName(), defaultFontHeight, + 1.0f, 0, 0, Font::plain, typeface)) { - typefaceName = face.getName(); - setBold (face.isBold()); - setItalic (face.isItalic()); - typeface = new Typeface (face); } bool Font::operator== (const Font& other) const throw() { - return height == other.height - && horizontalScale == other.horizontalScale - && kerning == other.kerning - && styleFlags == other.styleFlags - && typefaceName == other.typefaceName; + return font == other.font + || (font->height == other.font->height + && font->styleFlags == other.font->styleFlags + && font->horizontalScale == other.font->horizontalScale + && font->kerning == other.font->kerning + && font->typefaceName == other.font->typefaceName); } bool Font::operator!= (const Font& other) const throw() @@ -85518,11 +85682,36 @@ bool Font::operator!= (const Font& other) const throw() return ! operator== (other); } +void Font::dupeInternalIfShared() throw() +{ + if (font->getReferenceCount() > 1) + font = new SharedFontInternal (*font); +} + +const String Font::getDefaultSansSerifFontName() throw() +{ + return juce_defaultFontNameSans; +} + +const String Font::getDefaultSerifFontName() throw() +{ + return juce_defaultFontNameSerif; +} + +const String Font::getDefaultMonospacedFontName() throw() +{ + return juce_defaultFontNameMono; +} + void Font::setTypefaceName (const String& faceName) throw() { - typefaceName = faceName; - typeface = 0; - ascent = 0; + if (faceName != font->typefaceName) + { + dupeInternalIfShared(); + font->typefaceName = faceName; + font->typeface = 0; + font->ascent = 0; + } } static String fallbackFont; @@ -85539,92 +85728,114 @@ void Font::setFallbackFontName (const String& name) throw() void Font::setHeight (float newHeight) throw() { - height = jlimit (minFontHeight, maxFontHeight, newHeight); + newHeight = jlimit (minFontHeight, maxFontHeight, newHeight); + + if (font->height != newHeight) + { + dupeInternalIfShared(); + font->height = newHeight; + } } void Font::setHeightWithoutChangingWidth (float newHeight) throw() { newHeight = jlimit (minFontHeight, maxFontHeight, newHeight); - horizontalScale *= (height / newHeight); - height = newHeight; + + if (font->height != newHeight) + { + dupeInternalIfShared(); + font->horizontalScale *= (font->height / newHeight); + font->height = newHeight; + } } void Font::setStyleFlags (const int newFlags) throw() { - if (styleFlags != newFlags) + if (font->styleFlags != newFlags) { - styleFlags = newFlags; - typeface = 0; - ascent = 0; + dupeInternalIfShared(); + font->styleFlags = newFlags; + font->typeface = 0; + font->ascent = 0; } } -void Font::setSizeAndStyle (const float newHeight, +void Font::setSizeAndStyle (float newHeight, const int newStyleFlags, const float newHorizontalScale, const float newKerningAmount) throw() { - height = jlimit (minFontHeight, maxFontHeight, newHeight); - horizontalScale = newHorizontalScale; - kerning = newKerningAmount; + newHeight = jlimit (minFontHeight, maxFontHeight, newHeight); + + if (font->height != newHeight + || font->horizontalScale != newHorizontalScale + || font->kerning != newKerningAmount) + { + dupeInternalIfShared(); + font->height = newHeight; + font->horizontalScale = newHorizontalScale; + font->kerning = newKerningAmount; + } setStyleFlags (newStyleFlags); } void Font::setHorizontalScale (const float scaleFactor) throw() { - horizontalScale = scaleFactor; + dupeInternalIfShared(); + font->horizontalScale = scaleFactor; } void Font::setExtraKerningFactor (const float extraKerning) throw() { - kerning = extraKerning; + dupeInternalIfShared(); + font->kerning = extraKerning; } void Font::setBold (const bool shouldBeBold) throw() { - setStyleFlags (shouldBeBold ? (styleFlags | bold) - : (styleFlags & ~bold)); + setStyleFlags (shouldBeBold ? (font->styleFlags | bold) + : (font->styleFlags & ~bold)); } bool Font::isBold() const throw() { - return (styleFlags & bold) != 0; + return (font->styleFlags & bold) != 0; } void Font::setItalic (const bool shouldBeItalic) throw() { - setStyleFlags (shouldBeItalic ? (styleFlags | italic) - : (styleFlags & ~italic)); + setStyleFlags (shouldBeItalic ? (font->styleFlags | italic) + : (font->styleFlags & ~italic)); } bool Font::isItalic() const throw() { - return (styleFlags & italic) != 0; + return (font->styleFlags & italic) != 0; } void Font::setUnderline (const bool shouldBeUnderlined) throw() { - setStyleFlags (shouldBeUnderlined ? (styleFlags | underlined) - : (styleFlags & ~underlined)); + setStyleFlags (shouldBeUnderlined ? (font->styleFlags | underlined) + : (font->styleFlags & ~underlined)); } bool Font::isUnderlined() const throw() { - return (styleFlags & underlined) != 0; + return (font->styleFlags & underlined) != 0; } float Font::getAscent() const throw() { - if (ascent == 0) - ascent = getTypeface()->getAscent(); + if (font->ascent == 0) + font->ascent = getTypeface()->getAscent(); - return height * ascent; + return font->height * font->ascent; } float Font::getDescent() const throw() { - return height - getAscent(); + return font->height - getAscent(); } int Font::getStringWidth (const String& text) const throw() @@ -85634,35 +85845,32 @@ int Font::getStringWidth (const String& text) const throw() float Font::getStringWidthFloat (const String& text) const throw() { - float x = 0.0f; + float w = getTypeface()->getStringWidth (text); - if (text.isNotEmpty()) - { - Typeface* const typeface = getTypeface(); - const juce_wchar* t = (const juce_wchar*) text; + if (font->kerning != 0) + w += font->kerning * text.length(); - do - { - const TypefaceGlyphInfo* const glyph = typeface->getGlyph (*t++); - - if (glyph != 0) - x += kerning + glyph->getHorizontalSpacing (*t); - } - while (*t != 0); - - x *= height; - x *= horizontalScale; - } - - return x; + return w * font->height * font->horizontalScale; } -Typeface* Font::getTypeface() const throw() +void Font::getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) const throw() { - if (typeface == 0) - typeface = Typeface::getTypefaceFor (*this); + getTypeface()->getGlyphPositions (text, glyphs, xOffsets); - return typeface; + 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 + { + for (int i = 0; i < num; ++i) + x[i] *= scale; + } } void Font::findFonts (OwnedArray& destArray) throw() @@ -85673,55 +85881,107 @@ void Font::findFonts (OwnedArray& destArray) throw() destArray.add (new Font (names[i], defaultFontHeight, Font::plain)); } -END_JUCE_NAMESPACE -/********* End of inlined file: juce_Font.cpp *********/ - -/********* Start of inlined file: juce_GlyphArrangement.cpp *********/ - -BEGIN_JUCE_NAMESPACE - -#define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth)) - -class FontGlyphAlphaMap +class TypefaceCache : public DeletedAtShutdown { public: - - bool draw (const Graphics& g, float x, const float y) const throw() + TypefaceCache (int numToCache = 10) throw() + : counter (1), + faces (2) { - if (bitmap1 == 0) - return false; + while (--numToCache >= 0) + faces.add (new CachedFace()); + } - x += xOrigin; - const float xFloor = floorf (x); - const int intX = (int) xFloor; + ~TypefaceCache() + { + clearUpDefaultFontNames(); + clearSingletonInstance(); + } - g.drawImageAt (((x - xFloor) >= 0.5f && bitmap2 != 0) ? bitmap2 : bitmap1, - intX, (int) floorf (y + yOrigin), true); + juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache) - return true; + const Typeface::Ptr findTypefaceFor (const Font& font) throw() + { + const int flags = font.getStyleFlags() & (Font::bold | Font::italic); + const String faceName (font.getTypefaceName()); + + int i; + for (i = faces.size(); --i >= 0;) + { + CachedFace* const face = faces.getUnchecked(i); + + if (face->flags == flags + && face->typefaceName == faceName) + { + face->lastUsageCount = ++counter; + return face->typeFace; + } + } + + int replaceIndex = 0; + int bestLastUsageCount = INT_MAX; + + for (i = faces.size(); --i >= 0;) + { + const int lu = faces.getUnchecked(i)->lastUsageCount; + + if (bestLastUsageCount > lu) + { + bestLastUsageCount = lu; + replaceIndex = i; + } + } + + CachedFace* const face = faces.getUnchecked (replaceIndex); + face->typefaceName = faceName; + face->flags = flags; + face->lastUsageCount = ++counter; + face->typeFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); + jassert (face->typeFace != 0); // the look and feel must return a typeface! + + return face->typeFace; } juce_UseDebuggingNewOperator private: + struct CachedFace + { + CachedFace() throw() + : lastUsageCount (0), flags (-1) + { + } - Image* bitmap1; - Image* bitmap2; - float xOrigin, yOrigin; - int lastAccessCount; - Typeface::Ptr typeface; - float height, horizontalScale; - juce_wchar character; + String typefaceName; + int lastUsageCount; + int flags; + Typeface::Ptr typeFace; + }; - friend class GlyphCache; + int counter; + OwnedArray faces; + + TypefaceCache (const TypefaceCache&); + const TypefaceCache& operator= (const TypefaceCache&); +}; + +juce_ImplementSingleton_SingleThreaded (TypefaceCache) + +Typeface* Font::getTypeface() const throw() +{ + if (font->typeface == 0) + font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this); + + return font->typeface; +} + +class FontGlyphAlphaMap +{ +public: FontGlyphAlphaMap() throw() - : bitmap1 (0), - bitmap2 (0), - lastAccessCount (0), - height (0), - horizontalScale (0), - character (0) + : glyph (0), lastAccessCount (0), + bitmap1 (0), bitmap2 (0) { } @@ -85731,20 +85991,61 @@ private: delete bitmap2; } + void draw (LowLevelGraphicsContext& g, float x, const float y) const throw() + { + if (bitmap1 != 0) + { + x += xOrigin; + const float xFloor = floorf (x); + const int intX = (int) xFloor; + + g.fillAlphaChannel (((x - xFloor) >= 0.5f && bitmap2 != 0) ? *bitmap2 : *bitmap1, + intX, (int) floorf (y + yOrigin)); + } + } + + void generate (const Font& font_, const int glyph_) throw() + { + font = font_; + glyph = glyph_; + + deleteAndZero (bitmap1); + deleteAndZero (bitmap2); + + Path glyphPath; + font.getTypeface()->getOutlineForGlyph (glyph_, glyphPath); + + if (! glyphPath.isEmpty()) + { + const float fontHeight = font.getHeight(); + const float fontHScale = fontHeight * font.getHorizontalScale(); + + bitmap1 = createAlphaMapFromPath (glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.0f); + + if (fontHScale < 24.0f) + bitmap2 = createAlphaMapFromPath (glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.5f); + } + else + { + xOrigin = yOrigin = 0; + } + } + + int glyph, lastAccessCount; + Font font; + + juce_UseDebuggingNewOperator + +private: + Image* bitmap1; + Image* bitmap2; + float xOrigin, yOrigin; + class AlphaBitmapRenderer { - uint8* const data; - const int stride; - uint8* lineStart; - - AlphaBitmapRenderer (const AlphaBitmapRenderer&); - const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&); - public: - AlphaBitmapRenderer (uint8* const data_, - const int stride_) throw() - : data (data_), - stride (stride_) + AlphaBitmapRenderer (uint8* const data_, const int stride_) throw() + : data (data_), stride (stride_) { } @@ -85765,6 +86066,14 @@ private: while (--width >= 0) *d++ = (uint8) alphaLevel; } + + private: + uint8* const data; + const int stride; + uint8* lineStart; + + AlphaBitmapRenderer (const AlphaBitmapRenderer&); + const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&); }; Image* createAlphaMapFromPath (const Path& path, @@ -85780,15 +86089,15 @@ private: topLeftX = floorf (px * xScale); topLeftY = floorf (py * yScale); - int bitmapWidth = roundFloatToInt (pw * xScale) + 2; - int bitmapHeight = roundFloatToInt (ph * yScale) + 2; + const int bitmapWidth = roundFloatToInt (pw * xScale) + 2; + const int bitmapHeight = roundFloatToInt (ph * yScale) + 2; im = new Image (Image::SingleChannel, bitmapWidth, bitmapHeight, true); EdgeTable edgeTable (0, bitmapHeight, EdgeTable::Oversampling_16times); edgeTable.addPath (path, AffineTransform::scale (xScale, yScale) - .translated (subPixelOffsetX - topLeftX, -topLeftY)); + .translated (subPixelOffsetX - topLeftX, -topLeftY)); int stride, pixelStride; uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, bitmapWidth, bitmapHeight, stride, pixelStride); @@ -85798,121 +86107,82 @@ private: edgeTable.iterate (renderer, 0, 0, bitmapWidth, bitmapHeight, 0); im->releasePixelDataReadWrite (pixels); - return im; } - void generate (Typeface* const face, - const juce_wchar character_, - const float fontHeight, - const float fontHorizontalScale) throw() - { - character = character_; - typeface = face; - height = fontHeight; - horizontalScale = fontHorizontalScale; - - const Path* const glyphPath = face->getOutlineForGlyph (character_); - - deleteAndZero (bitmap1); - deleteAndZero (bitmap2); - - const float fontHScale = fontHeight * fontHorizontalScale; - - if (glyphPath != 0 && ! glyphPath->isEmpty()) - { - bitmap1 = createAlphaMapFromPath (*glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.0f); - - if (fontHScale < 24.0f) - bitmap2 = createAlphaMapFromPath (*glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.5f); - } - else - { - xOrigin = yOrigin = 0; - } - } + FontGlyphAlphaMap (const FontGlyphAlphaMap&); + const FontGlyphAlphaMap& operator= (const FontGlyphAlphaMap&); }; -static const int defaultNumGlyphsToCache = 120; -class GlyphCache; -static GlyphCache* cacheInstance = 0; - class GlyphCache : private DeletedAtShutdown { public: - - static GlyphCache* getInstance() throw() + GlyphCache() throw() + : accessCounter (0) { - if (cacheInstance == 0) - cacheInstance = new GlyphCache(); - - return cacheInstance; + setCacheSize (120); } - const FontGlyphAlphaMap& getGlyphFor (Typeface* const typeface, - const float fontHeight, - const float fontHorizontalScale, - const juce_wchar character) throw() + ~GlyphCache() throw() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); + + void drawGlyph (LowLevelGraphicsContext& g, const Font& font, int glyphNumber, float x, float y) throw() { ++accessCounter; int oldestCounter = INT_MAX; - int oldestIndex = 0; + FontGlyphAlphaMap* oldest = 0; - for (int i = numGlyphs; --i >= 0;) + for (int i = glyphs.size(); --i >= 0;) { - FontGlyphAlphaMap& g = glyphs[i]; + FontGlyphAlphaMap* const glyph = glyphs.getUnchecked (i); - if (g.character == character - && g.height == fontHeight - && g.typeface->hashCode() == typeface->hashCode() - && g.horizontalScale == fontHorizontalScale) + if (glyph->glyph == glyphNumber + && glyph->font == font) { - g.lastAccessCount = accessCounter; ++hits; - return g; + glyph->lastAccessCount = accessCounter; + glyph->draw (g, x, y); + return; } - if (oldestCounter > g.lastAccessCount) + if (glyph->lastAccessCount <= oldestCounter) { - oldestCounter = g.lastAccessCount; - oldestIndex = i; + oldestCounter = glyph->lastAccessCount; + oldest = glyph; } } ++misses; - if (hits + misses > (numGlyphs << 4)) + if (hits + misses > (glyphs.size() << 4)) { if (misses * 2 > hits) - setCacheSize (numGlyphs + 32); + setCacheSize (glyphs.size() + 32); hits = 0; misses = 0; - oldestIndex = 0; + oldest = glyphs.getUnchecked (0); } - FontGlyphAlphaMap& oldest = glyphs [oldestIndex]; - oldest.lastAccessCount = accessCounter; - - oldest.generate (typeface, - character, - fontHeight, - fontHorizontalScale); - - return oldest; + jassert (oldest != 0); + oldest->lastAccessCount = accessCounter; + oldest->generate (font, glyphNumber); + oldest->draw (g, x, y); } - void setCacheSize (const int num) throw() + void setCacheSize (int num) throw() { - if (numGlyphs != num) + if (glyphs.size() != num) { - numGlyphs = num; + glyphs.clear(); - if (glyphs != 0) - delete[] glyphs; - - glyphs = new FontGlyphAlphaMap [numGlyphs]; + while (--num >= 0) + glyphs.add (new FontGlyphAlphaMap()); hits = 0; misses = 0; @@ -85922,77 +86192,80 @@ public: juce_UseDebuggingNewOperator private: - FontGlyphAlphaMap* glyphs; - int numGlyphs, accessCounter; - int hits, misses; - - GlyphCache() throw() - : glyphs (0), - numGlyphs (0), - accessCounter (0) - { - setCacheSize (defaultNumGlyphsToCache); - } - - ~GlyphCache() throw() - { - delete[] glyphs; - - jassert (cacheInstance == this); - cacheInstance = 0; - } + OwnedArray glyphs; + int accessCounter, hits, misses; GlyphCache (const GlyphCache&); const GlyphCache& operator= (const GlyphCache&); }; +juce_ImplementSingleton_SingleThreaded (GlyphCache); + +void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y) +{ + if (font->height < 80.0f) + GlyphCache::getInstance()->drawGlyph (g, *this, glyphNumber, x, y); + else + renderGlyphIndirectly (g, glyphNumber, AffineTransform::translation (x, y)); +} + +void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform) +{ + Path p; + getTypeface()->getOutlineForGlyph (glyphNumber, p); + + g.fillPath (p, AffineTransform::scale (font->height * font->horizontalScale, font->height) + .followedBy (transform), + EdgeTable::Oversampling_16times); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Font.cpp *********/ + +/********* Start of inlined file: juce_GlyphArrangement.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth)) + PositionedGlyph::PositionedGlyph() throw() { } void PositionedGlyph::draw (const Graphics& g) const throw() { - if (! glyphInfo->isWhitespace()) + if (! isWhitespace()) { - if (fontHeight < 100.0f && fontHeight > 0.1f && ! g.isVectorDevice()) - { - const FontGlyphAlphaMap& alphaMap - = GlyphCache::getInstance()->getGlyphFor (glyphInfo->getTypeface(), - fontHeight, - fontHorizontalScale, - getCharacter()); - - alphaMap.draw (g, x, y); - } - else - { - // that's a bit of a dodgy size, isn't it?? - jassert (fontHeight > 0.0f && fontHeight < 4000.0f); - - draw (g, AffineTransform::identity); - } + g.getInternalContext()->setFont (font); + g.getInternalContext()->drawGlyph (glyph, x, y); } } void PositionedGlyph::draw (const Graphics& g, const AffineTransform& transform) const throw() { - if (! glyphInfo->isWhitespace()) + if (! isWhitespace()) { - g.fillPath (glyphInfo->getPath(), - AffineTransform::scale (fontHeight * fontHorizontalScale, fontHeight) - .translated (x, y) - .followedBy (transform)); + g.getInternalContext()->setFont (font); + g.getInternalContext()->drawGlyph (glyph, AffineTransform::translation (x, y) + .followedBy (transform)); } } void PositionedGlyph::createPath (Path& path) const throw() { - if (! glyphInfo->isWhitespace()) + if (! isWhitespace()) { - path.addPath (glyphInfo->getPath(), - AffineTransform::scale (fontHeight * fontHorizontalScale, fontHeight) - .translated (x, y)); + Typeface* const t = font.getTypeface(); + + if (t != 0) + { + Path p; + t->getOutlineForGlyph (glyph, p); + + path.addPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()) + .translated (x, y)); + } } } @@ -86000,14 +86273,21 @@ bool PositionedGlyph::hitTest (float px, float py) const throw() { if (px >= getLeft() && px < getRight() && py >= getTop() && py < getBottom() - && fontHeight > 0.0f - && ! glyphInfo->isWhitespace()) + && ! isWhitespace()) { - AffineTransform::translation (-x, -y) - .scaled (1.0f / (fontHeight * fontHorizontalScale), 1.0f / fontHeight) - .transformPoint (px, py); + Typeface* const t = font.getTypeface(); - return glyphInfo->getPath().contains (px, py); + if (t != 0) + { + Path p; + t->getOutlineForGlyph (glyph, p); + + AffineTransform::translation (-x, -y) + .scaled (1.0f / (font.getHeight() * font.getHorizontalScale()), 1.0f / font.getHeight()) + .transformPoint (px, py); + + return p.contains (px, py); + } } return false; @@ -86021,16 +86301,11 @@ void PositionedGlyph::moveBy (const float deltaX, } GlyphArrangement::GlyphArrangement() throw() - : numGlyphs (0), - numAllocated (0), - glyphs (0) + : glyphs (128) { } GlyphArrangement::GlyphArrangement (const GlyphArrangement& other) throw() - : numGlyphs (0), - numAllocated (0), - glyphs (0) { addGlyphArrangement (other); } @@ -86048,95 +86323,31 @@ const GlyphArrangement& GlyphArrangement::operator= (const GlyphArrangement& oth GlyphArrangement::~GlyphArrangement() throw() { - clear(); - juce_free (glyphs); -} - -void GlyphArrangement::ensureNumGlyphsAllocated (const int minGlyphs) throw() -{ - if (numAllocated <= minGlyphs) - { - numAllocated = minGlyphs + 2; - - if (glyphs == 0) - glyphs = (PositionedGlyph*) juce_malloc (numAllocated * sizeof (PositionedGlyph)); - else - glyphs = (PositionedGlyph*) juce_realloc (glyphs, numAllocated * sizeof (PositionedGlyph)); - } -} - -void GlyphArrangement::incGlyphRefCount (const int i) const throw() -{ - jassert (((unsigned int) i) < (unsigned int) numGlyphs); - - if (glyphs[i].glyphInfo != 0 && glyphs[i].glyphInfo->getTypeface() != 0) - glyphs[i].glyphInfo->getTypeface()->incReferenceCount(); -} - -void GlyphArrangement::decGlyphRefCount (const int i) const throw() -{ - if (glyphs[i].glyphInfo != 0 && glyphs[i].glyphInfo->getTypeface() != 0) - glyphs[i].glyphInfo->getTypeface()->decReferenceCount(); } void GlyphArrangement::clear() throw() { - for (int i = numGlyphs; --i >= 0;) - decGlyphRefCount (i); - - numGlyphs = 0; + glyphs.clear(); } PositionedGlyph& GlyphArrangement::getGlyph (const int index) const throw() { - jassert (((unsigned int) index) < (unsigned int) numGlyphs); + jassert (((unsigned int) index) < (unsigned int) glyphs.size()); - return glyphs [index]; + return *glyphs [index]; } void GlyphArrangement::addGlyphArrangement (const GlyphArrangement& other) throw() { - ensureNumGlyphsAllocated (numGlyphs + other.numGlyphs); + glyphs.ensureStorageAllocated (glyphs.size() + other.glyphs.size()); - memcpy (glyphs + numGlyphs, other.glyphs, - other.numGlyphs * sizeof (PositionedGlyph)); - - for (int i = other.numGlyphs; --i >= 0;) - incGlyphRefCount (numGlyphs++); -} - -void GlyphArrangement::removeLast() throw() -{ - if (numGlyphs > 0) - decGlyphRefCount (--numGlyphs); + for (int i = 0; i < other.glyphs.size(); ++i) + glyphs.add (new PositionedGlyph (*other.glyphs.getUnchecked (i))); } void GlyphArrangement::removeRangeOfGlyphs (int startIndex, const int num) throw() { - jassert (startIndex >= 0); - - if (startIndex < 0) - startIndex = 0; - - if (num < 0 || startIndex + num >= numGlyphs) - { - while (numGlyphs > startIndex) - removeLast(); - } - else if (num > 0) - { - int i; - for (i = startIndex; i < startIndex + num; ++i) - decGlyphRefCount (i); - - for (i = numGlyphs - (startIndex + num); --i >= 0;) - { - glyphs [startIndex] = glyphs [startIndex + num]; - ++startIndex; - } - - numGlyphs -= num; - } + glyphs.removeRange (startIndex, num); } void GlyphArrangement::addLineOfText (const Font& font, @@ -86156,59 +86367,41 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font, const float maxWidthPixels, const bool useEllipsis) throw() { - const int textLen = text.length(); + int textLen = text.length(); if (textLen > 0) { - ensureNumGlyphsAllocated (numGlyphs + textLen + 3); // extra chars for ellipsis - - Typeface* const typeface = font.getTypeface(); - const float fontHeight = font.getHeight(); - const float ascent = font.getAscent(); - const float fontHorizontalScale = font.getHorizontalScale(); - const float heightTimesScale = fontHorizontalScale * fontHeight; - const float kerningFactor = font.getExtraKerningFactor(); - const float startX = xOffset; + Array newGlyphs; + Array xOffsets; + font.getGlyphPositions (text, newGlyphs, xOffsets); const juce_wchar* const unicodeText = (const juce_wchar*) text; + textLen = jmin (textLen, newGlyphs.size()); for (int i = 0; i < textLen; ++i) { - const TypefaceGlyphInfo* const glyph = typeface->getGlyph (unicodeText[i]); + const float thisX = xOffsets.getUnchecked (i); + const float nextX = xOffsets.getUnchecked (i + 1); - if (glyph != 0) + if (nextX > maxWidthPixels + 1.0f) { - jassert (numAllocated > numGlyphs); + // curtail the string if it's too wide.. + if (useEllipsis && textLen > 3 && glyphs.size() >= 3) + appendEllipsis (font, xOffset + maxWidthPixels); - ensureNumGlyphsAllocated (numGlyphs); - PositionedGlyph& pg = glyphs [numGlyphs]; - pg.glyphInfo = glyph; - pg.x = xOffset; - pg.y = yOffset; - pg.w = heightTimesScale * glyph->getHorizontalSpacing (0); - pg.fontHeight = fontHeight; - pg.fontAscent = ascent; - pg.fontHorizontalScale = fontHorizontalScale; - pg.isUnderlined = font.isUnderlined(); + break; + } + else + { + PositionedGlyph* const pg = new PositionedGlyph(); + pg->x = xOffset + thisX; + pg->y = yOffset; + pg->w = nextX - thisX; + pg->font = font; + pg->glyph = newGlyphs.getUnchecked(i); + pg->character = unicodeText[i]; - xOffset += heightTimesScale * (kerningFactor + glyph->getHorizontalSpacing (unicodeText [i + 1])); - - if (xOffset - startX > maxWidthPixels + 1.0f) - { - // curtail the string if it's too wide.. - - if (useEllipsis && textLen > 3 && numGlyphs >= 3) - appendEllipsis (font, startX + maxWidthPixels); - - break; - } - else - { - if (glyph->getTypeface() != 0) - glyph->getTypeface()->incReferenceCount(); - - ++numGlyphs; - } + glyphs.add (pg); } } } @@ -86216,57 +86409,48 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font, void GlyphArrangement::appendEllipsis (const Font& font, const float maxXPixels) throw() { - const TypefaceGlyphInfo* const dotGlyph = font.getTypeface()->getGlyph (T('.')); + //const TypefaceGlyphInfo* const dotGlyph = font.getTypeface()->getGlyph (T('.')); - if (dotGlyph != 0) + if (/*dotGlyph != 0 &&*/ glyphs.size() > 0) { - if (numGlyphs > 0) + //PositionedGlyph* glyph = glyphs.getLast(); + //const float fontHeight = glyph->font.getHeight(); + //const float fontHorizontalScale = glyph->font.getHorizontalScale(); + + Array dotGlyphs; + Array dotXs; + font.getGlyphPositions (T(".."), dotGlyphs, dotXs); + + const float dx = dotXs[1]; + //fontHeight * fontHorizontalScale + // * (font.getExtraKerningFactor() + dotGlyph->getHorizontalSpacing (T('.'))); + + float xOffset = 0.0f, yOffset = 0.0f; + + for (int dotPos = 3; --dotPos >= 0 && glyphs.size() > 0;) { - PositionedGlyph& glyph = glyphs [numGlyphs - 1]; - const float fontHeight = glyph.fontHeight; - const float fontHorizontalScale = glyph.fontHorizontalScale; - const float fontAscent = glyph.fontAscent; + const PositionedGlyph* pg = glyphs.getUnchecked (glyphs.size() - 1); + xOffset = pg->x; + yOffset = pg->y; - const float dx = fontHeight * fontHorizontalScale - * (font.getExtraKerningFactor() + dotGlyph->getHorizontalSpacing (T('.'))); + glyphs.removeLast(); - float xOffset = 0.0f, yOffset = 0.0f; + if (xOffset + dx * 3 <= maxXPixels) + break; + } - for (int dotPos = 3; --dotPos >= 0 && numGlyphs > 0;) - { - removeLast(); + for (int i = 3; --i >= 0;) + { + PositionedGlyph* const pg = new PositionedGlyph(); + pg->x = xOffset; + pg->y = yOffset; + pg->w = dx; + pg->font = font; + pg->character = '.'; + pg->glyph = dotGlyphs.getFirst(); + glyphs.add (pg); - jassert (numAllocated > numGlyphs); - PositionedGlyph& pg = glyphs [numGlyphs]; - xOffset = pg.x; - yOffset = pg.y; - - if (numGlyphs == 0 || xOffset + dx * 3 <= maxXPixels) - break; - } - - for (int i = 3; --i >= 0;) - { - jassert (numAllocated > numGlyphs); - - ensureNumGlyphsAllocated (numGlyphs); - PositionedGlyph& pg = glyphs [numGlyphs]; - pg.glyphInfo = dotGlyph; - pg.x = xOffset; - pg.y = yOffset; - pg.w = dx; - pg.fontHeight = fontHeight; - pg.fontAscent = fontAscent; - pg.fontHorizontalScale = fontHorizontalScale; - pg.isUnderlined = font.isUnderlined(); - - xOffset += dx; - - if (dotGlyph->getTypeface() != 0) - dotGlyph->getTypeface()->incReferenceCount(); - - ++numGlyphs; - } + xOffset += dx; } } } @@ -86277,40 +86461,42 @@ void GlyphArrangement::addJustifiedText (const Font& font, const float maxLineWidth, const Justification& horizontalLayout) throw() { - int lineStartIndex = numGlyphs; + int lineStartIndex = glyphs.size(); addLineOfText (font, text, x, y); const float originalY = y; - while (lineStartIndex < numGlyphs) + while (lineStartIndex < glyphs.size()) { int i = lineStartIndex; - if (glyphs[i].getCharacter() != T('\n') && glyphs[i].getCharacter() != T('\r')) + if (glyphs.getUnchecked(i)->getCharacter() != T('\n') + && glyphs.getUnchecked(i)->getCharacter() != T('\r')) ++i; - const float lineMaxX = glyphs [lineStartIndex].getLeft() + maxLineWidth; + const float lineMaxX = glyphs.getUnchecked (lineStartIndex)->getLeft() + maxLineWidth; int lastWordBreakIndex = -1; - while (i < numGlyphs) + while (i < glyphs.size()) { - PositionedGlyph& pg = glyphs[i]; - const juce_wchar c = pg.getCharacter(); + const PositionedGlyph* pg = glyphs.getUnchecked (i); + const juce_wchar c = pg->getCharacter(); if (c == T('\r') || c == T('\n')) { ++i; - if (c == T('\r') && i < numGlyphs && glyphs [i].getCharacter() == T('\n')) + if (c == T('\r') && i < glyphs.size() + && glyphs.getUnchecked(i)->getCharacter() == T('\n')) ++i; break; } - else if (pg.isWhitespace()) + else if (pg->isWhitespace()) { lastWordBreakIndex = i + 1; } - else if (SHOULD_WRAP (pg.getRight(), lineMaxX)) + else if (SHOULD_WRAP (pg->getRight(), lineMaxX)) { if (lastWordBreakIndex >= 0) i = lastWordBreakIndex; @@ -86321,14 +86507,14 @@ void GlyphArrangement::addJustifiedText (const Font& font, ++i; } - const float currentLineStartX = glyphs [lineStartIndex].getLeft(); + const float currentLineStartX = glyphs.getUnchecked (lineStartIndex)->getLeft(); float currentLineEndX = currentLineStartX; for (int j = i; --j >= lineStartIndex;) { - if (! glyphs[j].isWhitespace()) + if (! glyphs.getUnchecked (j)->isWhitespace()) { - currentLineEndX = glyphs[j].getRight(); + currentLineEndX = glyphs.getUnchecked (j)->getRight(); break; } } @@ -86379,17 +86565,22 @@ void GlyphArrangement::addFittedText (const Font& f, ga.moveRangeOfGlyphs (0, -1, 0.0f, dy); - addGlyphArrangement (ga); + glyphs.ensureStorageAllocated (glyphs.size() + ga.glyphs.size()); + for (int i = 0; i < ga.glyphs.size(); ++i) + glyphs.add (ga.glyphs.getUnchecked (i)); + + ga.glyphs.clear (false); return; } - int startIndex = numGlyphs; + int startIndex = glyphs.size(); addLineOfText (f, text.trim(), x, y); - if (numGlyphs > startIndex) + if (glyphs.size() > startIndex) { - float lineWidth = glyphs[numGlyphs - 1].getRight() - glyphs[startIndex].getLeft(); + float lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() + - glyphs.getUnchecked (startIndex)->getLeft(); if (lineWidth <= 0) return; @@ -86398,26 +86589,26 @@ void GlyphArrangement::addFittedText (const Font& f, { if (lineWidth > width) { - stretchRangeOfGlyphs (startIndex, numGlyphs - startIndex, + stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex, width / lineWidth); } - justifyGlyphs (startIndex, numGlyphs - startIndex, + justifyGlyphs (startIndex, glyphs.size() - startIndex, x, y, width, height, layout); } else if (maximumLines <= 1) { const float ratio = jmax (minimumHorizontalScale, width / lineWidth); - stretchRangeOfGlyphs (startIndex, numGlyphs - startIndex, ratio); + stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex, ratio); - while (numGlyphs > 0 && glyphs [numGlyphs - 1].x + glyphs [numGlyphs - 1].w >= x + width) - removeLast(); + while (glyphs.size() > 0 && glyphs.getUnchecked (glyphs.size() - 1)->getRight() >= x + width) + glyphs.removeLast(); appendEllipsis (f, x + width); - justifyGlyphs (startIndex, numGlyphs - startIndex, + justifyGlyphs (startIndex, glyphs.size() - startIndex, x, y, width, height, layout); } else @@ -86438,7 +86629,7 @@ void GlyphArrangement::addFittedText (const Font& f, { ++numLines; - const float newFontHeight = height / (float)numLines; + const float newFontHeight = height / (float) numLines; if (newFontHeight < 8.0f) break; @@ -86447,12 +86638,13 @@ void GlyphArrangement::addFittedText (const Font& f, { font.setHeight (newFontHeight); - while (numGlyphs > startIndex) - removeLast(); + while (glyphs.size() > startIndex) + glyphs.removeLast(); addLineOfText (font, txt, x, y); - lineWidth = glyphs[numGlyphs - 1].getRight() - glyphs[startIndex].getLeft(); + lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() + - glyphs.getUnchecked (startIndex)->getLeft(); } if (numLines > lineWidth / width) @@ -86470,11 +86662,11 @@ void GlyphArrangement::addFittedText (const Font& f, { int i = startIndex; lastLineStartIndex = i; - float lineStartX = glyphs[startIndex].getLeft(); + float lineStartX = glyphs.getUnchecked (startIndex)->getLeft(); - while (i < numGlyphs) + while (i < glyphs.size()) { - lineWidth = (glyphs[i].getRight() - lineStartX); + lineWidth = (glyphs.getUnchecked (i)->getRight() - lineStartX); if (lineWidth > widthPerLine) { @@ -86482,12 +86674,12 @@ void GlyphArrangement::addFittedText (const Font& f, // good place to break it.. const int searchStartIndex = i; - while (i < numGlyphs) + while (i < glyphs.size()) { - if ((glyphs[i].getRight() - lineStartX) * minimumHorizontalScale < width) + if ((glyphs.getUnchecked (i)->getRight() - lineStartX) * minimumHorizontalScale < width) { - if (glyphs[i].isWhitespace() - || glyphs[i].getCharacter() == T('-')) + if (glyphs.getUnchecked (i)->isWhitespace() + || glyphs.getUnchecked (i)->getCharacter() == T('-')) { ++i; break; @@ -86500,8 +86692,8 @@ void GlyphArrangement::addFittedText (const Font& f, for (int back = 1; back < jmin (5, i - startIndex - 1); ++back) { - if (glyphs[i - back].isWhitespace() - || glyphs[i - back].getCharacter() == T('-')) + if (glyphs.getUnchecked (i - back)->isWhitespace() + || glyphs.getUnchecked (i - back)->getCharacter() == T('-')) { i -= back - 1; break; @@ -86521,18 +86713,18 @@ void GlyphArrangement::addFittedText (const Font& f, } int wsStart = i; - while (wsStart > 0 && glyphs[wsStart - 1].isWhitespace()) + while (wsStart > 0 && glyphs.getUnchecked (wsStart - 1)->isWhitespace()) --wsStart; int wsEnd = i; - while (wsEnd < numGlyphs && glyphs[wsEnd].isWhitespace()) + while (wsEnd < glyphs.size() && glyphs.getUnchecked (wsEnd)->isWhitespace()) ++wsEnd; removeRangeOfGlyphs (wsStart, wsEnd - wsStart); i = jmax (wsStart, startIndex + 1); - lineWidth = glyphs[i - 1].getRight() - lineStartX; + lineWidth = glyphs.getUnchecked (i - 1)->getRight() - lineStartX; if (lineWidth > width) { @@ -86547,21 +86739,20 @@ void GlyphArrangement::addFittedText (const Font& f, startIndex = i; lineY += font.getHeight(); - if (startIndex >= numGlyphs) + if (startIndex >= glyphs.size()) break; } - if (startIndex < numGlyphs) + if (startIndex < glyphs.size()) { - while (numGlyphs > startIndex) - removeLast(); + glyphs.removeRange (startIndex, glyphs.size()); if (startIndex - originalStartIndex > 4) { - const float lineStartX = glyphs[lastLineStartIndex].getLeft(); + const float lineStartX = glyphs.getUnchecked (lastLineStartIndex)->getLeft(); appendEllipsis (font, lineStartX + width); - lineWidth = glyphs[startIndex - 1].getRight() - lineStartX; + lineWidth = glyphs.getUnchecked (startIndex - 1)->getRight() - lineStartX; if (lineWidth > width) { @@ -86574,7 +86765,7 @@ void GlyphArrangement::addFittedText (const Font& f, layout.getOnlyHorizontalFlags() | Justification::verticallyCentred); } - startIndex = numGlyphs; + startIndex = glyphs.size(); } justifyGlyphs (originalStartIndex, startIndex - originalStartIndex, @@ -86590,14 +86781,11 @@ void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, if (dx != 0.0f || dy != 0.0f) { - if (num < 0 || startIndex + num > numGlyphs) - num = numGlyphs - startIndex; + if (num < 0 || startIndex + num > glyphs.size()) + num = glyphs.size() - startIndex; while (--num >= 0) - { - jassert (((unsigned int) startIndex) <= (unsigned int) numGlyphs); - glyphs [startIndex++].moveBy (dx, dy); - } + glyphs.getUnchecked (startIndex++)->moveBy (dx, dy); } } @@ -86606,21 +86794,20 @@ void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num, { jassert (startIndex >= 0); - if (num < 0 || startIndex + num > numGlyphs) - num = numGlyphs - startIndex; + if (num < 0 || startIndex + num > glyphs.size()) + num = glyphs.size() - startIndex; if (num > 0) { - const float xAnchor = glyphs[startIndex].getLeft(); + const float xAnchor = glyphs.getUnchecked (startIndex)->getLeft(); while (--num >= 0) { - jassert (((unsigned int) startIndex) <= (unsigned int) numGlyphs); - PositionedGlyph& pg = glyphs[startIndex++]; + PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); - pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor; - pg.fontHorizontalScale *= horizontalScaleFactor; - pg.w *= horizontalScaleFactor; + pg->x = xAnchor + (pg->x - xAnchor) * horizontalScaleFactor; + pg->font.setHorizontalScale (pg->font.getHorizontalScale() * horizontalScaleFactor); + pg->w *= horizontalScaleFactor; } } } @@ -86634,8 +86821,8 @@ void GlyphArrangement::getBoundingBox (int startIndex, int num, { jassert (startIndex >= 0); - if (num < 0 || startIndex + num > numGlyphs) - num = numGlyphs - startIndex; + if (num < 0 || startIndex + num > glyphs.size()) + num = glyphs.size() - startIndex; left = 0.0f; top = 0.0f; @@ -86645,24 +86832,24 @@ void GlyphArrangement::getBoundingBox (int startIndex, int num, while (--num >= 0) { - const PositionedGlyph& pg = glyphs [startIndex++]; + const PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); - if (includeWhitespace || ! pg.isWhitespace()) + if (includeWhitespace || ! pg->isWhitespace()) { if (isFirst) { isFirst = false; - left = pg.getLeft(); - top = pg.getTop(); - right = pg.getRight(); - bottom = pg.getBottom(); + left = pg->getLeft(); + top = pg->getTop(); + right = pg->getRight(); + bottom = pg->getBottom(); } else { - left = jmin (left, pg.getLeft()); - top = jmin (top, pg.getTop()); - right = jmax (right, pg.getRight()); - bottom = jmax (bottom, pg.getBottom()); + left = jmin (left, pg->getLeft()); + top = jmin (top, pg->getTop()); + right = jmax (right, pg->getRight()); + bottom = jmax (bottom, pg->getBottom()); } } } @@ -86676,7 +86863,7 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, { jassert (num >= 0 && startIndex >= 0); - if (numGlyphs > 0 && num > 0) + if (glyphs.size() > 0 && num > 0) { float left, top, right, bottom; getBoundingBox (startIndex, num, left, top, right, bottom, @@ -86708,12 +86895,12 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, if (justification.testFlags (Justification::horizontallyJustified)) { int lineStart = 0; - float baseY = glyphs [startIndex].getBaselineY(); + float baseY = glyphs.getUnchecked (startIndex)->getBaselineY(); int i; for (i = 0; i < num; ++i) { - const float glyphY = glyphs [startIndex + i].getBaselineY(); + const float glyphY = glyphs.getUnchecked (startIndex + i)->getBaselineY(); if (glyphY != baseY) { @@ -86732,16 +86919,16 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, void GlyphArrangement::spreadOutLine (const int start, const int num, const float targetWidth) throw() { - if (start + num < numGlyphs - && glyphs [start + num - 1].getCharacter() != T('\r') - && glyphs [start + num - 1].getCharacter() != T('\n')) + if (start + num < glyphs.size() + && glyphs.getUnchecked (start + num - 1)->getCharacter() != T('\r') + && glyphs.getUnchecked (start + num - 1)->getCharacter() != T('\n')) { int numSpaces = 0; int spacesAtEnd = 0; for (int i = 0; i < num; ++i) { - if (glyphs [start + i].isWhitespace()) + if (glyphs.getUnchecked (start + i)->isWhitespace()) { ++spacesAtEnd; ++numSpaces; @@ -86756,8 +86943,8 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa if (numSpaces > 0) { - const float startX = glyphs [start].getLeft(); - const float endX = glyphs [start + num - 1 - spacesAtEnd].getRight(); + const float startX = glyphs.getUnchecked (start)->getLeft(); + const float endX = glyphs.getUnchecked (start + num - 1 - spacesAtEnd)->getRight(); const float extraPaddingBetweenWords = (targetWidth - (endX - startX)) / (float) numSpaces; @@ -86766,9 +86953,9 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa for (int i = 0; i < num; ++i) { - glyphs [start + i].moveBy (deltaX, 0.0); + glyphs.getUnchecked (start + i)->moveBy (deltaX, 0.0); - if (glyphs [start + i].isWhitespace()) + if (glyphs.getUnchecked (start + i)->isWhitespace()) deltaX += extraPaddingBetweenWords; } } @@ -86777,74 +86964,64 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa void GlyphArrangement::draw (const Graphics& g) const throw() { - for (int i = 0; i < numGlyphs; ++i) + for (int i = 0; i < glyphs.size(); ++i) { - glyphs[i].draw (g); + const PositionedGlyph* const pg = glyphs.getUnchecked(i); - if (glyphs[i].isUnderlined) + if (pg->font.isUnderlined()) { - const float lineThickness = (glyphs[i].fontHeight - glyphs[i].fontAscent) * 0.3f; + const float lineThickness = (pg->font.getDescent()) * 0.3f; - juce_wchar nextChar = 0; + float nextX = pg->x + pg->w; - if (i < numGlyphs - 1 - && glyphs[i + 1].y == glyphs[i].y) - { - nextChar = glyphs[i + 1].glyphInfo->getCharacter(); - } + if (i < glyphs.size() - 1 && glyphs.getUnchecked (i + 1)->y == pg->y) + nextX = glyphs.getUnchecked (i + 1)->x; - g.fillRect (glyphs[i].x, - glyphs[i].y + lineThickness * 2.0f, - glyphs[i].fontHeight - * glyphs[i].fontHorizontalScale - * glyphs[i].glyphInfo->getHorizontalSpacing (nextChar), - lineThickness); + g.fillRect (pg->x, pg->y + lineThickness * 2.0f, + nextX - pg->x, lineThickness); } + + pg->draw (g); } } void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const throw() { - for (int i = 0; i < numGlyphs; ++i) + for (int i = 0; i < glyphs.size(); ++i) { - glyphs[i].draw (g, transform); + const PositionedGlyph* const pg = glyphs.getUnchecked(i); - if (glyphs[i].isUnderlined) + if (pg->font.isUnderlined()) { - const float lineThickness = (glyphs[i].fontHeight - glyphs[i].fontAscent) * 0.3f; + const float lineThickness = (pg->font.getDescent()) * 0.3f; - juce_wchar nextChar = 0; + float nextX = pg->x + pg->w; - if (i < numGlyphs - 1 - && glyphs[i + 1].y == glyphs[i].y) - { - nextChar = glyphs[i + 1].glyphInfo->getCharacter(); - } + if (i < glyphs.size() - 1 && glyphs.getUnchecked (i + 1)->y == pg->y) + nextX = glyphs.getUnchecked (i + 1)->x; Path p; - p.addLineSegment (glyphs[i].x, - glyphs[i].y + lineThickness * 2.5f, - glyphs[i].x + glyphs[i].fontHeight - * glyphs[i].fontHorizontalScale - * glyphs[i].glyphInfo->getHorizontalSpacing (nextChar), - glyphs[i].y + lineThickness * 2.5f, + p.addLineSegment (pg->x, pg->y + lineThickness * 2.0f, + nextX, pg->y + lineThickness * 2.0f, lineThickness); g.fillPath (p, transform); } + + pg->draw (g, transform); } } void GlyphArrangement::createPath (Path& path) const throw() { - for (int i = 0; i < numGlyphs; ++i) - glyphs[i].createPath (path); + for (int i = 0; i < glyphs.size(); ++i) + glyphs.getUnchecked (i)->createPath (path); } int GlyphArrangement::findGlyphIndexAt (float x, float y) const throw() { - for (int i = 0; i < numGlyphs; ++i) - if (glyphs[i].hitTest (x, y)) + for (int i = 0; i < glyphs.size(); ++i) + if (glyphs.getUnchecked (i)->hitTest (x, y)) return i; return -1; @@ -87214,162 +87391,83 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE -TypefaceGlyphInfo::TypefaceGlyphInfo (const juce_wchar character_, - const Path& shape, - const float horizontalSeparation, - Typeface* const typeface_) throw() - : character (character_), - path (shape), - width (horizontalSeparation), - typeface (typeface_) +Typeface::Typeface (const String& name_) throw() + : name (name_) { } -TypefaceGlyphInfo::~TypefaceGlyphInfo() throw() -{ -} - -float TypefaceGlyphInfo::getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw() -{ - if (subsequentCharacter != 0) - { - const KerningPair* const pairs = (const KerningPair*) kerningPairs.getData(); - const int numPairs = getNumKerningPairs(); - - for (int i = 0; i < numPairs; ++i) - if (pairs [i].character2 == subsequentCharacter) - return width + pairs [i].kerningAmount; - } - - return width; -} - -void TypefaceGlyphInfo::addKerningPair (const juce_wchar subsequentCharacter, - const float extraKerningAmount) throw() -{ - const int numPairs = getNumKerningPairs(); - kerningPairs.setSize ((numPairs + 1) * sizeof (KerningPair)); - - KerningPair& p = getKerningPair (numPairs); - p.character2 = subsequentCharacter; - p.kerningAmount = extraKerningAmount; -} - -TypefaceGlyphInfo::KerningPair& TypefaceGlyphInfo::getKerningPair (const int index) const throw() -{ - return ((KerningPair*) kerningPairs.getData()) [index]; -} - -int TypefaceGlyphInfo::getNumKerningPairs() const throw() -{ - return kerningPairs.getSize() / sizeof (KerningPair); -} - -const tchar* Typeface::defaultTypefaceNameSans = T(""); -const tchar* Typeface::defaultTypefaceNameSerif = T(""); -const tchar* Typeface::defaultTypefaceNameMono = T(""); - -Typeface::Typeface() throw() - : hash (0), - isFullyPopulated (false) -{ - zeromem (lookupTable, sizeof (lookupTable)); -} - -Typeface::Typeface (const Typeface& other) - : typefaceName (other.typefaceName), - ascent (other.ascent), - bold (other.bold), - italic (other.italic), - isFullyPopulated (other.isFullyPopulated), - defaultCharacter (other.defaultCharacter) -{ - zeromem (lookupTable, sizeof (lookupTable)); - - for (int i = 0; i < other.glyphs.size(); ++i) - addGlyphCopy ((const TypefaceGlyphInfo*) other.glyphs.getUnchecked(i)); - - updateHashCode(); -} - -Typeface::Typeface (const String& faceName, - const bool bold, - const bool italic) - : isFullyPopulated (false) -{ - zeromem (lookupTable, sizeof (lookupTable)); - - initialiseTypefaceCharacteristics (faceName, bold, italic, false); - - updateHashCode(); -} - Typeface::~Typeface() +{ +} + +class CustomTypefaceGlyphInfo +{ +public: + CustomTypefaceGlyphInfo (const juce_wchar character_, const Path& path_, const float width_) throw() + : character (character_), path (path_), width (width_) + { + } + + ~CustomTypefaceGlyphInfo() throw() + { + } + + struct KerningPair + { + juce_wchar character2; + float kerningAmount; + }; + + void addKerningPair (const juce_wchar subsequentCharacter, + const float extraKerningAmount) throw() + { + KerningPair kp; + kp.character2 = subsequentCharacter; + kp.kerningAmount = extraKerningAmount; + kerningPairs.add (kp); + } + + float getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw() + { + if (subsequentCharacter != 0) + { + for (int i = kerningPairs.size(); --i >= 0;) + if (kerningPairs.getReference(i).character2 == subsequentCharacter) + return width + kerningPairs.getReference(i).kerningAmount; + } + + return width; + } + + const juce_wchar character; + const Path path; + float width; + Array kerningPairs; + + juce_UseDebuggingNewOperator + +private: + CustomTypefaceGlyphInfo (const CustomTypefaceGlyphInfo&); + const CustomTypefaceGlyphInfo& operator= (const CustomTypefaceGlyphInfo&); +}; + +CustomTypeface::CustomTypeface() + : Typeface (String::empty) { clear(); } -const Typeface& Typeface::operator= (const Typeface& other) throw() +CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) + : Typeface (String::empty) { - if (this != &other) - { - clear(); - - typefaceName = other.typefaceName; - ascent = other.ascent; - bold = other.bold; - italic = other.italic; - isFullyPopulated = other.isFullyPopulated; - defaultCharacter = other.defaultCharacter; - - for (int i = 0; i < other.glyphs.size(); ++i) - addGlyphCopy ((const TypefaceGlyphInfo*) other.glyphs.getUnchecked(i)); - - updateHashCode(); - } - - return *this; -} - -void Typeface::updateHashCode() throw() -{ - hash = typefaceName.hashCode(); - - if (bold) - hash ^= 0xffff; - - if (italic) - hash ^= 0xffff0000; -} - -void Typeface::clear() throw() -{ - zeromem (lookupTable, sizeof (lookupTable)); - typefaceName = String::empty; - bold = false; - italic = false; - - for (int i = glyphs.size(); --i >= 0;) - { - TypefaceGlyphInfo* const g = (TypefaceGlyphInfo*) (glyphs.getUnchecked(i)); - delete g; - } - - glyphs.clear(); - updateHashCode(); -} - -Typeface::Typeface (InputStream& serialisedTypefaceStream) -{ - zeromem (lookupTable, sizeof (lookupTable)); - isFullyPopulated = true; + clear(); GZIPDecompressorInputStream gzin (&serialisedTypefaceStream, false); BufferedInputStream in (&gzin, 32768, false); - typefaceName = in.readString(); - bold = in.readBool(); - italic = in.readBool(); + name = in.readString(); + isBold = in.readBool(); + isItalic = in.readBool(); ascent = in.readFloat(); defaultCharacter = (juce_wchar) in.readShort(); @@ -87394,17 +87492,145 @@ Typeface::Typeface (InputStream& serialisedTypefaceStream) addKerningPair (char1, char2, in.readFloat()); } - - updateHashCode(); } -void Typeface::serialise (OutputStream& outputStream) +CustomTypeface::~CustomTypeface() +{ +} + +void CustomTypeface::clear() +{ + defaultCharacter = 0; + ascent = 1.0f; + isBold = isItalic = false; + zeromem (lookupTable, sizeof (lookupTable)); + glyphs.clear(); +} + +void CustomTypeface::setCharacteristics (const String& name_, const float ascent_, const bool isBold_, + const bool isItalic_, const juce_wchar defaultCharacter_) throw() +{ + name = name_; + defaultCharacter = defaultCharacter_; + ascent = ascent_; + isBold = isBold_; + isItalic = isItalic_; +} + +void CustomTypeface::addGlyph (const juce_wchar character, const Path& path, const float width) throw() +{ + // Check that you're not trying to add the same character twice.. + jassert (findGlyph (character, false) == 0); + + if (((unsigned int) character) < (unsigned int) numElementsInArray (lookupTable)) + lookupTable [character] = (short) glyphs.size(); + + glyphs.add (new CustomTypefaceGlyphInfo (character, path, width)); +} + +void CustomTypeface::addKerningPair (const juce_wchar char1, const juce_wchar char2, const float extraAmount) throw() +{ + if (extraAmount != 0) + { + CustomTypefaceGlyphInfo* const g = findGlyph (char1, true); + jassert (g != 0); // can only add kerning pairs for characters that exist! + + if (g != 0) + g->addKerningPair (char2, extraAmount); + } +} + +CustomTypefaceGlyphInfo* CustomTypeface::findGlyph (const juce_wchar character, const bool loadIfNeeded) throw() +{ + if (((unsigned int) character) < (unsigned int) numElementsInArray (lookupTable) && lookupTable [character] > 0) + return glyphs [(int) lookupTable [(int) character]]; + + for (int i = 0; i < glyphs.size(); ++i) + { + CustomTypefaceGlyphInfo* const g = glyphs.getUnchecked(i); + if (g->character == character) + return g; + } + + if (loadIfNeeded && loadGlyphIfPossible (character)) + return findGlyph (character, false); + + return 0; +} + +CustomTypefaceGlyphInfo* CustomTypeface::findGlyphSubstituting (const juce_wchar character) throw() +{ + CustomTypefaceGlyphInfo* glyph = findGlyph (character, true); + + if (glyph == 0) + { + if (CharacterFunctions::isWhitespace (character) && character != L' ') + glyph = findGlyph (L' ', true); + + if (glyph == 0) + { + const Font fallbackFont (Font::getFallbackFontName(), 10, 0); + Typeface* const fallbackTypeface = fallbackFont.getTypeface(); + if (fallbackTypeface != 0 && fallbackTypeface != this) + { + //xxx + } + + if (glyph == 0) + glyph = findGlyph (defaultCharacter, true); + } + } + + return glyph; +} + +bool CustomTypeface::loadGlyphIfPossible (const juce_wchar characterNeeded) +{ + return false; +} + +void CustomTypeface::addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) throw() +{ + for (int i = 0; i < numCharacters; ++i) + { + const juce_wchar c = characterStartIndex + i; + + Array glyphIndexes; + Array offsets; + typefaceToCopy.getGlyphPositions (String::charToString (c), glyphIndexes, offsets); + + const int glyphIndex = glyphIndexes.getFirst(); + + if (glyphIndex >= 0 && glyphIndexes.size() > 0) + { + const float glyphWidth = offsets[1]; + + Path p; + typefaceToCopy.getOutlineForGlyph (glyphIndex, p); + + addGlyph (c, p, glyphWidth); + + for (int j = glyphs.size() - 1; --j >= 0;) + { + const juce_wchar char2 = glyphs.getUnchecked (j)->character; + glyphIndexes.clearQuick(); + offsets.clearQuick(); + typefaceToCopy.getGlyphPositions (String::charToString (c) + String::charToString (char2), glyphIndexes, offsets); + + if (offsets.size() > 1) + addKerningPair (c, char2, offsets[1] - glyphWidth); + } + } + } +} + +bool CustomTypeface::writeToStream (OutputStream& outputStream) { GZIPCompressorOutputStream out (&outputStream); - out.writeString (typefaceName); - out.writeBool (bold); - out.writeBool (italic); + out.writeString (name); + out.writeBool (isBold); + out.writeBool (isItalic); out.writeFloat (ascent); out.writeShort ((short) (unsigned short) defaultCharacter); out.writeInt (glyphs.size()); @@ -87413,275 +87639,88 @@ void Typeface::serialise (OutputStream& outputStream) for (i = 0; i < glyphs.size(); ++i) { - const TypefaceGlyphInfo& g = *(const TypefaceGlyphInfo*)(glyphs.getUnchecked (i)); - out.writeShort ((short) (unsigned short) g.character); - out.writeFloat (g.width); - g.path.writePathToStream (out); + const CustomTypefaceGlyphInfo* const g = glyphs.getUnchecked (i); + out.writeShort ((short) (unsigned short) g->character); + out.writeFloat (g->width); + g->path.writePathToStream (out); - numKerningPairs += g.getNumKerningPairs(); + numKerningPairs += g->kerningPairs.size(); } out.writeInt (numKerningPairs); for (i = 0; i < glyphs.size(); ++i) { - const TypefaceGlyphInfo& g = *(const TypefaceGlyphInfo*)(glyphs.getUnchecked (i)); + const CustomTypefaceGlyphInfo* const g = glyphs.getUnchecked (i); - for (int j = 0; j < g.getNumKerningPairs(); ++j) + for (int j = 0; j < g->kerningPairs.size(); ++j) { - const TypefaceGlyphInfo::KerningPair& p = g.getKerningPair (j); - out.writeShort ((short) (unsigned short) g.character); + const CustomTypefaceGlyphInfo::KerningPair& p = g->kerningPairs.getReference (j); + out.writeShort ((short) (unsigned short) g->character); out.writeShort ((short) (unsigned short) p.character2); out.writeFloat (p.kerningAmount); } } + + return true; } -const Path* Typeface::getOutlineForGlyph (const juce_wchar character) throw() +float CustomTypeface::getAscent() const { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) getGlyph (character); - - if (g != 0) - return &(g->path); - else - return 0; + return ascent; } -const TypefaceGlyphInfo* Typeface::getGlyph (const juce_wchar character) throw() +float CustomTypeface::getDescent() const { - if (((unsigned int) character) < 128 && lookupTable [character] > 0) - return (const TypefaceGlyphInfo*) glyphs [(int) lookupTable [character]]; + return 1.0f - ascent; +} - for (int i = 0; i < glyphs.size(); ++i) +float CustomTypeface::getStringWidth (const String& text) +{ + float x = 0; + const juce_wchar* t = (const juce_wchar*) text; + + while (*t != 0) { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); + const CustomTypefaceGlyphInfo* const glyph = findGlyphSubstituting (*t++); - if (g->character == character) - return g; + if (glyph != 0) + x += glyph->getHorizontalSpacing (*t); } - if ((! isFullyPopulated) - && findAndAddSystemGlyph (character)) - { - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); + return x; +} - if (g->character == character) - return g; +void CustomTypeface::getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets) +{ + xOffsets.add (0); + float x = 0; + const juce_wchar* t = (const juce_wchar*) text; + + while (*t != 0) + { + const juce_wchar c = *t++; + const CustomTypefaceGlyphInfo* const glyph = findGlyphSubstituting (c); + + if (glyph != 0) + { + x += glyph->getHorizontalSpacing (*t); + glyphs.add ((int) glyph->character); + xOffsets.add (x); } } +} - if (CharacterFunctions::isWhitespace (character) && character != L' ') +bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) +{ + const CustomTypefaceGlyphInfo* const glyph = findGlyphSubstituting ((juce_wchar) glyphNumber); + if (glyph != 0) { - const TypefaceGlyphInfo* spaceGlyph = getGlyph (L' '); - - if (spaceGlyph != 0) - { - // Add a copy of the empty glyph, mapped onto this character - addGlyph (character, spaceGlyph->getPath(), spaceGlyph->getHorizontalSpacing (0)); - spaceGlyph = (const TypefaceGlyphInfo*) glyphs [(int) lookupTable [character]]; - } - - return spaceGlyph; - } - else if (character != defaultCharacter) - { - const Font fallbackFont (Font::getFallbackFontName(), 10, 0); - Typeface* const fallbackTypeface = fallbackFont.getTypeface(); - - if (fallbackTypeface != 0 && fallbackTypeface != this) - return fallbackTypeface->getGlyph (character); - - return getGlyph (defaultCharacter); + path = glyph->path; + return true; } - return 0; -} - -void Typeface::addGlyph (const juce_wchar character, - const Path& path, - const float horizontalSpacing) throw() -{ -#ifdef JUCE_DEBUG - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - if (g->character == character) - jassertfalse; - } -#endif - - if (((unsigned int) character) < 128) - lookupTable [character] = (short) glyphs.size(); - - glyphs.add (new TypefaceGlyphInfo (character, - path, - horizontalSpacing, - this)); -} - -void Typeface::addGlyphCopy (const TypefaceGlyphInfo* const glyphInfoToCopy) throw() -{ - if (glyphInfoToCopy != 0) - { - if (glyphInfoToCopy->character > 0 && glyphInfoToCopy->character < 128) - lookupTable [glyphInfoToCopy->character] = (short) glyphs.size(); - - TypefaceGlyphInfo* const newOne - = new TypefaceGlyphInfo (glyphInfoToCopy->character, - glyphInfoToCopy->path, - glyphInfoToCopy->width, - this); - - newOne->kerningPairs = glyphInfoToCopy->kerningPairs; - glyphs.add (newOne); - } -} - -void Typeface::addKerningPair (const juce_wchar char1, - const juce_wchar char2, - const float extraAmount) throw() -{ - TypefaceGlyphInfo* const g = (TypefaceGlyphInfo*) getGlyph (char1); - - if (g != 0) - g->addKerningPair (char2, extraAmount); -} - -void Typeface::setName (const String& name) throw() -{ - typefaceName = name; - updateHashCode(); -} - -void Typeface::setAscent (const float newAscent) throw() -{ - ascent = newAscent; -} - -void Typeface::setDefaultCharacter (const juce_wchar newDefaultCharacter) throw() -{ - defaultCharacter = newDefaultCharacter; -} - -void Typeface::setBold (const bool shouldBeBold) throw() -{ - bold = shouldBeBold; - updateHashCode(); -} - -void Typeface::setItalic (const bool shouldBeItalic) throw() -{ - italic = shouldBeItalic; - updateHashCode(); -} - -class TypefaceCache; -static TypefaceCache* typefaceCacheInstance = 0; - -void clearUpDefaultFontNames() throw(); // in juce_LookAndFeel.cpp - -class TypefaceCache : private DeletedAtShutdown -{ -private: - - struct CachedFace - { - CachedFace() throw() - : lastUsageCount (0), - flags (0) - { - } - - String typefaceName; - int lastUsageCount; - int flags; - Typeface::Ptr typeFace; - }; - - int counter; - OwnedArray faces; - - TypefaceCache (const TypefaceCache&); - const TypefaceCache& operator= (const TypefaceCache&); - -public: - - TypefaceCache (int numToCache = 10) - : counter (1), - faces (2) - { - while (--numToCache >= 0) - { - CachedFace* const face = new CachedFace(); - face->typeFace = new Typeface(); - faces.add (face); - } - } - - ~TypefaceCache() - { - faces.clear(); - jassert (typefaceCacheInstance == this); - typefaceCacheInstance = 0; - clearUpDefaultFontNames(); - } - - static TypefaceCache* getInstance() throw() - { - if (typefaceCacheInstance == 0) - typefaceCacheInstance = new TypefaceCache(); - - return typefaceCacheInstance; - } - - const Typeface::Ptr findTypefaceFor (const Font& font) throw() - { - const int flags = font.getStyleFlags() & (Font::bold | Font::italic); - - int i; - for (i = faces.size(); --i >= 0;) - { - CachedFace* const face = faces.getUnchecked(i); - - if (face->flags == flags - && face->typefaceName == font.getTypefaceName()) - { - face->lastUsageCount = ++counter; - return face->typeFace; - } - } - - int replaceIndex = 0; - int bestLastUsageCount = INT_MAX; - - for (i = faces.size(); --i >= 0;) - { - const int lu = faces.getUnchecked(i)->lastUsageCount; - - if (bestLastUsageCount > lu) - { - bestLastUsageCount = lu; - replaceIndex = i; - } - } - - CachedFace* const face = faces.getUnchecked (replaceIndex); - - face->typefaceName = font.getTypefaceName(); - face->flags = flags; - face->lastUsageCount = ++counter; - face->typeFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); - - return face->typeFace; - } -}; - -const Typeface::Ptr Typeface::getTypefaceFor (const Font& font) throw() -{ - return TypefaceCache::getInstance()->findTypefaceFor (font); + return false; } END_JUCE_NAMESPACE @@ -240050,9 +240089,7 @@ const StringArray Font::findAllTypefaceNames() throw() extern bool juce_IsRunningInWine() throw(); -void Typeface::getDefaultFontNames (String& defaultSans, - String& defaultSerif, - String& defaultFixed) throw() +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() { if (juce_IsRunningInWine()) { @@ -240071,26 +240108,11 @@ void Typeface::getDefaultFontNames (String& defaultSans, class FontDCHolder : private DeletedAtShutdown { - HDC dc; - String fontName; - KERNINGPAIR* kps; - int numKPs; - bool bold, italic; - int size; - - FontDCHolder (const FontDCHolder&); - const FontDCHolder& operator= (const FontDCHolder&); - public: - HFONT fontH; FontDCHolder() throw() - : dc (0), - kps (0), - numKPs (0), - bold (false), - italic (false), - size (0) + : dc (0), kps (0), numKPs (0), size (0), + bold (false), italic (false) { } @@ -240108,10 +240130,7 @@ public: juce_DeclareSingleton_SingleThreaded_Minimal (FontDCHolder); - HDC loadFont (const String& fontName_, - const bool bold_, - const bool italic_, - const int size_) throw() + HDC loadFont (const String& fontName_, const bool bold_, const bool italic_, const int size_) throw() { if (fontName != fontName_ || bold != bold_ || italic != italic_ || size != size_) { @@ -240124,7 +240143,6 @@ public: { DeleteDC (dc); DeleteObject (fontH); - juce_free (kps); kps = 0; } @@ -240195,239 +240213,166 @@ public: numKPs_ = numKPs; return kps; } + +private: + + HFONT fontH; + HDC dc; + String fontName; + KERNINGPAIR* kps; + int numKPs, size; + bool bold, italic; + + FontDCHolder (const FontDCHolder&); + const FontDCHolder& operator= (const FontDCHolder&); }; juce_ImplementSingleton_SingleThreaded (FontDCHolder); -static bool addGlyphToTypeface (HDC dc, - juce_wchar character, - Typeface& dest, - bool addKerning) +class WindowsTypeface : public CustomTypeface { - Path destShape; - GLYPHMETRICS gm; - - float height; - BOOL ok = false; - +public: + WindowsTypeface (const Font& font) { - const WCHAR charToTest[] = { (WCHAR) character, 0 }; - WORD index = 0; + HDC dc = FontDCHolder::getInstance()->loadFont (font.getTypefaceName(), + font.isBold(), font.isItalic(), 0); - if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR - && index == 0xffff) - { - return false; - } + TEXTMETRIC tm; + tm.tmAscent = tm.tmHeight = 1; + tm.tmDefaultChar = 0; + GetTextMetrics (dc, &tm); + + setCharacteristics (font.getTypefaceName(), + tm.tmAscent / (float) tm.tmHeight, + font.isBold(), font.isItalic(), + tm.tmDefaultChar); } - TEXTMETRIC tm; - ok = GetTextMetrics (dc, &tm); - - height = (float) tm.tmHeight; - - if (! ok) + bool loadGlyphIfPossible (const juce_wchar character) { - dest.addGlyph (character, destShape, 0); - return true; - } + HDC dc = FontDCHolder::getInstance()->loadFont (name, isBold, isItalic, 0); - const float scaleX = 1.0f / height; - const float scaleY = -1.0f / height; - static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + GLYPHMETRICS gm; - const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE, - &gm, 0, 0, &identityMatrix); - - if (bufSize > 0) - { - char* const data = (char*) juce_malloc (bufSize); - - GetGlyphOutline (dc, character, GGO_NATIVE, &gm, - bufSize, data, &identityMatrix); - - const TTPOLYGONHEADER* pheader = (TTPOLYGONHEADER*) data; - - while ((char*) pheader < data + bufSize) { - #define remapX(v) (scaleX * (v).x.value) - #define remapY(v) (scaleY * (v).y.value) + const WCHAR charToTest[] = { (WCHAR) character, 0 }; + WORD index = 0; - float x = remapX (pheader->pfxStart); - float y = remapY (pheader->pfxStart); - - destShape.startNewSubPath (x, y); - - const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); - const char* const curveEnd = ((const char*) pheader) + pheader->cb; - - while ((const char*) curve < curveEnd) + if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR + && index == 0xffff) { - if (curve->wType == TT_PRIM_LINE) + return false; + } + } + + Path glyphPath; + + TEXTMETRIC tm; + if (! GetTextMetrics (dc, &tm)) + { + addGlyph (character, glyphPath, 0); + return true; + } + + const float height = (float) tm.tmHeight; + static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + + const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE, + &gm, 0, 0, &identityMatrix); + + if (bufSize > 0) + { + char* const data = (char*) juce_malloc (bufSize); + + GetGlyphOutline (dc, character, GGO_NATIVE, &gm, + bufSize, data, &identityMatrix); + + const TTPOLYGONHEADER* pheader = (TTPOLYGONHEADER*) data; + + const float scaleX = 1.0f / height; + const float scaleY = -1.0f / height; + + while ((char*) pheader < data + bufSize) + { + #define remapX(v) (scaleX * (v).x.value) + #define remapY(v) (scaleY * (v).y.value) + + float x = remapX (pheader->pfxStart); + float y = remapY (pheader->pfxStart); + + glyphPath.startNewSubPath (x, y); + + const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); + const char* const curveEnd = ((const char*) pheader) + pheader->cb; + + while ((const char*) curve < curveEnd) { - for (int i = 0; i < curve->cpfx; ++i) + if (curve->wType == TT_PRIM_LINE) { - x = remapX (curve->apfx [i]); - y = remapY (curve->apfx [i]); + for (int i = 0; i < curve->cpfx; ++i) + { + x = remapX (curve->apfx [i]); + y = remapY (curve->apfx [i]); - destShape.lineTo (x, y); + glyphPath.lineTo (x, y); + } } - } - else if (curve->wType == TT_PRIM_QSPLINE) - { - for (int i = 0; i < curve->cpfx - 1; ++i) + else if (curve->wType == TT_PRIM_QSPLINE) { - const float x2 = remapX (curve->apfx [i]); - const float y2 = remapY (curve->apfx [i]); - float x3, y3; - - if (i < curve->cpfx - 2) + for (int i = 0; i < curve->cpfx - 1; ++i) { - x3 = 0.5f * (x2 + remapX (curve->apfx [i + 1])); - y3 = 0.5f * (y2 + remapY (curve->apfx [i + 1])); - } - else - { - x3 = remapX (curve->apfx [i + 1]); - y3 = remapY (curve->apfx [i + 1]); - } + const float x2 = remapX (curve->apfx [i]); + const float y2 = remapY (curve->apfx [i]); + float x3, y3; - destShape.quadraticTo (x2, y2, x3, y3); + if (i < curve->cpfx - 2) + { + x3 = 0.5f * (x2 + remapX (curve->apfx [i + 1])); + y3 = 0.5f * (y2 + remapY (curve->apfx [i + 1])); + } + else + { + x3 = remapX (curve->apfx [i + 1]); + y3 = remapY (curve->apfx [i + 1]); + } - x = x3; - y = y3; + glyphPath.quadraticTo (x2, y2, x3, y3); + + x = x3; + y = y3; + } } + + curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); } - curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); + pheader = (const TTPOLYGONHEADER*) curve; + + glyphPath.closeSubPath(); } - pheader = (const TTPOLYGONHEADER*) curve; - - destShape.closeSubPath(); + juce_free (data); } - juce_free (data); - } + addGlyph (character, glyphPath, gm.gmCellIncX / height); - dest.addGlyph (character, destShape, gm.gmCellIncX / height); - - if (addKerning) - { int numKPs; const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs); for (int i = 0; i < numKPs; ++i) { if (kps[i].wFirst == character) - { - dest.addKerningPair (kps[i].wFirst, - kps[i].wSecond, - kps[i].iKernAmount / height); - } - } - } - - return true; -} - -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() -{ - HDC dc = FontDCHolder::getInstance()->loadFont (getName(), isBold(), isItalic(), 0); - return addGlyphToTypeface (dc, character, *this, true); -} - -/*Image* Typeface::renderGlyphToImage (juce_wchar character, float& topLeftX, float& topLeftY) -{ - HDC dc = FontDCHolder::getInstance()->loadFont (getName(), isBold(), isItalic(), hintingSize); - - int bufSize; - GLYPHMETRICS gm; - - const UINT format = GGO_GRAY2_BITMAP; - const int shift = 6; - - if (wGetGlyphOutlineW != 0) - bufSize = wGetGlyphOutlineW (dc, character, format, &gm, 0, 0, &identityMatrix); - else - bufSize = GetGlyphOutline (dc, character, format, &gm, 0, 0, &identityMatrix); - - Image* im = new Image (Image::SingleChannel, jmax (1, gm.gmBlackBoxX), jmax (1, gm.gmBlackBoxY), true); - - if (bufSize > 0) - { - topLeftX = (float) gm.gmptGlyphOrigin.x; - topLeftY = (float) -gm.gmptGlyphOrigin.y; - - uint8* const data = (uint8*) juce_calloc (bufSize); - - if (wGetGlyphOutlineW != 0) - wGetGlyphOutlineW (dc, character, format, &gm, bufSize, data, &identityMatrix); - else - GetGlyphOutline (dc, character, format, &gm, bufSize, data, &identityMatrix); - - const int stride = ((gm.gmBlackBoxX + 3) & ~3); - - for (int y = gm.gmBlackBoxY; --y >= 0;) - { - for (int x = gm.gmBlackBoxX; --x >= 0;) - { - const int level = data [x + y * stride] << shift; - - if (level > 0) - im->setPixelAt (x, y, Colour ((uint8) 0xff, (uint8) 0xff, (uint8) 0xff, (uint8) jmin (0xff, level))); - } + addKerningPair (kps[i].wFirst, kps[i].wSecond, + kps[i].iKernAmount / height); } - juce_free (data); + return true; } +}; - return im; -}*/ - -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, - bool italic, - bool addAllGlyphsToFont) throw() +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { - clear(); - - HDC dc = FontDCHolder::getInstance()->loadFont (fontName, bold, italic, 0); - - float height; - int firstChar, lastChar; - - { - TEXTMETRIC tm; - GetTextMetrics (dc, &tm); - - height = (float) tm.tmHeight; - firstChar = tm.tmFirstChar; - lastChar = tm.tmLastChar; - - setAscent (tm.tmAscent / height); - setDefaultCharacter (tm.tmDefaultChar); - } - - setName (fontName); - setBold (bold); - setItalic (italic); - - if (addAllGlyphsToFont) - { - for (int character = firstChar; character <= lastChar; ++character) - addGlyphToTypeface (dc, (juce_wchar) character, *this, false); - - int numKPs; - const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs); - - for (int i = 0; i < numKPs; ++i) - { - addKerningPair (kps[i].wFirst, - kps[i].wSecond, - kps[i].iKernAmount / height); - } - } + return new WindowsTypeface (font); } #endif @@ -253380,40 +253325,35 @@ public: int faceIndex; }; - FreeTypeFontFace (const String& familyName) + FreeTypeFontFace (const String& familyName) throw() : hasSerif (false), monospaced (false) { family = familyName; } - void setFileName (const String& name, - const int faceIndex, - FontStyle style) + void setFileName (const String& name, const int faceIndex, FontStyle style) throw() { - if (names[(int) style].fileName.isEmpty()) + if (names [(int) style].fileName.isEmpty()) { - names[(int) style].fileName = name; - names[(int) style].faceIndex = faceIndex; + names [(int) style].fileName = name; + names [(int) style].faceIndex = faceIndex; } } - const String& getFamilyName() const throw() - { - return family; - } + const String& getFamilyName() const throw() { return family; } - const String& getFileName (int style, int* faceIndex) const throw() + const String& getFileName (const int style, int& faceIndex) const throw() { - *faceIndex = names [style].faceIndex; + faceIndex = names[style].faceIndex; return names[style].fileName; } - void setMonospaced (bool mono) { monospaced = mono; } - bool getMonospaced () const throw() { return monospaced; } + void setMonospaced (bool mono) throw() { monospaced = mono; } + bool getMonospaced() const throw() { return monospaced; } - void setSerif (const bool serif) { hasSerif = serif; } - bool getSerif () const throw() { return hasSerif; } + void setSerif (const bool serif) throw() { hasSerif = serif; } + bool getSerif() const throw() { return hasSerif; } private: @@ -253534,23 +253474,13 @@ public: style |= (int) FreeTypeFontFace::Italic; newFace->setFileName (possible.getFullPathName(), faceIndex, (FreeTypeFontFace::FontStyle) style); - - if ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0) - newFace->setMonospaced (true); - else - newFace->setMonospaced (false); + newFace->setMonospaced ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0); // Surely there must be a better way to do this? - if (String (face->family_name).containsIgnoreCase (T("Sans")) - || String (face->family_name).containsIgnoreCase (T("Verdana")) - || String (face->family_name).containsIgnoreCase (T("Arial"))) - { - newFace->setSerif (false); - } - else - { - newFace->setSerif (true); - } + const String name (face->family_name); + newFace->setSerif (! (name.containsIgnoreCase (T("Sans")) + || name.containsIgnoreCase (T("Verdana")) + || name.containsIgnoreCase (T("Arial")))); } FT_Done_Face (face); @@ -253568,7 +253498,7 @@ public: const bool bold, const bool italic) throw() { - FT_Face face = NULL; + FT_Face face = 0; if (fontName == lastFontName && bold == lastBold && italic == lastItalic) { @@ -253576,10 +253506,10 @@ public: } else { - if (lastFace) + if (lastFace != 0) { FT_Done_Face (lastFace); - lastFace = NULL; + lastFace = 0; } lastFontName = fontName; @@ -253599,25 +253529,25 @@ public: style |= (int) FreeTypeFontFace::Italic; int faceIndex; - String fileName (ftFace->getFileName (style, &faceIndex)); + String fileName (ftFace->getFileName (style, faceIndex)); if (fileName.isEmpty()) { style ^= (int) FreeTypeFontFace::Bold; - fileName = ftFace->getFileName (style, &faceIndex); + fileName = ftFace->getFileName (style, faceIndex); if (fileName.isEmpty()) { style ^= (int) FreeTypeFontFace::Bold; style ^= (int) FreeTypeFontFace::Italic; - fileName = ftFace->getFileName (style, &faceIndex); + fileName = ftFace->getFileName (style, faceIndex); if (! fileName.length()) { style ^= (int) FreeTypeFontFace::Bold; - fileName = ftFace->getFileName (style, &faceIndex); + fileName = ftFace->getFileName (style, faceIndex); } } } @@ -253632,10 +253562,11 @@ public: } } } + return face; } - bool addGlyph (FT_Face face, Typeface& dest, uint32 character) throw() + bool addGlyph (FT_Face face, CustomTypeface& dest, uint32 character) throw() { const unsigned int glyphIndex = FT_Get_Char_Index (face, character); const float height = (float) (face->ascender - face->descender); @@ -253646,10 +253577,8 @@ public: #define CONVERTX(val) (scaleX * (val).x) #define CONVERTY(val) (scaleY * (val).y) - if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE - | FT_LOAD_NO_BITMAP - | FT_LOAD_IGNORE_TRANSFORM) != 0 - || face->glyph->format != ft_glyph_format_outline) + if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) != 0 + || face->glyph->format != ft_glyph_format_outline) { return false; } @@ -253738,7 +253667,7 @@ public: destShape.closeSubPath(); } - dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance/height); + dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance / height); if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) addKerning (face, dest, character, glyphIndex); @@ -253746,7 +253675,7 @@ public: return true; } - void addKerning (FT_Face face, Typeface& dest, const uint32 character, const uint32 glyphIndex) throw() + void addKerning (FT_Face face, CustomTypeface& dest, const uint32 character, const uint32 glyphIndex) throw() { const float height = (float) (face->ascender - face->descender); @@ -253770,7 +253699,7 @@ public: // Add a glyph to a font bool addGlyphToFont (const uint32 character, const tchar* fontName, bool bold, bool italic, - Typeface& dest) throw() + CustomTypeface& dest) throw() { FT_Face face = createFT_Face (fontName, bold, italic); @@ -253780,49 +253709,6 @@ public: return false; } - // Create a Typeface object for given name/style - bool createTypeface (const String& fontName, - const bool bold, const bool italic, - Typeface& dest, - const bool addAllGlyphs) throw() - { - dest.clear(); - dest.setName (fontName); - dest.setBold (bold); - dest.setItalic (italic); - - FT_Face face = createFT_Face (fontName, bold, italic); - - if (face == 0) - { -#ifdef JUCE_DEBUG - String msg (T("Failed to create typeface: ")); - msg << fontName << " " << (bold ? 'B' : ' ') << (italic ? 'I' : ' '); - DBG (msg); -#endif - return face; - } - - const float height = (float) (face->ascender - face->descender); - - dest.setAscent (face->ascender / height); - dest.setDefaultCharacter (L' '); - - if (addAllGlyphs) - { - uint32 glyphIndex; - uint32 charCode = FT_Get_First_Char (face, &glyphIndex); - - while (glyphIndex != 0) - { - addGlyph (face, dest, charCode); - charCode = FT_Get_Next_Char (face, charCode, &glyphIndex); - } - } - - return true; - } - void getFamilyNames (StringArray& familyNames) const throw() { for (int i = 0; i < faces.size(); i++) @@ -253863,18 +253749,41 @@ private: juce_ImplementSingleton_SingleThreaded (FreeTypeInterface) -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, bool italic, - bool addAllGlyphsToFont) throw() +class FreetypeTypeface : public CustomTypeface { - FreeTypeInterface::getInstance() - ->createTypeface (fontName, bold, italic, *this, addAllGlyphsToFont); -} +public: + FreetypeTypeface (const Font& font) + { + FT_Face face = FreeTypeInterface::getInstance() + ->createFT_Face (font.getTypefaceName(), font.isBold(), font.isItalic()); -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() + if (face == 0) + { +#ifdef JUCE_DEBUG + String msg (T("Failed to create typeface: ")); + msg << font.getTypefaceName() << " " << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' '); + DBG (msg); +#endif + } + else + { + setCharacteristics (font.getTypefaceName(), + face->ascender / (float) (face->ascender - face->descender), + font.isBold(), font.isItalic(), + L' '); + } + } + + bool loadGlyphIfPossible (const juce_wchar character) + { + return FreeTypeInterface::getInstance() + ->addGlyphToFont (character, name, isBold, isItalic, *this); + } +}; + +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { - return FreeTypeInterface::getInstance() - ->addGlyphToFont (character, getName(), isBold(), isItalic(), *this); + return new FreetypeTypeface (font); } const StringArray Font::findAllTypefaceNames() throw() @@ -253935,7 +253844,7 @@ static const String linux_getDefaultMonospacedFontName() return pickBestFont (allFonts, "Bitstream Vera Sans Mono, Courier, Sans Mono, Mono"); } -void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() { defaultSans = linux_getDefaultSansSerifFontName(); defaultSerif = linux_getDefaultSerifFontName(); @@ -261940,6 +261849,373 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() #if ! JUCE_ONLY_BUILD_CORE_LIBRARY #if JUCE_IPHONE +/********* Start of inlined file: juce_mac_Fonts.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +class MacTypeface : public Typeface +{ +public: + + MacTypeface (const Font& font) + : Typeface (font.getTypefaceName()), + charToGlyphMapper (0) + { + const ScopedAutoReleasePool pool; + renderingTransform = CGAffineTransformIdentity; + + bool needsItalicTransform = false; + +#if JUCE_IPHONE + NSString* fontName = juceStringToNS (font.getTypefaceName()); + + if (font.isItalic() || font.isBold()) + { + NSArray* familyFonts = [UIFont fontNamesForFamilyName: juceStringToNS (font.getTypefaceName())]; + + for (NSString* i in familyFonts) + { + const String fn (nsStringToJuce (i)); + const String afterDash (fn.fromFirstOccurrenceOf (T("-"), false, false)); + + const bool probablyBold = afterDash.containsIgnoreCase (T("bold")) || fn.endsWithIgnoreCase (T("bold")); + const bool probablyItalic = afterDash.containsIgnoreCase (T("oblique")) + || afterDash.containsIgnoreCase (T("italic")) + || fn.endsWithIgnoreCase (T("oblique")) + || fn.endsWithIgnoreCase (T("italic")); + + if (probablyBold == font.isBold() + && probablyItalic == font.isItalic()) + { + fontName = i; + needsItalicTransform = false; + break; + } + else if (probablyBold && (! probablyItalic) && probablyBold == font.isBold()) + { + fontName = i; + needsItalicTransform = true; // not ideal, so carry on in case we find a better one + } + } + + if (needsItalicTransform) + renderingTransform.c = 0.15f; + } + + fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); + +#else + nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; + + if (font.isItalic()) + { + NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: nsFont + toHaveTrait: NSItalicFontMask]; + + if (newFont == nsFont) + needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. + + nsFont = newFont; + } + + if (font.isBold()) + nsFont = [[NSFontManager sharedFontManager] convertFont: nsFont toHaveTrait: NSBoldFontMask]; + + [nsFont retain]; + + ascent = fabsf ([nsFont ascender]); + float totalSize = ascent + fabsf ([nsFont descender]); + ascent /= totalSize; + + pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); + + if (needsItalicTransform) + { + pathTransform = pathTransform.sheared (-0.15, 0); + renderingTransform.c = 0.15f; + } + + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); +#endif + + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + } + + ~MacTypeface() + { + [nsFont release]; + CGFontRelease (fontRef); + delete charToGlyphMapper; + } + + float getAscent() const + { + return ascent; + } + + float getDescent() const + { + return 1.0f - ascent; + } + + float getStringWidth (const String& text) + { + if (fontRef == 0) + return 0; + + Array glyphs (128); + createGlyphsForString (text, glyphs); + + if (glyphs.size() == 0) + return 0; + + 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]; + + juce_free (advances); + return x * unitsToHeightScaleFactor; + } + + void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) + { + if (fontRef == 0) + return; + + createGlyphsForString (text, glyphs); + + xOffsets.add (0); + if (glyphs.size() == 0) + return; + + int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) + { + int x = 0; + for (int i = 0; i < glyphs.size(); ++i) + { + x += advances [i * 2]; + xOffsets.add (x * unitsToHeightScaleFactor); + } + } + + juce_free (advances); + } + + bool getOutlineForGlyph (int glyphNumber, Path& path) + { +#if JUCE_IPHONE + return false; +#else + if (nsFont == 0) + return false; + + // we might need to apply a transform to the path, so it mustn't have anything else in it + jassert (path.isEmpty()); + + const ScopedAutoReleasePool pool; + + NSBezierPath* bez = [NSBezierPath bezierPath]; + [bez moveToPoint: NSMakePoint (0, 0)]; + [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber + inFont: nsFont]; + + for (int i = 0; i < [bez elementCount]; ++i) + { + NSPoint p[3]; + switch ([bez elementAtIndex: i associatedPoints: p]) + { + case NSMoveToBezierPathElement: + path.startNewSubPath (p[0].x, -p[0].y); + break; + case NSLineToBezierPathElement: + path.lineTo (p[0].x, -p[0].y); + break; + case NSCurveToBezierPathElement: + path.cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); + break; + case NSClosePathBezierPathElement: + path.closeSubPath(); + break; + default: + jassertfalse + break; + } + } + + path.applyTransform (pathTransform); + return true; +#endif + } + + juce_UseDebuggingNewOperator + + CGFontRef fontRef; + float fontHeightToCGSizeFactor; + CGAffineTransform renderingTransform; + +private: + float ascent, unitsToHeightScaleFactor; + +#if JUCE_IPHONE + +#else + NSFont* nsFont; + AffineTransform pathTransform; +#endif + + void createGlyphsForString (const String& text, Array & dest) throw() + { + if (charToGlyphMapper == 0) + charToGlyphMapper = new CharToGlyphMapper (fontRef); + + const juce_wchar* t = (const juce_wchar*) text; + + while (*t != 0) + dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); + } + + // Reads a CGFontRef's character map table to convert unicode into glyph numbers + class CharToGlyphMapper + { + public: + CharToGlyphMapper (CGFontRef fontRef) throw() + : segCount (0), endCode (0), startCode (0), idDelta (0), + idRangeOffset (0), glyphIndexes (0) + { + CFDataRef cmapTable = CGFontCopyTableForTag (fontRef, 'cmap'); + + if (cmapTable != 0) + { + const int numSubtables = getValue16 (cmapTable, 2); + + for (int i = 0; i < numSubtables; ++i) + { + if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0 + { + const int offset = getValue32 (cmapTable, i * 8 + 8); + + if (getValue16 (cmapTable, offset) == 4) // check that it's format 4.. + { + const int length = getValue16 (cmapTable, offset + 2); + const int segCountX2 = getValue16 (cmapTable, offset + 6); + segCount = segCountX2 / 2; + const int endCodeOffset = offset + 14; + const int startCodeOffset = endCodeOffset + 2 + segCountX2; + const int idDeltaOffset = startCodeOffset + segCountX2; + const int idRangeOffsetOffset = idDeltaOffset + segCountX2; + const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2; + + endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2); + startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2); + idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2); + idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2); + glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset); + } + + break; + } + } + + CFRelease (cmapTable); + } + } + + ~CharToGlyphMapper() throw() + { + if (endCode != 0) + { + CFRelease (endCode); + CFRelease (startCode); + CFRelease (idDelta); + CFRelease (idRangeOffset); + CFRelease (glyphIndexes); + } + } + + int getGlyphForCharacter (const juce_wchar c) const throw() + { + for (int i = 0; i < segCount; ++i) + { + if (getValue16 (endCode, i * 2) >= c) + { + const int start = getValue16 (startCode, i * 2); + if (start > c) + break; + + const int delta = getValue16 (idDelta, i * 2); + const int rangeOffset = getValue16 (idRangeOffset, i * 2); + + if (rangeOffset == 0) + return delta + c; + else + return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i))); + } + } + + // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts! + return jmax (-1, c - 29); + } + + private: + int segCount; + CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes; + + static uint16 getValue16 (CFDataRef data, const int index) throw() + { + return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index)); + } + + static uint32 getValue32 (CFDataRef data, const int index) throw() + { + return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index)); + } + }; + + CharToGlyphMapper* charToGlyphMapper; +}; + +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) +{ + return new MacTypeface (font); +} + +const StringArray Font::findAllTypefaceNames() throw() +{ + StringArray names; + + const ScopedAutoReleasePool pool; + +#if JUCE_IPHONE + NSArray* fonts = [UIFont familyNames]; +#else + NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies]; +#endif + + for (unsigned int i = 0; i < [fonts count]; ++i) + names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); + + names.sort (true); + return names; +} + +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +{ + defaultSans = "Lucida Grande"; + defaultSerif = "Times New Roman"; + defaultFixed = "Monaco"; +} + +#endif +/********* End of inlined file: juce_mac_Fonts.mm *********/ + /********* Start of inlined file: juce_iphone_UIViewComponentPeer.mm *********/ // (This file gets included by juce_mac_NativeCode.mm, rather than being // compiled on its own). @@ -262822,331 +263098,6 @@ const int KeyPress::rewindKey = 0x30003; #endif /********* End of inlined file: juce_iphone_UIViewComponentPeer.mm *********/ -/********* Start of inlined file: juce_iphone_Fonts.mm *********/ -// (This file gets included by juce_mac_NativeCode.mm, rather than being -// compiled on its own). -#if JUCE_INCLUDED_FILE - -class FontHelper -{ - UIFont* font; - -public: - String name; - bool isBold, isItalic, needsItalicTransform; - float fontSize, totalSize, ascent; - int refCount; - - FontHelper (const String& name_, - const bool bold_, - const bool italic_, - const float size_) - : font (0), - name (name_), - isBold (bold_), - isItalic (italic_), - needsItalicTransform (false), - fontSize (size_), - refCount (1) - { - //attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] - // forKey: NSLigatureAttributeName] retain]; - - font = [UIFont fontWithName: juceStringToNS (name_) size: size_]; - - if (italic_) - { -/* NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; - - if (newFont == font) - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - - font = newFont;*/ - } - -// if (bold_) - // font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; - - [font retain]; - - ascent = fabsf (font.ascender); - totalSize = ascent + fabsf (font.descender); - } - - ~FontHelper() - { - [font release]; - } - - bool getPathAndKerning (const juce_wchar char1, - const juce_wchar char2, - Path* path, - float& kerning, - float* ascent, - float* descent) - { - const ScopedAutoReleasePool pool; - - return false; -/* if (font == 0 - || ! [[font coveredCharacterSet] longCharacterIsMember: (UTF32Char) char1]) - return false; - - String chars; - chars << ' ' << char1 << char2; - - NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) - attributes: attributes] autorelease]; - NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; - NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; - [layoutManager addTextContainer: textContainer]; - [textStorage addLayoutManager: layoutManager]; - [textStorage setFont: font]; - - unsigned int glyphIndex = [layoutManager glyphRangeForCharacterRange: NSMakeRange (1, 1) - actualCharacterRange: 0].location; - NSPoint p1 = [layoutManager locationForGlyphAtIndex: glyphIndex]; - NSPoint p2 = [layoutManager locationForGlyphAtIndex: glyphIndex + 1]; - kerning = p2.x - p1.x; - - if (ascent != 0) - *ascent = this->ascent; - - if (descent != 0) - *descent = fabsf ([font descender]); - - if (path != 0) - { - NSBezierPath* bez = [NSBezierPath bezierPath]; - [bez moveToPoint: NSMakePoint (0, 0)]; - [bez appendBezierPathWithGlyph: [layoutManager glyphAtIndex: glyphIndex] - inFont: font]; - - for (int i = 0; i < [bez elementCount]; ++i) - { - NSPoint p[3]; - switch ([bez elementAtIndex: i associatedPoints: p]) - { - case NSMoveToBezierPathElement: - path->startNewSubPath (p[0].x, -p[0].y); - break; - case NSLineToBezierPathElement: - path->lineTo (p[0].x, -p[0].y); - break; - case NSCurveToBezierPathElement: - path->cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); - break; - case NSClosePathBezierPathElement: - path->closeSubPath(); - break; - default: - jassertfalse - break; - } - } - - if (needsItalicTransform) - path->applyTransform (AffineTransform::identity.sheared (-0.15, 0)); - } - - return kerning != 0;*/ - } - - juce_wchar getDefaultChar() - { - return 0; - } -}; - -class FontHelperCache : public Timer, - public DeletedAtShutdown -{ - VoidArray cache; - -public: - FontHelperCache() - { - } - - ~FontHelperCache() - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - delete f; - } - - clearSingletonInstance(); - } - - FontHelper* getFont (const String& name, - const bool bold, - const bool italic, - const float size = 1024) - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - - if (f->name == name - && f->isBold == bold - && f->isItalic == italic - && f->fontSize == size) - { - f->refCount++; - return f; - } - } - - FontHelper* const f = new FontHelper (name, bold, italic, size); - cache.add (f); - return f; - } - - void releaseFont (FontHelper* f) - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f2 = (FontHelper*) cache.getUnchecked(i); - - if (f == f2) - { - f->refCount--; - - if (f->refCount == 0) - startTimer (5000); - - break; - } - } - } - - void timerCallback() - { - stopTimer(); - - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - - if (f->refCount == 0) - { - cache.remove (i); - delete f; - } - } - - if (cache.size() == 0) - delete this; - } - - juce_DeclareSingleton_SingleThreaded_Minimal (FontHelperCache) -}; - -juce_ImplementSingleton_SingleThreaded (FontHelperCache) - -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, - bool italic, - bool addAllGlyphsToFont) throw() -{ - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (fontName, bold, italic); - - clear(); - setAscent (helper->ascent / helper->totalSize); - setName (fontName); - setDefaultCharacter (helper->getDefaultChar()); - setBold (bold); - setItalic (italic); - - if (addAllGlyphsToFont) - { - //xxx - jassertfalse - } - - FontHelperCache::getInstance()->releaseFont (helper); -} - -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() -{ - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - if (character == 0) - return false; - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (getName(), isBold(), isItalic()); - - Path path; - float width; - bool foundOne = false; - - if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0)) - { - path.applyTransform (AffineTransform::scale (1.0f / helper->totalSize, - 1.0f / helper->totalSize)); - - addGlyph (character, path, width / helper->totalSize); - - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - float kerning; - if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0)) - { - kerning = (kerning - width) / helper->totalSize; - - if (kerning != 0) - addKerningPair (character, g->getCharacter(), kerning); - } - - if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0)) - { - kerning = kerning / helper->totalSize - g->width; - - if (kerning != 0) - addKerningPair (g->getCharacter(), character, kerning); - } - } - - foundOne = true; - } - - FontHelperCache::getInstance()->releaseFont (helper); - return foundOne; -} - -const StringArray Font::findAllTypefaceNames() throw() -{ - StringArray names; - - const ScopedAutoReleasePool pool; - NSArray* fonts = [UIFont familyNames]; - - for (unsigned int i = 0; i < [fonts count]; ++i) - names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); - - names.sort (true); - return names; -} - -void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() -{ - defaultSans = "Lucida Grande"; - defaultSerif = "Times New Roman"; - defaultFixed = "Monaco"; -} - -#endif -/********* End of inlined file: juce_iphone_Fonts.mm *********/ - /********* Start of inlined file: juce_iphone_MessageManager.mm *********/ // (This file gets included by juce_mac_NativeCode.mm, rather than being // compiled on its own). @@ -265554,6 +265505,375 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) #else +/********* Start of inlined file: juce_mac_Fonts.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +class MacTypeface : public Typeface +{ +public: + + MacTypeface (const Font& font) + : Typeface (font.getTypefaceName()), + charToGlyphMapper (0) + { + const ScopedAutoReleasePool pool; + renderingTransform = CGAffineTransformIdentity; + + bool needsItalicTransform = false; + +#if JUCE_IPHONE + NSString* fontName = juceStringToNS (font.getTypefaceName()); + + if (font.isItalic() || font.isBold()) + { + NSArray* familyFonts = [UIFont fontNamesForFamilyName: juceStringToNS (font.getTypefaceName())]; + + for (NSString* i in familyFonts) + { + const String fn (nsStringToJuce (i)); + const String afterDash (fn.fromFirstOccurrenceOf (T("-"), false, false)); + + const bool probablyBold = afterDash.containsIgnoreCase (T("bold")) || fn.endsWithIgnoreCase (T("bold")); + const bool probablyItalic = afterDash.containsIgnoreCase (T("oblique")) + || afterDash.containsIgnoreCase (T("italic")) + || fn.endsWithIgnoreCase (T("oblique")) + || fn.endsWithIgnoreCase (T("italic")); + + if (probablyBold == font.isBold() + && probablyItalic == font.isItalic()) + { + fontName = i; + needsItalicTransform = false; + break; + } + else if (probablyBold && (! probablyItalic) && probablyBold == font.isBold()) + { + fontName = i; + needsItalicTransform = true; // not ideal, so carry on in case we find a better one + } + } + + if (needsItalicTransform) + renderingTransform.c = 0.15f; + } + + fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); + +#else + nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; + + if (font.isItalic()) + { + NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: nsFont + toHaveTrait: NSItalicFontMask]; + + if (newFont == nsFont) + needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. + + nsFont = newFont; + } + + if (font.isBold()) + nsFont = [[NSFontManager sharedFontManager] convertFont: nsFont toHaveTrait: NSBoldFontMask]; + + [nsFont retain]; + + ascent = fabsf ([nsFont ascender]); + float totalSize = ascent + fabsf ([nsFont descender]); + ascent /= totalSize; + + pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); + + if (needsItalicTransform) + { + pathTransform = pathTransform.sheared (-0.15, 0); + renderingTransform.c = 0.15f; + } + + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); +#endif + + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; + } + + ~MacTypeface() + { + [nsFont release]; + CGFontRelease (fontRef); + delete charToGlyphMapper; + } + + float getAscent() const + { + return ascent; + } + + float getDescent() const + { + return 1.0f - ascent; + } + + float getStringWidth (const String& text) + { + if (fontRef == 0) + return 0; + + Array glyphs (128); + createGlyphsForString (text, glyphs); + + if (glyphs.size() == 0) + return 0; + + 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]; + + juce_free (advances); + return x * unitsToHeightScaleFactor; + } + + void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) + { + if (fontRef == 0) + return; + + createGlyphsForString (text, glyphs); + + xOffsets.add (0); + if (glyphs.size() == 0) + return; + + int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) + { + int x = 0; + for (int i = 0; i < glyphs.size(); ++i) + { + x += advances [i * 2]; + xOffsets.add (x * unitsToHeightScaleFactor); + } + } + + juce_free (advances); + } + + bool getOutlineForGlyph (int glyphNumber, Path& path) + { +#if JUCE_IPHONE + return false; +#else + if (nsFont == 0) + return false; + + // we might need to apply a transform to the path, so it mustn't have anything else in it + jassert (path.isEmpty()); + + const ScopedAutoReleasePool pool; + + NSBezierPath* bez = [NSBezierPath bezierPath]; + [bez moveToPoint: NSMakePoint (0, 0)]; + [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber + inFont: nsFont]; + + for (int i = 0; i < [bez elementCount]; ++i) + { + NSPoint p[3]; + switch ([bez elementAtIndex: i associatedPoints: p]) + { + case NSMoveToBezierPathElement: + path.startNewSubPath (p[0].x, -p[0].y); + break; + case NSLineToBezierPathElement: + path.lineTo (p[0].x, -p[0].y); + break; + case NSCurveToBezierPathElement: + path.cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); + break; + case NSClosePathBezierPathElement: + path.closeSubPath(); + break; + default: + jassertfalse + break; + } + } + + path.applyTransform (pathTransform); + return true; +#endif + } + + juce_UseDebuggingNewOperator + + CGFontRef fontRef; + float fontHeightToCGSizeFactor; + CGAffineTransform renderingTransform; + +private: + float ascent, unitsToHeightScaleFactor; + +#if JUCE_IPHONE + +#else + NSFont* nsFont; + AffineTransform pathTransform; +#endif + + void createGlyphsForString (const String& text, Array & dest) throw() + { + if (charToGlyphMapper == 0) + charToGlyphMapper = new CharToGlyphMapper (fontRef); + + const juce_wchar* t = (const juce_wchar*) text; + + while (*t != 0) + dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); + } + + // Reads a CGFontRef's character map table to convert unicode into glyph numbers + class CharToGlyphMapper + { + public: + CharToGlyphMapper (CGFontRef fontRef) throw() + : segCount (0), endCode (0), startCode (0), idDelta (0), + idRangeOffset (0), glyphIndexes (0) + { + CFDataRef cmapTable = CGFontCopyTableForTag (fontRef, 'cmap'); + + if (cmapTable != 0) + { + const int numSubtables = getValue16 (cmapTable, 2); + + for (int i = 0; i < numSubtables; ++i) + { + if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0 + { + const int offset = getValue32 (cmapTable, i * 8 + 8); + + if (getValue16 (cmapTable, offset) == 4) // check that it's format 4.. + { + const int length = getValue16 (cmapTable, offset + 2); + const int segCountX2 = getValue16 (cmapTable, offset + 6); + segCount = segCountX2 / 2; + const int endCodeOffset = offset + 14; + const int startCodeOffset = endCodeOffset + 2 + segCountX2; + const int idDeltaOffset = startCodeOffset + segCountX2; + const int idRangeOffsetOffset = idDeltaOffset + segCountX2; + const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2; + + endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2); + startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2); + idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2); + idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2); + glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset); + } + + break; + } + } + + CFRelease (cmapTable); + } + } + + ~CharToGlyphMapper() throw() + { + if (endCode != 0) + { + CFRelease (endCode); + CFRelease (startCode); + CFRelease (idDelta); + CFRelease (idRangeOffset); + CFRelease (glyphIndexes); + } + } + + int getGlyphForCharacter (const juce_wchar c) const throw() + { + for (int i = 0; i < segCount; ++i) + { + if (getValue16 (endCode, i * 2) >= c) + { + const int start = getValue16 (startCode, i * 2); + if (start > c) + break; + + const int delta = getValue16 (idDelta, i * 2); + const int rangeOffset = getValue16 (idRangeOffset, i * 2); + + if (rangeOffset == 0) + return delta + c; + else + return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i))); + } + } + + // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts! + return jmax (-1, c - 29); + } + + private: + int segCount; + CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes; + + static uint16 getValue16 (CFDataRef data, const int index) throw() + { + return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index)); + } + + static uint32 getValue32 (CFDataRef data, const int index) throw() + { + return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index)); + } + }; + + CharToGlyphMapper* charToGlyphMapper; +}; + +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) +{ + return new MacTypeface (font); +} + +const StringArray Font::findAllTypefaceNames() throw() +{ + StringArray names; + + const ScopedAutoReleasePool pool; + +#if JUCE_IPHONE + NSArray* fonts = [UIFont familyNames]; +#else + NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies]; +#endif + + for (unsigned int i = 0; i < [fonts count]; ++i) + names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); + + names.sort (true); + return names; +} + +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +{ + defaultSans = "Lucida Grande"; + defaultSerif = "Times New Roman"; + defaultFixed = "Monaco"; +} + +#endif +/********* End of inlined file: juce_mac_Fonts.mm *********/ + + // (must go before juce_mac_CoreGraphicsContext.mm) + /********* Start of inlined file: juce_mac_CoreGraphicsContext.mm *********/ // (This file gets included by juce_mac_NativeCode.mm, rather than being // compiled on its own). @@ -265567,11 +265887,15 @@ public: flipHeight (flipHeight_) { CGContextRetain (context); + CGContextSetShouldSmoothFonts (context, true); + CGContextSetBlendMode (context, kCGBlendModeNormal); + state = new SavedState(); } ~CoreGraphicsContext() { CGContextRelease (context); + delete state; } bool isVectorDevice() const { return false; } @@ -265613,11 +265937,25 @@ public: void saveState() { CGContextSaveGState (context); + stateStack.add (new SavedState (*state)); } void restoreState() { CGContextRestoreGState (context); + + SavedState* const top = stateStack.getLast(); + + if (top != 0) + { + delete state; + state = top; + stateStack.removeLast (1, false); + } + else + { + jassertfalse // trying to pop with an empty stack! + } } bool clipRegionIntersects (int x, int y, int w, int h) @@ -265640,95 +265978,116 @@ public: return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); } - void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) + void setColour (const Colour& colour) { - CGContextSetBlendMode (context, replaceExistingContents ? kCGBlendModeCopy : kCGBlendModeNormal); + state->colour = colour; + deleteAndZero (state->gradient); + + CGContextSetRGBFillColor (context, + colour.getFloatRed(), colour.getFloatGreen(), + colour.getFloatBlue(), colour.getFloatAlpha()); CGContextSetAlpha (context, 1.0f); - setColour (colour); - CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); } - void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) + void setGradient (const ColourGradient& gradient) { - CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); - flip(); - drawGradient (gradient); - CGContextRestoreGState (context); + if (state->gradient == 0) + state->gradient = new ColourGradient (gradient); + else + *state->gradient = gradient; } - void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel /*quality*/) + void setOpacity (float opacity) { - CGContextSetAlpha (context, 1.0f); - CGContextSaveGState (context); - flip(); - applyTransform (transform); - createPath (path); - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); - CGContextFillPath (context); - CGContextRestoreGState (context); + setColour (state->colour.withAlpha (opacity)); } - void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) + void setInterpolationQuality (Graphics::ResamplingQuality quality) + { + CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality + ? kCGInterpolationLow + : kCGInterpolationHigh); + } + + void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) + { + if (replaceExistingContents) + { + CGContextSetBlendMode (context, kCGBlendModeCopy); + fillRect (x, y, w, h, false); + CGContextSetBlendMode (context, kCGBlendModeNormal); + } + else + { + if (state->gradient == 0) + { + CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + } + else + { + CGContextSaveGState (context); + CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + flip(); + drawGradient(); + CGContextRestoreGState (context); + } + } + } + + void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality) { CGContextSaveGState (context); - createPath (path, transform); - CGContextClip (context); - flip(); - applyTransform (gradient.transform); - drawGradient (gradient); + + if (state->gradient == 0) + { + flip(); + applyTransform (transform); + createPath (path); + CGContextFillPath (context); + } + else + { + createPath (path, transform); + CGContextClip (context); + flip(); + applyTransform (state->gradient->transform); + drawGradient(); + } + CGContextRestoreGState (context); } void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel /*quality*/) + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality) { CGContextSaveGState (context); createPath (path, transform); CGContextClip (context); - blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0, alpha); + blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0); CGContextRestoreGState (context); } - void fillAlphaChannelWithColour (const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour) + void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) { Image* singleChannelImage = createAlphaChannelImage (alphaImage); CGImageRef image = createImage (*singleChannelImage, true); + CGContextSaveGState (context); CGContextSetAlpha (context, 1.0f); - CGContextSaveGState (context); + CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), alphaImage.getWidth(), alphaImage.getHeight()); CGContextClipToMask (context, r, image); - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); - CGContextFillRect (context, r); + + fillRect (alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight(), false); + CGContextRestoreGState (context); - - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - void fillAlphaChannelWithGradient (const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - flip(); - drawGradient (gradient); - CGContextRestoreGState (context); - CGImageRelease (image); deleteAlphaChannelImage (alphaImage, singleChannelImage); } void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha) + const Image& fillerImage, int fillerImageX, int fillerImageY) { Image* singleChannelImage = createAlphaChannelImage (alphaImage); CGImageRef image = createImage (*singleChannelImage, true); @@ -265740,7 +266099,7 @@ public: blendImage (fillerImage, fillerImageX, fillerImageY, fillerImage.getWidth(), fillerImage.getHeight(), - 0, 0, alpha); + 0, 0); CGContextRestoreGState (context); @@ -265749,15 +266108,13 @@ public: } void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY, - float alpha) + int destX, int destY, int destW, int destH, int sourceX, int sourceY) { - CGContextSetBlendMode (context, kCGBlendModeNormal); CGImageRef image = createImage (sourceImage, false); CGContextSaveGState (context); CGContextClipToRect (context, CGRectMake (destX, flipHeight - (destY + destH), destW, destH)); - CGContextSetAlpha (context, alpha); + CGContextSetAlpha (context, state->colour.getFloatAlpha()); CGContextDrawImage (context, CGRectMake (destX - sourceX, flipHeight - ((destY - sourceY) + sourceImage.getHeight()), sourceImage.getWidth(), @@ -265769,24 +266126,18 @@ public: void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality) + const AffineTransform& transform) { - CGContextSetBlendMode (context, kCGBlendModeNormal); CGImageRef fullImage = createImage (sourceImage, false); CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), srcClipW, srcClipH)); CGImageRelease (fullImage); CGContextSaveGState (context); - CGContextSetAlpha (context, alpha); flip(); applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); - CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality - ? kCGInterpolationLow - : kCGInterpolationHigh); - + CGContextSetAlpha (context, state->colour.getFloatAlpha()); CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()), image); @@ -265794,43 +266145,123 @@ public: CGContextRestoreGState (context); } - void drawLine (double x1, double y1, double x2, double y2, const Colour& colour) + void drawLine (double x1, double y1, double x2, double y2) { - CGContextSetAlpha (context, 1.0f); - CGContextSetRGBStrokeColor (context, colour.getFloatRed(), colour.getFloatGreen(), - colour.getFloatBlue(), colour.getFloatAlpha()); CGContextSetLineCap (context, kCGLineCapSquare); CGContextSetLineWidth (context, 1.0f); + CGContextSetRGBStrokeColor (context, + state->colour.getFloatRed(), state->colour.getFloatGreen(), + state->colour.getFloatBlue(), state->colour.getFloatAlpha()); CGPoint line[] = { { x1 + 0.5f, flipHeight - (y1 + 0.5f) }, { x2 + 0.5f, flipHeight - (y2 + 0.5f) } }; + CGContextStrokeLineSegments (context, line, 1); } - void drawVerticalLine (const int x, double top, double bottom, const Colour& colour) + void drawVerticalLine (const int x, double top, double bottom) { - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); CGContextFillRect (context, CGRectMake (x, flipHeight - bottom, 1.0f, bottom - top)); } - void drawHorizontalLine (const int y, double left, double right, const Colour& colour) + void drawHorizontalLine (const int y, double left, double right) { - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); CGContextFillRect (context, CGRectMake (left, y, right - left, 1.0f)); } + void setFont (const Font& newFont) + { + if (state->font != newFont) + { + state->fontRef = 0; + state->font = newFont; + + MacTypeface* mf = dynamic_cast ((Typeface*) state->font.getTypeface()); + + if (mf != 0) + { + state->fontRef = mf->fontRef; + CGContextSetFont (context, state->fontRef); + CGContextSetFontSize (context, state->font.getHeight() * mf->fontHeightToCGSizeFactor); + + state->fontTransform = mf->renderingTransform; + state->fontTransform.a *= state->font.getHorizontalScale(); + CGContextSetTextMatrix (context, state->fontTransform); + } + } + } + + void drawGlyph (int glyphNumber, float x, float y) + { + if (state->fontRef != 0) + { + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, x, flipHeight - y, &g, 1); + } + else + { + state->font.renderGlyphIndirectly (*this, glyphNumber, x, y); + } + } + + void drawGlyph (int glyphNumber, const AffineTransform& transform) + { + if (state->fontRef != 0) + { + CGContextSaveGState (context); + flip(); + applyTransform (transform); + + CGAffineTransform t = state->fontTransform; + t.d = -t.d; + CGContextSetTextMatrix (context, t); + + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); + + CGContextSetTextMatrix (context, state->fontTransform); + CGContextRestoreGState (context); + } + else + { + state->font.renderGlyphIndirectly (*this, glyphNumber, transform); + } + } + private: CGContextRef context; const float flipHeight; - void setColour (const Colour& colour) const throw() + struct SavedState { - CGContextSetRGBFillColor (context, - colour.getFloatRed(), colour.getFloatGreen(), - colour.getFloatBlue(), colour.getFloatAlpha()); - } + SavedState() throw() + : gradient (0), font (1.0f), fontRef (0), + fontTransform (CGAffineTransformIdentity) + { + } + + SavedState (const SavedState& other) throw() + : colour (other.colour), + gradient (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0), + font (other.font), fontRef (other.fontRef), + fontTransform (other.fontTransform) + { + } + + ~SavedState() throw() + { + delete gradient; + } + + Colour colour; + ColourGradient* gradient; + Font font; + CGFontRef fontRef; + CGAffineTransform fontTransform; + }; + + SavedState* state; + OwnedArray stateStack; static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) { @@ -265842,26 +266273,26 @@ private: outData[3] = c.getFloatAlpha(); } - CGShadingRef createGradient (const ColourGradient& gradient) const throw() + static CGShadingRef createGradient (const ColourGradient* const gradient) throw() { CGShadingRef result = 0; CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); CGFunctionCallbacks callbacks = { 0, gradientCallback, 0 }; - CGFunctionRef function = CGFunctionCreate ((void*) &gradient, 1, 0, 4, 0, &callbacks); - CGPoint p1 = CGPointMake (gradient.x1, gradient.y1); + CGFunctionRef function = CGFunctionCreate ((void*) gradient, 1, 0, 4, 0, &callbacks); + CGPoint p1 = CGPointMake (gradient->x1, gradient->y1); - if (gradient.isRadial) + if (gradient->isRadial) { result = CGShadingCreateRadial (colourSpace, p1, 0, - p1, hypotf (gradient.x1 - gradient.x2, gradient.y1 - gradient.y2), + p1, hypotf (gradient->x1 - gradient->x2, gradient->y1 - gradient->y2), function, true, true); } else { result = CGShadingCreateAxial (colourSpace, p1, - CGPointMake (gradient.x2, gradient.y2), + CGPointMake (gradient->x2, gradient->y2), function, true, true); } @@ -265870,11 +266301,10 @@ private: return result; } - void drawGradient (const ColourGradient& gradient) const throw() + void drawGradient() const throw() { - CGContextSetBlendMode (context, kCGBlendModeNormal); CGContextSetAlpha (context, 1.0f); - CGShadingRef shading = createGradient (gradient); + CGShadingRef shading = createGradient (state->gradient); CGContextDrawShading (context, shading); CGShadingRelease (shading); } @@ -266052,6 +266482,7 @@ END_JUCE_NAMESPACE - (void) scrollWheel: (NSEvent*) ev; - (BOOL) acceptsFirstMouse: (NSEvent*) ev; - (void) frameChanged: (NSNotification*) n; +- (void) viewDidMoveToWindow; - (void) keyDown: (NSEvent*) ev; - (void) keyUp: (NSEvent*) ev; @@ -266169,6 +266600,8 @@ public: virtual bool windowShouldClose(); virtual void redirectMovedOrResized(); + virtual void viewMovedToWindow(); + virtual NSRect constrainRect (NSRect r); static void showArrowCursorIfNeeded(); @@ -266345,6 +266778,12 @@ END_JUCE_NAMESPACE owner->redirectMovedOrResized(); } +- (void) viewDidMoveToWindow +{ + if (owner != 0) + owner->viewMovedToWindow(); +} + - (void) asyncRepaint: (id) rect { NSRect* r = (NSRect*) [((NSData*) rect) bytes]; @@ -267463,6 +267902,12 @@ void NSViewComponentPeer::redirectMovedOrResized() handleMovedOrResized(); } +void NSViewComponentPeer::viewMovedToWindow() +{ + if (isSharedWindow) + window = [view window]; +} + void juce_setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool allowMenusAndBars) { // Very annoyingly, this function has to use the old SetSystemUIMode function, @@ -269745,332 +270190,6 @@ void AudioCDReader::ejectDisk() #endif /********* End of inlined file: juce_mac_AudioCDBurner.mm *********/ -/********* Start of inlined file: juce_mac_Fonts.mm *********/ -// (This file gets included by juce_mac_NativeCode.mm, rather than being -// compiled on its own). -#if JUCE_INCLUDED_FILE - -class FontHelper -{ - NSFont* font; - -public: - String name; - bool isBold, isItalic, needsItalicTransform; - float fontSize, totalSize, ascent; - int refCount; - NSMutableDictionary* attributes; - - FontHelper (const String& name_, - const bool bold_, - const bool italic_, - const float size_) - : font (0), - name (name_), - isBold (bold_), - isItalic (italic_), - needsItalicTransform (false), - fontSize (size_), - refCount (1) - { - attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] - forKey: NSLigatureAttributeName] retain]; - - font = [NSFont fontWithName: juceStringToNS (name_) size: size_]; - - if (italic_) - { - NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; - - if (newFont == font) - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - - font = newFont; - } - - if (bold_) - font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; - - [font retain]; - - ascent = fabsf ([font ascender]); - totalSize = ascent + fabsf ([font descender]); - } - - ~FontHelper() - { - [font release]; - [attributes release]; - } - - bool getPathAndKerning (const juce_wchar char1, - const juce_wchar char2, - Path* path, - float& kerning, - float* ascent, - float* descent) - { - const ScopedAutoReleasePool pool; - - if (font == 0 - || ! [[font coveredCharacterSet] longCharacterIsMember: (UTF32Char) char1]) - return false; - - String chars; - chars << ' ' << char1 << char2; - - NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) - attributes: attributes] autorelease]; - NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; - NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; - [layoutManager addTextContainer: textContainer]; - [textStorage addLayoutManager: layoutManager]; - [textStorage setFont: font]; - - unsigned int glyphIndex = [layoutManager glyphRangeForCharacterRange: NSMakeRange (1, 1) - actualCharacterRange: 0].location; - NSPoint p1 = [layoutManager locationForGlyphAtIndex: glyphIndex]; - NSPoint p2 = [layoutManager locationForGlyphAtIndex: glyphIndex + 1]; - kerning = p2.x - p1.x; - - if (ascent != 0) - *ascent = this->ascent; - - if (descent != 0) - *descent = fabsf ([font descender]); - - if (path != 0) - { - NSBezierPath* bez = [NSBezierPath bezierPath]; - [bez moveToPoint: NSMakePoint (0, 0)]; - [bez appendBezierPathWithGlyph: [layoutManager glyphAtIndex: glyphIndex] - inFont: font]; - - for (int i = 0; i < [bez elementCount]; ++i) - { - NSPoint p[3]; - switch ([bez elementAtIndex: i associatedPoints: p]) - { - case NSMoveToBezierPathElement: - path->startNewSubPath (p[0].x, -p[0].y); - break; - case NSLineToBezierPathElement: - path->lineTo (p[0].x, -p[0].y); - break; - case NSCurveToBezierPathElement: - path->cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); - break; - case NSClosePathBezierPathElement: - path->closeSubPath(); - break; - default: - jassertfalse - break; - } - } - - if (needsItalicTransform) - path->applyTransform (AffineTransform::identity.sheared (-0.15, 0)); - } - - return kerning != 0; - } - - juce_wchar getDefaultChar() - { - return 0; - } -}; - -class FontHelperCache : public Timer, - public DeletedAtShutdown -{ - VoidArray cache; - -public: - FontHelperCache() - { - } - - ~FontHelperCache() - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - delete f; - } - - clearSingletonInstance(); - } - - FontHelper* getFont (const String& name, - const bool bold, - const bool italic, - const float size = 1024) - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - - if (f->name == name - && f->isBold == bold - && f->isItalic == italic - && f->fontSize == size) - { - f->refCount++; - return f; - } - } - - FontHelper* const f = new FontHelper (name, bold, italic, size); - cache.add (f); - return f; - } - - void releaseFont (FontHelper* f) - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f2 = (FontHelper*) cache.getUnchecked(i); - - if (f == f2) - { - f->refCount--; - - if (f->refCount == 0) - startTimer (5000); - - break; - } - } - } - - void timerCallback() - { - stopTimer(); - - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - - if (f->refCount == 0) - { - cache.remove (i); - delete f; - } - } - - if (cache.size() == 0) - delete this; - } - - juce_DeclareSingleton_SingleThreaded_Minimal (FontHelperCache) -}; - -juce_ImplementSingleton_SingleThreaded (FontHelperCache) - -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, - bool italic, - bool addAllGlyphsToFont) throw() -{ - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (fontName, bold, italic); - - clear(); - setAscent (helper->ascent / helper->totalSize); - setName (fontName); - setDefaultCharacter (helper->getDefaultChar()); - setBold (bold); - setItalic (italic); - - if (addAllGlyphsToFont) - { - //xxx - jassertfalse - } - - FontHelperCache::getInstance()->releaseFont (helper); -} - -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() -{ - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - if (character == 0) - return false; - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (getName(), isBold(), isItalic()); - - Path path; - float width; - bool foundOne = false; - - if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0)) - { - path.applyTransform (AffineTransform::scale (1.0f / helper->totalSize, - 1.0f / helper->totalSize)); - - addGlyph (character, path, width / helper->totalSize); - - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - float kerning; - if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0)) - { - kerning = (kerning - width) / helper->totalSize; - - if (kerning != 0) - addKerningPair (character, g->getCharacter(), kerning); - } - - if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0)) - { - kerning = kerning / helper->totalSize - g->width; - - if (kerning != 0) - addKerningPair (g->getCharacter(), character, kerning); - } - } - - foundOne = true; - } - - FontHelperCache::getInstance()->releaseFont (helper); - return foundOne; -} - -const StringArray Font::findAllTypefaceNames() throw() -{ - StringArray names; - - const ScopedAutoReleasePool pool; - NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies]; - - for (unsigned int i = 0; i < [fonts count]; ++i) - names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); - - names.sort (true); - return names; -} - -void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() -{ - defaultSans = "Lucida Grande"; - defaultSerif = "Times New Roman"; - defaultFixed = "Monaco"; -} - -#endif -/********* End of inlined file: juce_mac_Fonts.mm *********/ - /********* Start of inlined file: juce_mac_MessageManager.mm *********/ // (This file gets included by juce_mac_NativeCode.mm, rather than being // compiled on its own). diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 90122c509e..943d177232 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -17428,293 +17428,197 @@ private: /********* End of inlined file: juce_Path.h *********/ class Font; -class Typeface; +class CustomTypefaceGlyphInfo; -/** - Stores information about the shape and kerning of one of the glyphs in a Typeface. +/** A typeface represents a size-independent font. - @see Typeface, PositionedGlyph, GlyphArrangement -*/ -class JUCE_API TypefaceGlyphInfo -{ -public: + This base class is abstract, but calling createSystemTypefaceFor() will return + a platform-specific subclass that can be used. - /** Returns the path that describes the glyph's outline. + The CustomTypeface subclass allow you to build your own typeface, and to + load and save it in the Juce typeface format. - This is normalised to a height of 1.0, and its origin is the - left-hand edge of the glyph's baseline. - */ - const Path& getPath() const throw() { return path; } - - /** Returns the unicode character that this glyph represents. */ - juce_wchar getCharacter() const throw() { return character; } - - bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (character); } - - /** Returns the distance to leave between this and a following character. - - The value returned is expressed as a proportion of the font's height. - */ - float getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw(); - - /** Returns the typeface that this glyph belongs to. */ - Typeface* getTypeface() const throw() { return typeface; } - -private: - - friend class Typeface; - - struct KerningPair - { - juce_wchar character2; - float kerningAmount; - }; - - const juce_wchar character; - const Path path; - float width; - MemoryBlock kerningPairs; - Typeface* const typeface; - - TypefaceGlyphInfo (const juce_wchar character, - const Path& shape, - const float horizontalSeparation, - Typeface* const typeface) throw(); - ~TypefaceGlyphInfo() throw(); - - KerningPair& getKerningPair (const int index) const throw(); - int getNumKerningPairs() const throw(); - - void addKerningPair (const juce_wchar subsequentCharacter, - const float extraKerningAmount) throw(); - - const TypefaceGlyphInfo& operator= (const TypefaceGlyphInfo&); -}; - -/** - Represents a size-independent system font. - - A Font object represents a particular Typeface along with a specific size, - style, kerning, scale, etc, wheras the Typeface is just a generalised description - of the shapes of the glyphs and their properties. + Normally you should never need to deal directly with Typeface objects - the Font + class does everything you typically need for rendering text. + @see CustomTypeface, Font */ class JUCE_API Typeface : public ReferenceCountedObject { public: - /** Tries to load a named system font and to initialise all the glyphs - appropriately from it. + /** A handy typedef for a pointer to a typeface. */ + typedef ReferenceCountedObjectPtr Ptr; - @param faceName the name of the typeface, e.g. "Times" - @param bold whether to try to find a bold version of the font (may not always be available) - @param italic whether to try to find an italicised version of the font (may not always be available) + /** Returns the name of the typeface. + @see Font::getTypefaceName */ - Typeface (const String& faceName, - const bool bold, - const bool italic); + const String getName() const throw() { return name; } - /** Creates a copy of another typeface */ - Typeface (const Typeface& other); + /** Creates a new system typeface. */ + static const Ptr createSystemTypefaceFor (const Font& font); /** Destructor. */ - ~Typeface(); + virtual ~Typeface(); - /** Copies another typeface over this one. */ - const Typeface& operator= (const Typeface& other) throw(); - - /** Returns a unique ID for the typeface. - - This is based on the name and style, so can be used to compare two Typeface objects. + /** Returns the ascent of the font, as a proportion of its height. + The height is considered to always be normalised as 1.0, so this will be a + value less that 1.0, indicating the proportion of the font that lies above + its baseline. */ - int hashCode() const throw() { return hash; } + virtual float getAscent() const = 0; - /** Returns the name of the typeface, e.g. "Times", "Verdana", etc */ - const String& getName() const throw() { return typefaceName; } - - /** Returns the font's ascent as a proportion of its height. */ - float getAscent() const throw() { return ascent; } - - /** Returns true if the font is flagged as being bold. */ - bool isBold() const throw() { return bold; } - - /** Returns true if the typeface's 'italic' flag is set. */ - bool isItalic() const throw() { return italic; } - - /** Finds the Path that describes the outline shape of a character. - - The height of the path is normalised to 1.0 (i.e. a distance of 1.0 is the - height of the font). - - This may return 0 if the typeface has no characters, but if the character - that is asked for is not found, it will first try to return a default - character instead. + /** Returns the descent of the font, as a proportion of its height. + The height is considered to always be normalised as 1.0, so this will be a + value less that 1.0, indicating the proportion of the font that lies below + its baseline. */ - const Path* getOutlineForGlyph (const juce_wchar character) throw(); + virtual float getDescent() const = 0; - /** Tries to find the information describing a glyph for this character. + /** Measures the width of a line of text. - If there isn't a glyph specifically for the character it will return - a default glyph instead; if the typeface is empty, it may return a null - pointer. + The distance returned is based on the font having an normalised height of 1.0. + + You should never need to call this directly! Use Font::getStringWidth() instead! */ - const TypefaceGlyphInfo* getGlyph (const juce_wchar character) throw(); + virtual float getStringWidth (const String& text) = 0; - /** Deletes all the glyphs and kerning data fom the typeface. */ - void clear() throw(); + /** Converts a line of text into its glyph numbers and their positions. - /** Adds a glyph to the typeface. + The distances returned are based on the font having an normalised height of 1.0. - This is typically only called by the platform-specific code that generates - the typeface from a system font. + You should never need to call this directly! Use Font::getGlyphPositions() instead! */ - void addGlyph (const juce_wchar character, - const Path& path, - const float horizontalSpacing) throw(); + virtual void getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets) = 0; - /** Adds a kerning distance to the typeface. + /** Returns the outline for a glyph. - The extra amount passed in is expressed as a proportion of the font's - height, normalised to 1.0. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. + The path returned will be normalised to a font height of 1.0. */ - void addKerningPair (const juce_wchar firstChar, - const juce_wchar secondChar, - const float extraAmount) throw(); - - /** Sets the typeface's name. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. Calling this method won't actually affect - the underlying font being used. - */ - void setName (const String& name) throw(); - - /** Sets the font's ascent value, as a proportion of the font height. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void setAscent (const float newAscent) throw(); - - /** Sets the typeface's 'bold' flag. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void setBold (const bool shouldBeBold) throw(); - - /** Sets the typeface's 'italic' flag. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void setItalic (const bool shouldBeItalic) throw(); - - /** Changes the character index to use as the default character. - - This is the character that gets returned for characters which don't have a - glyph set for them. - */ - void setDefaultCharacter (const juce_wchar newDefaultCharacter) throw(); - - /** Creates a typeface from data created using Typeface::serialise(). - - This will attempt to load a compressed typeface that was created using - the Typeface::serialise() method. This is handy if you want to store - a typeface in your application as a binary blob, and use it without - having to actually install it on the computer. - - @see Typeface::serialise() - */ - Typeface (InputStream& serialisedTypefaceStream); - - /** Writes the typeface to a stream (using a proprietary format). - - This lets you save a typeface and reload it using the - Typeface::Typeface (InputStream&) constructor. The data's saved in - a compressed format. - - @see Typeface::Typeface (InputStream&) - */ - void serialise (OutputStream& outputStream); - - /** A name that represents the default sans-serif typeface name. - - Note that this is NOT the platform-specific typeface name (e.g. "Times"), but - is a generic string that represents whatever that font is, such as "DefaultSans". - - If you try to create a typeface with this name, the global default LookAndFeel - object will be asked to provide an appropriate typeface. - */ - static const tchar* defaultTypefaceNameSans; - - /** A name that represents the default serif typeface name. - - Note that this is NOT the platform-specific typeface name (e.g. "Times"), but - is a generic string that represents it, such as "DefaultSans". - - If you try to create a typeface with this name, the global default LookAndFeel - object will be asked to provide an appropriate typeface. - */ - static const tchar* defaultTypefaceNameSerif; - - /** A name that represents the default monospaced typeface name. - - Note that this is NOT the platform-specific typeface name (e.g. "Times"), but - is a generic string that represents it, such as "DefaultSans". - - If you try to create a typeface with this name, the global default LookAndFeel - object will be asked to provide an appropriate typeface. - */ - static const tchar* defaultTypefaceNameMono; - - /** A handy typedef to make it easy to use ref counted pointers to this class. */ - typedef ReferenceCountedObjectPtr Ptr; + virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; juce_UseDebuggingNewOperator +protected: + String name; + + Typeface (const String& name) throw(); + private: - VoidArray glyphs; + Typeface (const Typeface&); + const Typeface& operator= (const Typeface&); +}; + +/** A typeface that can be populated with custom glyphs. + + You can create a CustomTypeface if you need one that contains your own glyphs, + or if you need to load a typeface from a Juce-formatted binary stream. + + If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface() + to copy glyphs into this face. + + @see Typeface, Font +*/ +class JUCE_API CustomTypeface : public Typeface +{ +public: + + /** Creates a new, empty typeface. */ + CustomTypeface(); + + /** Loads a typeface from a previously saved stream. + The stream must have been created by writeToStream(). + @see writeToStream + */ + CustomTypeface (InputStream& serialisedTypefaceStream); + + /** Destructor. */ + ~CustomTypeface(); + + /** Resets this typeface, deleting all its glyphs and settings. */ + void clear(); + + /** Sets the vital statistics for the typeface. + @param name the typeface's name + @param ascent the ascent - this is normalised to a height of 1.0 and this is + the value that will be returned by Typeface::getAscent(). The + descent is assumed to be (1.0 - ascent) + @param isBold should be true if the typeface is bold + @param isItalic should be true if the typeface is italic + @param defaultCharacter the character to be used as a replacement if there's + no glyph available for the character that's being drawn + */ + void setCharacteristics (const String& name, const float ascent, + const bool isBold, const bool isItalic, + const juce_wchar defaultCharacter) throw(); + + /** Adds a glyph to the typeface. + + The path that is passed in is normalised so that the font height is 1.0, and its + origin is the anchor point of the character on its baseline. + + The width is the nominal width of the character, and any extra kerning values that + are specified will be added to this width. + */ + void addGlyph (const juce_wchar character, const Path& path, const float width) throw(); + + /** Specifies an extra kerning amount to be used between a pair of characters. + The amount will be added to the nominal width of the first character when laying out a string. + */ + void addKerningPair (const juce_wchar char1, const juce_wchar char2, const float extraAmount) throw(); + + /** Adds a range of glyphs from another typeface. + This will attempt to pull in the paths and kerning information from another typeface and + add it to this one. + */ + void addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) throw(); + + /** Saves this typeface as a Juce-formatted font file. + A CustomTypeface can be created to reload the data that is written - see the CustomTypeface + constructor. + */ + bool writeToStream (OutputStream& outputStream); + + // The following methods implement the basic Typeface behaviour. + float getAscent() const; + float getDescent() const; + float getStringWidth (const String& text); + void getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets); + bool getOutlineForGlyph (int glyphNumber, Path& path); + int getGlyphForCharacter (juce_wchar character); + + juce_UseDebuggingNewOperator + +protected: + juce_wchar defaultCharacter; + float ascent; + bool isBold, isItalic; + + /** If a subclass overrides this, it can load glyphs into the font on-demand. + When methods such as getGlyphPositions() or getOutlineForGlyph() are asked for a + particular character and there's no corresponding glyph, they'll call this + method so that a subclass can try to add that glyph, returning true if it + manages to do so. + */ + virtual bool loadGlyphIfPossible (const juce_wchar characterNeeded); + +private: + + OwnedArray glyphs; short lookupTable [128]; - String typefaceName; - int hash; - float ascent; // as a proportion of the height - bool bold, italic, isFullyPopulated; - juce_wchar defaultCharacter; // the char to use if a matching glyph can't be found. + CustomTypeface (const CustomTypeface&); + const CustomTypeface& operator= (const CustomTypeface&); - Typeface() throw(); - void addGlyphCopy (const TypefaceGlyphInfo* const glyphInfoToCopy) throw(); - - friend class Font; - friend class TypefaceCache; - friend class FontGlyphAlphaMap; - - static const Ptr getTypefaceFor (const Font& font) throw(); - - // this is a platform-dependent method that will look for the given typeface - // and set up its kerning tables, etc. accordingly. - // If addAllGlyphsToFont is true, it should also add all the glyphs in the font - // to the typeface immediately, rather than having to add them later on-demand. - void initialiseTypefaceCharacteristics (const String& fontName, - bool bold, bool italic, - bool addAllGlyphsToFont) throw(); - - // platform-specific routine to look up and add a glyph to this typeface - bool findAndAddSystemGlyph (juce_wchar character) throw(); - - void updateHashCode() throw(); - - friend class LookAndFeel; - static void getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw(); + CustomTypefaceGlyphInfo* findGlyph (const juce_wchar character, const bool loadIfNeeded) throw(); + CustomTypefaceGlyphInfo* findGlyphSubstituting (const juce_wchar character) throw(); }; #endif // __JUCE_TYPEFACE_JUCEHEADER__ /********* End of inlined file: juce_Typeface.h *********/ +class LowLevelGraphicsContext; + /** Represents a particular font, including its size, style, etc. @@ -17766,12 +17670,8 @@ public: /** Creates a copy of another Font object. */ Font (const Font& other) throw(); - /** Creates a font based on a typeface. - - The font object stores its own internal copy of the typeface, so you can safely - delete the one passed in after calling this. - */ - Font (const Typeface& typeface) throw(); + /** Creates a font for a typeface. */ + Font (const Typeface::Ptr& typeface) throw(); /** Creates a basic sans-serif font at a default height. @@ -17794,9 +17694,11 @@ public: e.g. "Arial", "Courier", etc. - This may also be set to Typeface::defaultTypefaceNameSans, Typeface::defaultTypefaceNameSerif, - or Typeface::defaultTypefaceNameMono, which are not actual platform-specific font names, but - are generic names that are used to represent the various default fonts. + This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, + but are generic names that are used to represent the various default fonts. + If you need to know the exact typeface name being used, you can call + Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. If a suitable font isn't found on the machine, it'll just use a default instead. */ @@ -17806,43 +17708,50 @@ public: e.g. "Arial", "Courier", etc. - Note that this may also be one of the values: Typeface::defaultTypefaceNameSans, - Typeface::defaultTypefaceNameSerif, or Typeface::defaultTypefaceNameMono, which are not actual - platform-specific font names, but are generic names that are used to represent the various - default fonts. If you need to know the exact typeface name being used, you can call + This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, + but are generic names that are used to represent the various default fonts. + + If you need to know the exact typeface name being used, you can call Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. */ - const String& getTypefaceName() const throw() { return typefaceName; } + const String& getTypefaceName() const throw() { return font->typefaceName; } /** Returns a typeface name that represents the default sans-serif font. This is also the typeface that will be used when a font is created without specifying any typeface details. - Note that this method just returns the same value as Typeface::defaultTypefaceNameSans, - which is a generic placeholder string, and not a platform-specific font name. + Note that this method just returns a generic placeholder string that means "the default + sans-serif font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). - @see Typeface::defaultTypefaceNameSans, setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName, + @see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName */ - static const String getDefaultSansSerifFontName() throw() { return Typeface::defaultTypefaceNameSans; } + static const String getDefaultSansSerifFontName() throw(); /** Returns a typeface name that represents the default sans-serif font. - Note that this method just returns the same value as Typeface::defaultTypefaceNameSerif, - which is a generic placeholder string, and not a platform-specific font name. + Note that this method just returns a generic placeholder string that means "the default + serif font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). - @see Typeface::defaultTypefaceNameSerif, setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName */ - static const String getDefaultSerifFontName() throw() { return Typeface::defaultTypefaceNameSerif; } + static const String getDefaultSerifFontName() throw(); /** Returns a typeface name that represents the default sans-serif font. - Note that this method just returns the same value as Typeface::defaultTypefaceNameMono, - which is a generic placeholder string, and not a platform-specific font name. + Note that this method just returns a generic placeholder string that means "the default + monospaced font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). - @see Typeface::defaultTypefaceNameMono, setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName */ - static const String getDefaultMonospacedFontName() throw() { return Typeface::defaultTypefaceNameMono; } + static const String getDefaultMonospacedFontName() throw(); + + /** Returns the typeface names of the default fonts on the current platform. */ + static void getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw(); /** Returns the total height of this font. @@ -17851,7 +17760,7 @@ public: @see setHeight, setHeightWithoutChangingWidth, getAscent */ - float getHeight() const throw() { return height; } + float getHeight() const throw() { return font->height; } /** Changes the font's height. @@ -17888,7 +17797,7 @@ public: @see FontStyleFlags */ - int getStyleFlags() const throw() { return styleFlags; } + int getStyleFlags() const throw() { return font->styleFlags; } /** Changes the font's style. @@ -17927,7 +17836,7 @@ public: @see setHorizontalScale */ - float getHorizontalScale() const throw() { return horizontalScale; } + float getHorizontalScale() const throw() { return font->horizontalScale; } /** Changes the font's kerning. @@ -17946,22 +17855,14 @@ public: A value of zero is normal spacing, positive values will spread the letters out more, and negative values make them closer together. */ - float getExtraKerningFactor() const throw() { return kerning; } + float getExtraKerningFactor() const throw() { return font->kerning; } /** Changes all the font's characteristics with one call. */ - void setSizeAndStyle (const float newHeight, + void setSizeAndStyle (float newHeight, const int newStyleFlags, const float newHorizontalScale, const float newKerningAmount) throw(); - /** Resets this font's characteristics. - - This is basically like saying "myFont = Font();", because it resets the - typeface, size, style, etc to a default state. Not very useful to most - people, its raison d'etre is to help the Graphics class be more efficient. - */ - void resetToDefaultState() throw(); - /** Returns the total width of a string as it would be drawn using this font. For a more accurate floating-point result, use getStringWidthFloat(). @@ -17974,6 +17875,30 @@ public: */ float getStringWidthFloat (const String& text) const throw(); + /** Returns the series of glyph numbers and their x offsets needed to represent a string. + + An extra x offset is added at the end of the run, to indicate where the right hand + edge of the last character is. + */ + void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) const throw(); + + /** Renders a glyph in a context without using methods other than the context's glyph-rendering + methods. + + For smaller fonts, this uses an internal cache of glyph images to speed things up, and renders + them using the context's image blending methods. For larger fonts, it gets the glyph's path + from the typeface and renders it as a shape. + + This method is primarily called by graphics contexts as a way of drawing a glyph if they can't do + it by native means. + */ + void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y); + + /** Renders a transformed glyph using path-filling techniques rather than calling a context's + actual glyph-rendering methods. + */ + void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform); + /** Returns the typeface used by this font. Note that the object returned may go out of scope if this font is deleted @@ -18016,11 +17941,22 @@ private: friend class FontGlyphAlphaMap; friend class TypefaceCache; - String typefaceName; - float height, horizontalScale, kerning; - mutable float ascent; - int styleFlags; - mutable Typeface::Ptr typeface; + class SharedFontInternal : public ReferenceCountedObject + { + public: + SharedFontInternal (const String& typefaceName, const float height, const float horizontalScale, + const float kerning, const float ascent, const int styleFlags, + Typeface* const typeface) throw(); + SharedFontInternal (const SharedFontInternal& other) throw(); + + String typefaceName; + float height, horizontalScale, kerning, ascent; + int styleFlags; + Typeface::Ptr typeface; + }; + + ReferenceCountedObjectPtr font; + void dupeInternalIfShared() throw(); }; #endif // __JUCE_FONT_JUCEHEADER__ @@ -19832,6 +19768,8 @@ public: If a solid colour is being used for drawing, this changes its opacity to this new value (i.e. it doesn't multiply the colour's opacity by this amount). + If a gradient is being used, this will have no effect on it. + A value of 0.0 is completely transparent, 1.0 is completely opaque. */ void setOpacity (const float newOpacity) throw(); @@ -20439,10 +20377,8 @@ private: GraphicsState (const GraphicsState&) throw(); ~GraphicsState() throw(); - Colour colour; Brush* brush; Font font; - ResamplingQuality quality; }; GraphicsState* state; @@ -39370,9 +39306,9 @@ class JUCE_API PositionedGlyph public: /** Returns the character the glyph represents. */ - juce_wchar getCharacter() const throw() { return glyphInfo->getCharacter(); } + juce_wchar getCharacter() const throw() { return character; } /** Checks whether the glyph is actually empty. */ - bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (glyphInfo->getCharacter()); } + bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (character); } /** Returns the position of the glyph's left-hand edge. */ float getLeft() const throw() { return x; } @@ -39381,9 +39317,9 @@ public: /** Returns the y position of the glyph's baseline. */ float getBaselineY() const throw() { return y; } /** Returns the y position of the top of the glyph. */ - float getTop() const throw() { return y - fontAscent; } + float getTop() const throw() { return y - font.getAscent(); } /** Returns the y position of the bottom of the glyph. */ - float getBottom() const throw() { return y + fontHeight - fontAscent; } + float getBottom() const throw() { return y + font.getDescent(); } /** Shifts the glyph's position by a relative amount. */ void moveBy (const float deltaX, @@ -39410,9 +39346,9 @@ private: friend class GlyphArrangement; float x, y, w; - float fontHeight, fontAscent, fontHorizontalScale; - bool isUnderlined; - const TypefaceGlyphInfo* glyphInfo; + Font font; + juce_wchar character; + int glyph; PositionedGlyph() throw(); }; @@ -39446,7 +39382,7 @@ public: ~GlyphArrangement() throw(); /** Returns the total number of glyphs in the arrangement. */ - int getNumGlyphs() const throw() { return numGlyphs; } + int getNumGlyphs() const throw() { return glyphs.size(); } /** Returns one of the glyphs from the arrangement. @@ -39625,16 +39561,9 @@ public: juce_UseDebuggingNewOperator private: - int numGlyphs, numAllocated; - PositionedGlyph* glyphs; + OwnedArray glyphs; - void ensureNumGlyphsAllocated (int minGlyphs) throw(); - void removeLast() throw(); void appendEllipsis (const Font& font, const float maxXPixels) throw(); - - void incGlyphRefCount (const int index) const throw(); - void decGlyphRefCount (const int index) const throw(); - void spreadOutLine (const int start, const int numGlyphs, const float targetWidth) throw(); }; @@ -39975,32 +39904,35 @@ public: virtual const Rectangle getClipBounds() const = 0; virtual bool isClipEmpty() const = 0; - virtual void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) = 0; - virtual void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) = 0; + virtual void setColour (const Colour& colour) = 0; + virtual void setGradient (const ColourGradient& gradient) = 0; + virtual void setOpacity (float opacity) = 0; + virtual void setInterpolationQuality (Graphics::ResamplingQuality quality) = 0; + + virtual void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) = 0; + virtual void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality) = 0; - virtual void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality) = 0; - virtual void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) = 0; virtual void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality) = 0; + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality) = 0; - virtual void fillAlphaChannelWithColour (const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour) = 0; - virtual void fillAlphaChannelWithGradient (const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient) = 0; + virtual void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) = 0; virtual void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha) = 0; + const Image& fillerImage, int fillerImageX, int fillerImageY) = 0; virtual void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY, - float alpha) = 0; + int destX, int destY, int destW, int destH, int sourceX, int sourceY) = 0; virtual void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality) = 0; + const AffineTransform& transform) = 0; - virtual void drawLine (double x1, double y1, double x2, double y2, const Colour& colour) = 0; + virtual void drawLine (double x1, double y1, double x2, double y2) = 0; + virtual void drawVerticalLine (const int x, double top, double bottom) = 0; + virtual void drawHorizontalLine (const int y, double left, double right) = 0; - virtual void drawVerticalLine (const int x, double top, double bottom, const Colour& col) = 0; - virtual void drawHorizontalLine (const int y, double left, double right, const Colour& col) = 0; + virtual void setFont (const Font& newFont) = 0; + virtual void drawGlyph (int glyphNumber, float x, float y) = 0; + virtual void drawGlyph (int glyphNumber, const AffineTransform& transform) = 0; }; #endif // __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ @@ -40042,30 +39974,35 @@ public: const Rectangle getClipBounds() const; bool isClipEmpty() const; - void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient); + void setColour (const Colour& colour); + void setGradient (const ColourGradient& gradient); + void setOpacity (float opacity); + void setInterpolationQuality (Graphics::ResamplingQuality quality); + + void fillRect (int x, int y, int w, int h, const bool replaceExistingContents); + void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality); - void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); - void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality); - void fillAlphaChannelWithColour (const Image& alphaImage, int imageX, int imageY, const Colour& colour); - void fillAlphaChannelWithGradient (const Image& alphaImage, int imageX, int imageY, const ColourGradient& gradient); + void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY); void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + const Image& fillerImage, int fillerImageX, int fillerImageY); void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, - int sourceX, int sourceY, float alpha); + int sourceX, int sourceY); void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality); + const AffineTransform& transform); - void drawLine (double x1, double y1, double x2, double y2, const Colour& colour); + void drawLine (double x1, double y1, double x2, double y2); - void drawVerticalLine (const int x, double top, double bottom, const Colour& col); - void drawHorizontalLine (const int x, double top, double bottom, const Colour& col); + void drawVerticalLine (const int x, double top, double bottom); + void drawHorizontalLine (const int x, double top, double bottom); + + void setFont (const Font& newFont); + void drawGlyph (int glyphNumber, float x, float y); + void drawGlyph (int glyphNumber, const AffineTransform& transform); RectangleList* getRawClipRegion() throw() { return clip; } @@ -40076,14 +40013,24 @@ protected: Image& image; RectangleList* clip; int xOffset, yOffset; + Font font; + Colour colour; + ColourGradient* gradient; + Graphics::ResamplingQuality interpolationQuality; struct SavedState { - SavedState (RectangleList* const clip, const int xOffset, const int yOffset); + SavedState (RectangleList* const clip, const int xOffset, const int yOffset, + const Font& font, const Colour& colour, ColourGradient* gradient, + Graphics::ResamplingQuality interpolationQuality); ~SavedState(); RectangleList* clip; const int xOffset, yOffset; + Font font; + Colour colour; + ColourGradient* gradient; + Graphics::ResamplingQuality interpolationQuality; private: SavedState (const SavedState&); @@ -40092,8 +40039,8 @@ protected: OwnedArray stateStack; - void drawVertical (const int x, const double top, const double bottom, const Colour& col); - void drawHorizontal (const int y, const double top, const double bottom, const Colour& col); + void drawVertical (const int x, const double top, const double bottom); + void drawHorizontal (const int y, const double top, const double bottom); bool getPathBounds (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, @@ -40101,29 +40048,25 @@ protected: void clippedFillRectWithColour (const Rectangle& clipRect, int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - void clippedFillPathWithColour (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); - void clippedFillPathWithGradient (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); + void clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality); void clippedFillPathWithImage (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); - void clippedFillAlphaChannelWithColour (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour); - void clippedFillAlphaChannelWithGradient (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient); + void clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY); void clippedFillAlphaChannelWithImage (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + const Image& fillerImage, int fillerImageX, int fillerImageY, const float opacity); void clippedBlendImage (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY, - float alpha); + int destX, int destY, int destW, int destH, int sourceX, int sourceY); void clippedBlendImageWarping (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality); + const AffineTransform& transform); - void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2, const Colour& colour); + void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2); - void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom, const Colour& col); - void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom, const Colour& col); + void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); + void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); LowLevelGraphicsSoftwareRenderer (const LowLevelGraphicsSoftwareRenderer& other); const LowLevelGraphicsSoftwareRenderer& operator= (const LowLevelGraphicsSoftwareRenderer&); @@ -40175,30 +40118,35 @@ public: const Rectangle getClipBounds() const; bool isClipEmpty() const; - void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient); + void setColour (const Colour& colour); + void setGradient (const ColourGradient& gradient); + void setOpacity (float opacity); + void setInterpolationQuality (Graphics::ResamplingQuality quality); + + void fillRect (int x, int y, int w, int h, const bool replaceExistingContents); + void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality); - void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); - void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality); - void fillAlphaChannelWithColour (const Image& alphaImage, int imageX, int imageY, const Colour& colour); - void fillAlphaChannelWithGradient (const Image& alphaImage, int imageX, int imageY, const ColourGradient& gradient); + void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY); void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + const Image& fillerImage, int fillerImageX, int fillerImageY); void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, - int sourceX, int sourceY, float alpha); + int sourceX, int sourceY); void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality); + const AffineTransform& transform); - void drawLine (double x1, double y1, double x2, double y2, const Colour& colour); + void drawLine (double x1, double y1, double x2, double y2); - void drawVerticalLine (const int x, double top, double bottom, const Colour& col); - void drawHorizontalLine (const int x, double top, double bottom, const Colour& col); + void drawVerticalLine (const int x, double top, double bottom); + void drawHorizontalLine (const int x, double top, double bottom); + + void setFont (const Font& newFont); + void drawGlyph (int glyphNumber, float x, float y); + void drawGlyph (int glyphNumber, const AffineTransform& transform); juce_UseDebuggingNewOperator @@ -40208,15 +40156,21 @@ protected: RectangleList* clip; int totalWidth, totalHeight, xOffset, yOffset; bool needToClip; - Colour lastColour; + Colour lastColour, colour; + ColourGradient* gradient; + Font font; struct SavedState { - SavedState (RectangleList* const clip, const int xOffset, const int yOffset); + SavedState (RectangleList* const clip, const int xOffset, const int yOffset, + const Colour& colour, ColourGradient* const gradient, const Font& font); ~SavedState(); RectangleList* clip; const int xOffset, yOffset; + Colour colour; + ColourGradient* gradient; + Font font; private: SavedState (const SavedState&); @@ -45020,6 +44974,9 @@ public: enabled. */ }; + /** Changes the size of the scrollbars. */ + void setScrollbarThickness (const int thickness) throw(); + /** @internal */ void resized(); /** @internal */ @@ -51058,7 +51015,8 @@ public: /** Destructor. */ ~FileBrowserComponent(); - /** + /** Returns the file that the user has currently chosen. + @see getHighlightedFile */ const File getCurrentFile() const throw(); @@ -51071,6 +51029,13 @@ public: */ bool currentFileIsValid() const; + /** This returns the item in the view that is currently highlighted. + This may be different from getCurrentFile(), which returns the value + that is shown in the filename box. + @see getCurrentFile + */ + const File getHighlightedFile() const throw(); + /** Returns the directory whose contents are currently being shown in the listbox. */ const File getRoot() const; diff --git a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp index 5008382d9b..1449791742 100644 --- a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp +++ b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp @@ -225,7 +225,7 @@ LookAndFeel::LookAndFeel() setColour (standardColours [i], Colour (standardColours [i + 1])); if (defaultSansName.isEmpty()) - Typeface::getDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName); + Font::getPlatformDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName); defaultSans = defaultSansName; defaultSerif = defaultSerifName; @@ -313,14 +313,16 @@ const Typeface::Ptr LookAndFeel::getTypefaceForFont (const Font& font) { String faceName (font.getTypefaceName()); - if (faceName == Typeface::defaultTypefaceNameSans) + if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultSans; - else if (faceName == Typeface::defaultTypefaceNameSerif) + else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultSerif; - else if (faceName == Typeface::defaultTypefaceNameMono) + else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultFixed; - return new Typeface (faceName, font.isBold(), font.isItalic()); + Font f (font); + f.setTypefaceName (faceName); + return Typeface::createSystemTypefaceFor (f); } void LookAndFeel::setDefaultSansSerifTypefaceName (const String& newName) diff --git a/src/gui/graphics/brushes/juce_GradientBrush.cpp b/src/gui/graphics/brushes/juce_GradientBrush.cpp index b9a003fa0f..e9ccc0cfa3 100644 --- a/src/gui/graphics/brushes/juce_GradientBrush.cpp +++ b/src/gui/graphics/brushes/juce_GradientBrush.cpp @@ -79,13 +79,15 @@ bool GradientBrush::isInvisible() const throw() void GradientBrush::paintPath (LowLevelGraphicsContext& context, const Path& path, const AffineTransform& transform) throw() { - context.fillPathWithGradient (path, transform, gradient, EdgeTable::Oversampling_4times); + context.setGradient (gradient); + context.fillPath (path, transform, EdgeTable::Oversampling_4times); } void GradientBrush::paintRectangle (LowLevelGraphicsContext& context, int x, int y, int w, int h) throw() { - context.fillRectWithGradient (x, y, w, h, gradient); + context.setGradient (gradient); + context.fillRect (x, y, w, h, false); } void GradientBrush::paintAlphaChannel (LowLevelGraphicsContext& context, @@ -95,7 +97,10 @@ void GradientBrush::paintAlphaChannel (LowLevelGraphicsContext& context, context.saveState(); if (context.reduceClipRegion (x, y, w, h)) - context.fillAlphaChannelWithGradient (alphaChannelImage, imageX, imageY, gradient); + { + context.setGradient (gradient); + context.fillAlphaChannel (alphaChannelImage, imageX, imageY); + } context.restoreState(); } diff --git a/src/gui/graphics/brushes/juce_ImageBrush.cpp b/src/gui/graphics/brushes/juce_ImageBrush.cpp index 349939e3bb..be87d05b83 100644 --- a/src/gui/graphics/brushes/juce_ImageBrush.cpp +++ b/src/gui/graphics/brushes/juce_ImageBrush.cpp @@ -124,7 +124,8 @@ void ImageBrush::paintRectangle (LowLevelGraphicsContext& context, while (x < right) { - context.blendImage (*image, x, y, iw, ih, 0, 0, opacity); + context.setOpacity (opacity); + context.blendImage (*image, x, y, iw, ih, 0, 0); x += iw; } @@ -141,6 +142,7 @@ void ImageBrush::paintPath (LowLevelGraphicsContext& context, if (image != 0) { Rectangle clip (context.getClipBounds()); + context.setOpacity (opacity); { float x, y, w, h; @@ -169,7 +171,7 @@ void ImageBrush::paintPath (LowLevelGraphicsContext& context, while (x < right) { - context.fillPathWithImage (path, transform, *image, x, y, opacity, EdgeTable::Oversampling_4times); + context.fillPathWithImage (path, transform, *image, x, y, EdgeTable::Oversampling_4times); x += iw; } @@ -186,6 +188,7 @@ void ImageBrush::paintAlphaChannel (LowLevelGraphicsContext& context, if (image != 0 && context.reduceClipRegion (x, y, w, h)) { + context.setOpacity (opacity); const Rectangle clip (context.getClipBounds()); x = clip.getX(); y = clip.getY(); @@ -206,7 +209,7 @@ void ImageBrush::paintAlphaChannel (LowLevelGraphicsContext& context, { context.fillAlphaChannelWithImage (alphaChannelImage, imageX, imageY, *image, - x, y, opacity); + x, y); x += iw; } diff --git a/src/gui/graphics/brushes/juce_SolidColourBrush.cpp b/src/gui/graphics/brushes/juce_SolidColourBrush.cpp index 9eb809fa10..b68c6394e1 100644 --- a/src/gui/graphics/brushes/juce_SolidColourBrush.cpp +++ b/src/gui/graphics/brushes/juce_SolidColourBrush.cpp @@ -69,15 +69,15 @@ bool SolidColourBrush::isInvisible() const throw() void SolidColourBrush::paintPath (LowLevelGraphicsContext& context, const Path& path, const AffineTransform& transform) throw() { - if (! colour.isTransparent()) - context.fillPathWithColour (path, transform, colour, EdgeTable::Oversampling_4times); + context.setColour (colour); + context.fillPath (path, transform, EdgeTable::Oversampling_4times); } void SolidColourBrush::paintRectangle (LowLevelGraphicsContext& context, int x, int y, int w, int h) throw() { - if (! colour.isTransparent()) - context.fillRectWithColour (x, y, w, h, colour, false); + context.setColour (colour); + context.fillRect (x, y, w, h, false); } void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, @@ -89,7 +89,10 @@ void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, context.saveState(); if (context.reduceClipRegion (x, y, w, h)) - context.fillAlphaChannelWithColour (alphaChannelImage, imageX, imageY, colour); + { + context.setColour (colour); + context.fillAlphaChannel (alphaChannelImage, imageX, imageY); + } context.restoreState(); } @@ -98,19 +101,22 @@ void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, void SolidColourBrush::paintVerticalLine (LowLevelGraphicsContext& context, int x, float y1, float y2) throw() { - context.drawVerticalLine (x, y1, y2, colour); + context.setColour (colour); + context.drawVerticalLine (x, y1, y2); } void SolidColourBrush::paintHorizontalLine (LowLevelGraphicsContext& context, int y, float x1, float x2) throw() { - context.drawHorizontalLine (y, x1, x2, colour); + context.setColour (colour); + context.drawHorizontalLine (y, x1, x2); } void SolidColourBrush::paintLine (LowLevelGraphicsContext& context, float x1, float y1, float x2, float y2) throw() { - context.drawLine (x1, y1, x2, y2, colour); + context.setColour (colour); + context.drawLine (x1, y1, x2, y2); } diff --git a/src/gui/graphics/contexts/juce_Graphics.cpp b/src/gui/graphics/contexts/juce_Graphics.cpp index 05421fe610..890a9dede4 100644 --- a/src/gui/graphics/contexts/juce_Graphics.cpp +++ b/src/gui/graphics/contexts/juce_Graphics.cpp @@ -69,6 +69,7 @@ Graphics::Graphics (Image& imageToDrawOnto) throw() state (new GraphicsState()), saveStatePending (false) { + resetToDefaultState(); } Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() @@ -77,6 +78,7 @@ Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() state (new GraphicsState()), saveStatePending (false) { + resetToDefaultState(); } Graphics::~Graphics() throw() @@ -90,9 +92,10 @@ Graphics::~Graphics() throw() //============================================================================== void Graphics::resetToDefaultState() throw() { - setColour (Colours::black); - state->font.resetToDefaultState(); - state->quality = defaultQuality; + saveStateIfPending(); + context->setColour (Colours::black); + context->setFont (Font()); + context->setInterpolationQuality (defaultQuality); } bool Graphics::isVectorDevice() const throw() @@ -192,32 +195,52 @@ bool Graphics::clipRegionIntersects (const int x, const int y, void Graphics::setColour (const Colour& newColour) throw() { saveStateIfPending(); - state->colour = newColour; + deleteAndZero (state->brush); + context->setColour (newColour); } void Graphics::setOpacity (const float newOpacity) throw() { saveStateIfPending(); - state->colour = state->colour.withAlpha (newOpacity); + context->setOpacity (newOpacity); } void Graphics::setBrush (const Brush* const newBrush) throw() { saveStateIfPending(); delete state->brush; + state->brush = 0; if (newBrush != 0) - state->brush = newBrush->createCopy(); - else - state->brush = 0; + { + const SolidColourBrush* cb = dynamic_cast (newBrush); + + if (cb != 0) + { + setColour (cb->getColour()); + } + else + { + const GradientBrush* gb = dynamic_cast (newBrush); + + if (gb != 0) + { + setGradientFill (gb->getGradient()); + } + else + { + state->brush = newBrush->createCopy(); + } + } + } } void Graphics::setGradientFill (const ColourGradient& gradient) throw() { saveStateIfPending(); - delete state->brush; - state->brush = new GradientBrush (gradient); + deleteAndZero (state->brush); + context->setGradient (gradient); } void Graphics::setTiledImageFill (Image& imageToUse, @@ -232,17 +255,13 @@ void Graphics::setTiledImageFill (Image& imageToUse, //============================================================================== Graphics::GraphicsState::GraphicsState() throw() - : colour (Colours::black), - brush (0), - quality (defaultQuality) + : brush (0) { } Graphics::GraphicsState::GraphicsState (const GraphicsState& other) throw() - : colour (other.colour), - brush (other.brush != 0 ? other.brush->createCopy() : 0), - font (other.font), - quality (other.quality) + : brush (other.brush != 0 ? other.brush->createCopy() : 0), + font (other.font) { } @@ -256,13 +275,15 @@ void Graphics::setFont (const Font& newFont) throw() { saveStateIfPending(); state->font = newFont; + context->setFont (newFont); } void Graphics::setFont (const float newFontHeight, const int newFontStyleFlags) throw() { saveStateIfPending(); - state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0.0f); + state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0); + context->setFont (state->font); } //============================================================================== @@ -365,8 +386,10 @@ void Graphics::fillRect (int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, width, height); + if (state->brush == 0) + context->fillRect (x, y, width, height, false); + else + state->brush->paintRectangle (*context, x, y, width, height); } void Graphics::fillRect (const Rectangle& r) const throw() @@ -394,8 +417,10 @@ void Graphics::setPixel (int x, int y) const throw() { if (context->clipRegionIntersects (x, y, 1, 1)) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, 1, 1); + if (state->brush == 0) + context->fillRect (x, y, 1, 1, false); + else + state->brush->paintRectangle (*context, x, y, 1, 1); } } @@ -410,8 +435,10 @@ void Graphics::fillAll (const Colour& colourToUse) const throw() { const Rectangle clip (context->getClipBounds()); - context->fillRectWithColour (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(), - colourToUse, false); + context->saveState(); + context->setColour (colourToUse); + context->fillRect (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(), false); + context->restoreState(); } } @@ -422,8 +449,10 @@ void Graphics::fillPath (const Path& path, { if ((! context->isClipEmpty()) && ! path.isEmpty()) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintPath (*context, path, transform); + if (state->brush == 0) + context->fillPath (path, transform, EdgeTable::Oversampling_4times); + else + state->brush->paintPath (*context, path, transform); } } @@ -431,7 +460,7 @@ void Graphics::strokePath (const Path& path, const PathStrokeType& strokeType, const AffineTransform& transform) const throw() { - if ((! state->colour.isTransparent()) || state->brush != 0) +// if ((! state->colour.isTransparent()) || state->brush != 0) { Path stroke; strokeType.createStrokedPath (stroke, path, transform); @@ -449,13 +478,21 @@ void Graphics::drawRect (const int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - SolidColourBrush colourBrush (state->colour); - Brush& b = (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush); - - b.paintRectangle (*context, x, y, width, lineThickness); - b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness); + if (state->brush == 0) + { + context->fillRect (x, y, width, lineThickness, false); + context->fillRect (x, y + lineThickness, lineThickness, height - lineThickness * 2, false); + context->fillRect (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2, false); + context->fillRect (x, y + height - lineThickness, width, lineThickness, false); + } + else + { + Brush& b = *(state->brush); + b.paintRectangle (*context, x, y, width, lineThickness); + b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2); + b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2); + b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness); + } } void Graphics::drawRect (const float x, @@ -498,7 +535,9 @@ void Graphics::drawBevel (const int x, if (clipRegionIntersects (x, y, width, height)) { - const float oldOpacity = state->colour.getFloatAlpha(); + context->saveState(); + + const float oldOpacity = 1.0f;//xxx state->colour.getFloatAlpha(); const float ramp = oldOpacity / bevelThickness; for (int i = bevelThickness; --i >= 0;) @@ -506,11 +545,17 @@ void Graphics::drawBevel (const int x, const float op = useGradient ? ramp * (sharpEdgeOnOutside ? bevelThickness - i : i) : oldOpacity; - context->fillRectWithColour (x + i, y + i, width - i * 2, 1, topLeftColour.withMultipliedAlpha (op), false); - context->fillRectWithColour (x + i, y + i + 1, 1, height - i * 2 - 2, topLeftColour.withMultipliedAlpha (op * 0.75f), false); - context->fillRectWithColour (x + i, y + height - i - 1, width - i * 2, 1, bottomRightColour.withMultipliedAlpha (op), false); - context->fillRectWithColour (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, bottomRightColour.withMultipliedAlpha (op * 0.75f), false); + context->setColour (topLeftColour.withMultipliedAlpha (op)); + context->fillRect (x + i, y + i, width - i * 2, 1, false); + context->setColour (topLeftColour.withMultipliedAlpha (op * 0.75f)); + context->fillRect (x + i, y + i + 1, 1, height - i * 2 - 2, false); + context->setColour (bottomRightColour.withMultipliedAlpha (op)); + context->fillRect (x + i, y + height - i - 1, width - i * 2, 1, false); + context->setColour (bottomRightColour.withMultipliedAlpha (op * 0.75f)); + context->fillRect (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, false); } + + context->restoreState(); } } @@ -618,9 +663,12 @@ void Graphics::fillCheckerBoard (int x, int y, if (checkWidth > 0 && checkHeight > 0) { + context->saveState(); + if (colour1 == colour2) { - context->fillRectWithColour (x, y, width, height, colour1, false); + context->setColour (colour1); + context->fillRect (x, y, width, height, false); } else { @@ -635,30 +683,38 @@ void Graphics::fillCheckerBoard (int x, int y, int cx = cy; for (int xx = x; xx < right; xx += checkWidth) - context->fillRectWithColour (xx, y, - jmin (checkWidth, right - xx), - jmin (checkHeight, bottom - y), - ((cx++ & 1) == 0) ? colour1 : colour2, - false); + { + context->setColour (((cx++ & 1) == 0) ? colour1 : colour2); + context->fillRect (xx, y, + jmin (checkWidth, right - xx), + jmin (checkHeight, bottom - y), + false); + } ++cy; y += checkHeight; } } + + context->restoreState(); } } //============================================================================== void Graphics::drawVerticalLine (const int x, float top, float bottom) const throw() { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintVerticalLine (*context, x, top, bottom); + if (state->brush == 0) + context->drawVerticalLine (x, top, bottom); + else + state->brush->paintVerticalLine (*context, x, top, bottom); } void Graphics::drawHorizontalLine (const int y, float left, float right) const throw() { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintHorizontalLine (*context, y, left, right); + if (state->brush == 0) + context->drawHorizontalLine (y, left, right); + else + state->brush->paintHorizontalLine (*context, y, left, right); } void Graphics::drawLine (float x1, float y1, @@ -666,8 +722,10 @@ void Graphics::drawLine (float x1, float y1, { if (! context->isClipEmpty()) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintLine (*context, x1, y1, x2, y2); + if (state->brush == 0) + context->drawLine (x1, y1, x2, y2); + else + state->brush->paintLine (*context, x1, y1, x2, y2); } } @@ -740,7 +798,7 @@ void Graphics::drawDashedLine (const float startX, void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality) throw() { saveStateIfPending(); - state->quality = newQuality; + context->setInterpolationQuality (newQuality); } //============================================================================== @@ -847,18 +905,26 @@ void Graphics::drawImage (const Image* const imageToDraw, if (fillAlphaChannelWithCurrentBrush) { - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush) - .paintAlphaChannel (*context, *imageToDraw, - dx - sx, dy - sy, - dx, dy, - dw, dh); + if (state->brush == 0) + { + context->saveState(); + + if (context->reduceClipRegion (dx, dy, dw, dh)) + context->fillAlphaChannel (*imageToDraw, dx - sx, dy - sy); + + context->restoreState(); + } + else + { + state->brush->paintAlphaChannel (*context, *imageToDraw, + dx - sx, dy - sy, + dx, dy, dw, dh); + } } else { context->blendImage (*imageToDraw, - dx, dy, dw, dh, sx, sy, - state->colour.getFloatAlpha()); + dx, dy, dw, dh, sx, sy); } } else @@ -883,7 +949,7 @@ void Graphics::drawImage (const Image* const imageToDraw, { Image temp (imageToDraw->getFormat(), tw, th, true); Graphics g (temp); - g.setImageResamplingQuality (state->quality); +//xxx g.setImageResamplingQuality (state->quality); g.setOrigin (dx - tx, dy - ty); g.drawImage (imageToDraw, @@ -891,9 +957,19 @@ void Graphics::drawImage (const Image* const imageToDraw, sx, sy, sw, sh, false); - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush) - .paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + if (state->brush == 0) + { + context->saveState(); + + if (context->reduceClipRegion (tx, ty, tw, th)) + context->fillAlphaChannel (temp, tx, ty); + + context->restoreState(); + } + else + { + state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + } } } } @@ -906,9 +982,7 @@ void Graphics::drawImage (const Image* const imageToDraw, .scaled (dw / (float) sw, dh / (float) sh) .translated ((float) dx, - (float) dy), - state->colour.getFloatAlpha(), - state->quality); + (float) dy)); } } } @@ -951,7 +1025,7 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, { Image temp (imageToDraw->getFormat(), tw, th, true); Graphics g (temp); - g.setImageResamplingQuality (state->quality); +//xxx g.setImageResamplingQuality (state->quality); g.drawImageTransformed (imageToDraw, sourceClipX, @@ -961,8 +1035,19 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, transform.translated ((float) -tx, (float) -ty), false); - SolidColourBrush colourBrush (state->colour); - (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + if (state->brush == 0) + { + context->saveState(); + + if (context->reduceClipRegion (tx, ty, tw, th)) + context->fillAlphaChannel (temp, tx, ty); + + context->restoreState(); + } + else + { + state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + } } } else @@ -972,9 +1057,7 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, sourceClipY, sourceClipWidth, sourceClipHeight, - transform, - state->colour.getFloatAlpha(), - state->quality); + transform); } } } diff --git a/src/gui/graphics/contexts/juce_Graphics.h b/src/gui/graphics/contexts/juce_Graphics.h index 06bdfdda6a..4fccbeba12 100644 --- a/src/gui/graphics/contexts/juce_Graphics.h +++ b/src/gui/graphics/contexts/juce_Graphics.h @@ -88,6 +88,8 @@ public: If a solid colour is being used for drawing, this changes its opacity to this new value (i.e. it doesn't multiply the colour's opacity by this amount). + If a gradient is being used, this will have no effect on it. + A value of 0.0 is completely transparent, 1.0 is completely opaque. */ void setOpacity (const float newOpacity) throw(); @@ -706,10 +708,8 @@ private: GraphicsState (const GraphicsState&) throw(); ~GraphicsState() throw(); - Colour colour; Brush* brush; Font font; - ResamplingQuality quality; }; GraphicsState* state; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h index 3a4f801862..2ceb67db18 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h @@ -84,34 +84,38 @@ public: virtual bool isClipEmpty() const = 0; //============================================================================== - virtual void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) = 0; - virtual void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) = 0; + virtual void setColour (const Colour& colour) = 0; + virtual void setGradient (const ColourGradient& gradient) = 0; + virtual void setOpacity (float opacity) = 0; + virtual void setInterpolationQuality (Graphics::ResamplingQuality quality) = 0; + + //============================================================================== + virtual void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) = 0; + virtual void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality) = 0; - virtual void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality) = 0; - virtual void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) = 0; virtual void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality) = 0; + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality) = 0; - virtual void fillAlphaChannelWithColour (const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour) = 0; - virtual void fillAlphaChannelWithGradient (const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient) = 0; + virtual void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) = 0; virtual void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha) = 0; + const Image& fillerImage, int fillerImageX, int fillerImageY) = 0; //============================================================================== virtual void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY, - float alpha) = 0; + int destX, int destY, int destW, int destH, int sourceX, int sourceY) = 0; virtual void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality) = 0; + const AffineTransform& transform) = 0; //============================================================================== - virtual void drawLine (double x1, double y1, double x2, double y2, const Colour& colour) = 0; + virtual void drawLine (double x1, double y1, double x2, double y2) = 0; + virtual void drawVerticalLine (const int x, double top, double bottom) = 0; + virtual void drawHorizontalLine (const int y, double left, double right) = 0; - virtual void drawVerticalLine (const int x, double top, double bottom, const Colour& col) = 0; - virtual void drawHorizontalLine (const int y, double left, double right, const Colour& col) = 0; + virtual void setFont (const Font& newFont) = 0; + virtual void drawGlyph (int glyphNumber, float x, float y) = 0; + virtual void drawGlyph (int glyphNumber, const AffineTransform& transform) = 0; }; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index 5b6cbae3bb..91af3a48e4 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -62,7 +62,9 @@ LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputSt totalHeight (totalHeight_), xOffset (0), yOffset (0), - needToClip (true) + needToClip (true), + colour (Colours::black), + gradient (0) { clip = new RectangleList (Rectangle (0, 0, totalWidth_, totalHeight_)); @@ -102,6 +104,7 @@ LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputSt LowLevelGraphicsPostScriptRenderer::~LowLevelGraphicsPostScriptRenderer() { delete clip; + delete gradient; } //============================================================================== @@ -155,21 +158,28 @@ bool LowLevelGraphicsPostScriptRenderer::isClipEmpty() const //============================================================================== LowLevelGraphicsPostScriptRenderer::SavedState::SavedState (RectangleList* const clip_, - const int xOffset_, const int yOffset_) + const int xOffset_, const int yOffset_, + const Colour& colour_, ColourGradient* const gradient_, + const Font& font_) : clip (clip_), xOffset (xOffset_), - yOffset (yOffset_) + yOffset (yOffset_), + colour (colour_), + gradient (gradient_), + font (font_) { } LowLevelGraphicsPostScriptRenderer::SavedState::~SavedState() { delete clip; + delete gradient; } void LowLevelGraphicsPostScriptRenderer::saveState() { - stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset)); + stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset, colour, + gradient != 0 ? new ColourGradient (*gradient) : 0, font)); } void LowLevelGraphicsPostScriptRenderer::restoreState() @@ -178,13 +188,14 @@ void LowLevelGraphicsPostScriptRenderer::restoreState() if (top != 0) { - clip->swapWith (*top->clip); - + swapVariables (clip, top->clip); + swapVariables (gradient, top->gradient); + colour = top->colour; xOffset = top->xOffset; yOffset = top->yOffset; + font = top->font; stateStack.removeLast(); - needToClip = true; } else @@ -326,78 +337,95 @@ void LowLevelGraphicsPostScriptRenderer::writeTransform (const AffineTransform& } //============================================================================== -void LowLevelGraphicsPostScriptRenderer::fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool /*replaceExistingContents*/) +void LowLevelGraphicsPostScriptRenderer::setColour (const Colour& colour_) { - writeClip(); - writeColour (colour); - - x += xOffset; - y += yOffset; - - out << x << ' ' << -(y + h) << ' ' << w << ' ' << h << " rectfill\n"; + colour = colour_; + deleteAndZero (gradient); } -void LowLevelGraphicsPostScriptRenderer::fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) +void LowLevelGraphicsPostScriptRenderer::setGradient (const ColourGradient& gradient_) { - Path p; - p.addRectangle ((float) x, (float) y, (float) w, (float) h); + delete gradient; + gradient = new ColourGradient (gradient_); +} - fillPathWithGradient (p, AffineTransform::identity, gradient, EdgeTable::Oversampling_256times); +void LowLevelGraphicsPostScriptRenderer::setOpacity (float opacity) +{ +} + +void LowLevelGraphicsPostScriptRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) +{ } //============================================================================== -void LowLevelGraphicsPostScriptRenderer::fillPathWithColour (const Path& path, const AffineTransform& t, - const Colour& colour, EdgeTable::OversamplingLevel /*quality*/) +void LowLevelGraphicsPostScriptRenderer::fillRect (int x, int y, int w, int h, const bool /*replaceExistingContents*/) { - writeClip(); + if (gradient == 0) + { + writeClip(); + writeColour (colour); - Path p (path); - p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); - writePath (p); + x += xOffset; + y += yOffset; - writeColour (colour); + out << x << ' ' << -(y + h) << ' ' << w << ' ' << h << " rectfill\n"; + } + else + { + Path p; + p.addRectangle ((float) x, (float) y, (float) w, (float) h); + fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_256times); + } - out << "fill\n"; } -void LowLevelGraphicsPostScriptRenderer::fillPathWithGradient (const Path& path, const AffineTransform& t, const ColourGradient& gradient, EdgeTable::OversamplingLevel /*quality*/) +//============================================================================== +void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t, + EdgeTable::OversamplingLevel /*quality*/) { - // this doesn't work correctly yet - it could be improved to handle solid gradients, but - // postscript can't do semi-transparent ones. - notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file - - writeClip(); - out << "gsave "; - + if (gradient == 0) { + writeClip(); + Path p (path); p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); writePath (p); - out << "clip\n"; + + writeColour (colour); + + out << "fill\n"; } + else + { + // this doesn't work correctly yet - it could be improved to handle solid gradients, but + // postscript can't do semi-transparent ones. + notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file - int numColours = 256; - PixelARGB* const colours = gradient.createLookupTable (numColours); + writeClip(); + out << "gsave "; - for (int i = numColours; --i >= 0;) - colours[i].unpremultiply(); + { + Path p (path); + p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); + writePath (p); + out << "clip\n"; + } - const Rectangle bounds (clip->getBounds()); + const Rectangle bounds (clip->getBounds()); - // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the - // time-being, this just fills it with the average colour.. - writeColour (Colour (colours [numColours / 2].getARGB())); - out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n"; + // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the + // time-being, this just fills it with the average colour.. + writeColour (gradient->getColourAtPosition (0.5)); + out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n"; - - juce_free (colours); - out << "grestore\n"; + out << "grestore\n"; + } } void LowLevelGraphicsPostScriptRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, const Image& sourceImage, int imageX, int imageY, - float opacity, EdgeTable::OversamplingLevel /*quality*/) + EdgeTable::OversamplingLevel /*quality*/) { writeClip(); @@ -407,36 +435,30 @@ void LowLevelGraphicsPostScriptRenderer::fillPathWithImage (const Path& path, co writePath (p); out << "clip\n"; - blendImage (sourceImage, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight(), 0, 0, opacity); + blendImage (sourceImage, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight(), 0, 0); out << "grestore\n"; } //============================================================================== -void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithColour (const Image& /*clipImage*/, int x, int y, const Colour& colour) +void LowLevelGraphicsPostScriptRenderer::fillAlphaChannel (const Image& /*clipImage*/, int x, int y) { x += xOffset; y += yOffset; writeClip(); - writeColour (colour); - notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file -} - -void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithGradient (const Image& /*alphaChannelImage*/, int imageX, int imageY, const ColourGradient& /*gradient*/) -{ - imageX += xOffset; - imageY += yOffset; - - writeClip(); + if (gradient == 0) + { + writeColour (colour); + } notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file } void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithImage (const Image& /*alphaImage*/, int alphaImageX, int alphaImageY, - const Image& /*fillerImage*/, int fillerImageX, int fillerImageY, float /*opacity*/) + const Image& /*fillerImage*/, int fillerImageX, int fillerImageY) { alphaImageX += xOffset; alphaImageY += yOffset; @@ -450,12 +472,11 @@ void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithImage (const Image& } //============================================================================== -void LowLevelGraphicsPostScriptRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) +void LowLevelGraphicsPostScriptRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { blendImageWarping (sourceImage, sx, sy, dw, dh, - AffineTransform::translation ((float) dx, (float) dy), - opacity, Graphics::highResamplingQuality); + AffineTransform::translation ((float) dx, (float) dy)); } //============================================================================== @@ -524,9 +545,7 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, void LowLevelGraphicsPostScriptRenderer::blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t, - float /*opacity*/, - const Graphics::ResamplingQuality /*quality*/) + const AffineTransform& t) { const int w = jmin (sourceImage.getWidth(), srcClipX + srcClipW); const int h = jmin (sourceImage.getHeight(), srcClipY + srcClipH); @@ -570,24 +589,38 @@ void LowLevelGraphicsPostScriptRenderer::blendImageWarping (const Image& sourceI //============================================================================== -void LowLevelGraphicsPostScriptRenderer::drawLine (double x1, double y1, double x2, double y2, const Colour& colour) +void LowLevelGraphicsPostScriptRenderer::drawLine (double x1, double y1, double x2, double y2) { Path p; p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f); - - fillPathWithColour (p, AffineTransform::identity, colour, EdgeTable::Oversampling_256times); + fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_256times); } -void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, double top, double bottom, const Colour& col) +void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, double top, double bottom) { - drawLine (x, top, x, bottom, col); + drawLine (x, top, x, bottom); } -void LowLevelGraphicsPostScriptRenderer::drawHorizontalLine (const int y, double left, double right, const Colour& col) +void LowLevelGraphicsPostScriptRenderer::drawHorizontalLine (const int y, double left, double right) { - drawLine (left, y, right, y, col); + drawLine (left, y, right, y); } +//============================================================================== +void LowLevelGraphicsPostScriptRenderer::setFont (const Font& newFont) +{ + font = newFont; +} + +void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, float x, float y) +{ + drawGlyph (glyphNumber, AffineTransform::translation (x, y)); +} + +void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) +{ + font.renderGlyphIndirectly (*this, glyphNumber, transform); +} END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h index 2f39a75270..c62c94942f 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h @@ -62,33 +62,39 @@ public: bool isClipEmpty() const; //============================================================================== - void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient); + void setColour (const Colour& colour); + void setGradient (const ColourGradient& gradient); + void setOpacity (float opacity); + void setInterpolationQuality (Graphics::ResamplingQuality quality); + + //============================================================================== + void fillRect (int x, int y, int w, int h, const bool replaceExistingContents); + void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality); - void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); - void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality); - void fillAlphaChannelWithColour (const Image& alphaImage, int imageX, int imageY, const Colour& colour); - void fillAlphaChannelWithGradient (const Image& alphaImage, int imageX, int imageY, const ColourGradient& gradient); + void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY); void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + const Image& fillerImage, int fillerImageX, int fillerImageY); //============================================================================== void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, - int sourceX, int sourceY, float alpha); + int sourceX, int sourceY); void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality); + const AffineTransform& transform); //============================================================================== - void drawLine (double x1, double y1, double x2, double y2, const Colour& colour); + void drawLine (double x1, double y1, double x2, double y2); - void drawVerticalLine (const int x, double top, double bottom, const Colour& col); - void drawHorizontalLine (const int x, double top, double bottom, const Colour& col); + void drawVerticalLine (const int x, double top, double bottom); + void drawHorizontalLine (const int x, double top, double bottom); + //============================================================================== + void setFont (const Font& newFont); + void drawGlyph (int glyphNumber, float x, float y); + void drawGlyph (int glyphNumber, const AffineTransform& transform); //============================================================================== juce_UseDebuggingNewOperator @@ -99,15 +105,21 @@ protected: RectangleList* clip; int totalWidth, totalHeight, xOffset, yOffset; bool needToClip; - Colour lastColour; + Colour lastColour, colour; + ColourGradient* gradient; + Font font; struct SavedState { - SavedState (RectangleList* const clip, const int xOffset, const int yOffset); + SavedState (RectangleList* const clip, const int xOffset, const int yOffset, + const Colour& colour, ColourGradient* const gradient, const Font& font); ~SavedState(); RectangleList* clip; const int xOffset, yOffset; + Colour colour; + ColourGradient* gradient; + Font font; private: SavedState (const SavedState&); diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index 4836eff73b..25ca9063e2 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -34,6 +34,7 @@ BEGIN_JUCE_NAMESPACE #include "../geometry/juce_PathStrokeType.h" #include "../geometry/juce_Rectangle.h" #include "../../../core/juce_SystemStats.h" +#include "../../../utilities/juce_DeletedAtShutdown.h" #if (JUCE_WINDOWS || JUCE_LINUX) && ! JUCE_64BIT #define JUCE_USE_SSE_INSTRUCTIONS 1 @@ -904,7 +905,7 @@ static void transformedImageRender (Image& destImage, if (((unsigned int) iy) < (unsigned int) srcClipHeight) { const SrcPixelType* const src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - dest->set (*src); + dest->blend (*src); } } @@ -1062,7 +1063,9 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image : image (image_), xOffset (0), yOffset (0), - stateStack (20) + stateStack (20), + colour (0xff000000), + gradient (0) { clip = new RectangleList (Rectangle (0, 0, image_.getWidth(), image_.getHeight())); } @@ -1070,6 +1073,7 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() { delete clip; + delete gradient; } bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const @@ -1119,21 +1123,30 @@ bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const //============================================================================== LowLevelGraphicsSoftwareRenderer::SavedState::SavedState (RectangleList* const clip_, - const int xOffset_, const int yOffset_) + const int xOffset_, const int yOffset_, + const Font& font_, const Colour& colour_, ColourGradient* gradient_, + Graphics::ResamplingQuality interpolationQuality_) : clip (clip_), xOffset (xOffset_), - yOffset (yOffset_) + yOffset (yOffset_), + font (font_), + colour (colour_), + gradient (gradient_), + interpolationQuality (interpolationQuality_) { } LowLevelGraphicsSoftwareRenderer::SavedState::~SavedState() { delete clip; + delete gradient; } void LowLevelGraphicsSoftwareRenderer::saveState() { - stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset)); + stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset, + font, colour, gradient != 0 ? new ColourGradient (*gradient) : 0, + interpolationQuality)); } void LowLevelGraphicsSoftwareRenderer::restoreState() @@ -1142,10 +1155,13 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() if (top != 0) { - clip->swapWith (*top->clip); - + swapVariables (clip, top->clip); xOffset = top->xOffset; yOffset = top->yOffset; + font = top->font; + colour = top->colour; + swapVariables (gradient, top->gradient); + interpolationQuality = top->interpolationQuality; stateStack.removeLast(); } @@ -1156,14 +1172,50 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() } //============================================================================== -void LowLevelGraphicsSoftwareRenderer::fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) +void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour_) { - x += xOffset; - y += yOffset; + deleteAndZero (gradient); + colour = colour_; +} - for (RectangleList::Iterator i (*clip); i.next();) +void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient_) +{ + delete gradient; + gradient = new ColourGradient (gradient_); +} + +void LowLevelGraphicsSoftwareRenderer::setOpacity (float opacity) +{ + colour = colour.withAlpha (opacity); +} + +void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) +{ + interpolationQuality = quality; +} + +//============================================================================== +void LowLevelGraphicsSoftwareRenderer::fillRect (int x, int y, int w, int h, const bool replaceExistingContents) +{ + if (gradient != 0) { - clippedFillRectWithColour (*i.getRectangle(), x, y, w, h, colour, replaceExistingContents); + if (replaceExistingContents && ! gradient->isOpaque()) + { + for (RectangleList::Iterator i (*clip); i.next();) + clippedFillRectWithColour (*i.getRectangle(), x + xOffset, y + yOffset, w, h, Colours::transparentBlack, true); + } + + Path p; + p.addRectangle ((float) x, (float) y, (float) w, (float) h); + fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_none); + } + else + { + x += xOffset; + y += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + clippedFillRectWithColour (*i.getRectangle(), x, y, w, h, colour, replaceExistingContents); } } @@ -1198,13 +1250,6 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillRectWithColour (const Rectangl } } -void LowLevelGraphicsSoftwareRenderer::fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) -{ - Path p; - p.addRectangle ((float) x, (float) y, (float) w, (float) h); - fillPathWithGradient (p, AffineTransform::identity, gradient, EdgeTable::Oversampling_none); -} - //============================================================================== bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, @@ -1224,19 +1269,19 @@ bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int return Rectangle::intersectRectangles (x, y, w, h, clipX, clipY, clipW, clipH); } -void LowLevelGraphicsSoftwareRenderer::fillPathWithColour (const Path& path, const AffineTransform& t, - const Colour& colour, EdgeTable::OversamplingLevel quality) +void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality) { for (RectangleList::Iterator i (*clip); i.next();) { const Rectangle& r = *i.getRectangle(); - clippedFillPathWithColour (r.getX(), r.getY(), r.getWidth(), r.getHeight(), path, t, colour, quality); + clippedFillPath (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + path, transform, quality); } } -void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithColour (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& t, - const Colour& colour, EdgeTable::OversamplingLevel quality) +void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, + const AffineTransform& t, EdgeTable::OversamplingLevel quality) { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); int cx, cy, cw, ch; @@ -1244,139 +1289,114 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithColour (int clipX, int if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) { EdgeTable edgeTable (0, ch, quality); - edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); int stride, pixelStride; uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); - if (image.getFormat() == Image::RGB) + if (gradient != 0) { - jassert (pixelStride == 3); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (pixelStride == 1); - AlphaBitmapRenderer renderer (pixels, stride); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } + ColourGradient g2 (*gradient); - image.releasePixelDataReadWrite (pixels); - } -} + const bool isIdentity = g2.transform.isIdentity(); + if (isIdentity) + { + g2.x1 += xOffset - cx; + g2.x2 += xOffset - cx; + g2.y1 += yOffset - cy; + g2.y2 += yOffset - cy; + } + else + { + g2.transform = g2.transform.translated ((float) (xOffset - cx), + (float) (yOffset - cy)); + } -void LowLevelGraphicsSoftwareRenderer::fillPathWithGradient (const Path& path, const AffineTransform& t, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) -{ - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); + int numLookupEntries; + PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); + jassert (numLookupEntries > 0); - clippedFillPathWithGradient (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, t, gradient, quality); - } -} + if (image.getFormat() == Image::RGB) + { + jassert (pixelStride == 3); -void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithGradient (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& t, - const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) -{ - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - int cx, cy, cw, ch; + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else if (image.getFormat() == Image::ARGB) + { + jassert (pixelStride == 4); - if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) - { - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else if (image.getFormat() == Image::SingleChannel) + { + jassertfalse // not done! + } - ColourGradient g2 (gradient); - - const bool isIdentity = g2.transform.isIdentity(); - if (isIdentity) - { - g2.x1 += xOffset - cx; - g2.x2 += xOffset - cx; - g2.y1 += yOffset - cy; - g2.y2 += yOffset - cy; + juce_free (lookupTable); } else { - g2.transform = g2.transform.translated ((float) (xOffset - cx), - (float) (yOffset - cy)); - } - - int numLookupEntries; - PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); - jassert (numLookupEntries > 0); - - EdgeTable edgeTable (0, ch, quality); - - edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); - - if (image.getFormat() == Image::RGB) - { - jassert (pixelStride == 3); - - if (g2.isRadial) + if (image.getFormat() == Image::RGB) { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } + jassert (pixelStride == 3); + SolidColourEdgeTableRenderer renderer (pixels, stride, colour); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); } - else + else if (image.getFormat() == Image::ARGB) { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + jassert (pixelStride == 4); + SolidColourEdgeTableRenderer renderer (pixels, stride, colour); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (pixelStride == 1); + AlphaBitmapRenderer renderer (pixels, stride); edgeTable.iterate (renderer, 0, 0, cw, ch, 0); } } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer, 0, 0, cw, ch, 0); - } - } - else if (image.getFormat() == Image::SingleChannel) - { - jassertfalse // not done! - } - - juce_free (lookupTable); image.releasePixelDataReadWrite (pixels); } } void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& sourceImage, int imageX, int imageY, float opacity, EdgeTable::OversamplingLevel quality) + const Image& sourceImage, int imageX, int imageY, EdgeTable::OversamplingLevel quality) { imageX += xOffset; imageY += yOffset; @@ -1386,7 +1406,8 @@ void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, cons const Rectangle& r = *i.getRectangle(); clippedFillPathWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, transform, sourceImage, imageX, imageY, opacity, quality); + path, transform, sourceImage, imageX, imageY, + colour.getFloatAlpha(), quality); } } @@ -1459,7 +1480,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithImage (int x, int y, i } //============================================================================== -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithColour (const Image& clipImage, int x, int y, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::fillAlphaChannel (const Image& clipImage, int x, int y) { x += xOffset; y += yOffset; @@ -1468,114 +1489,101 @@ void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithColour (const Image& { const Rectangle& r = *i.getRectangle(); - clippedFillAlphaChannelWithColour (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - clipImage, x, y, colour); + clippedFillAlphaChannel (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + clipImage, x, y); } } -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithColour (int clipX, int clipY, int clipW, int clipH, const Image& clipImage, int x, int y, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& clipImage, int x, int y) { - int w = clipImage.getWidth(); - int h = clipImage.getHeight(); - int sx = 0; - int sy = 0; - - if (x < clipX) + if (gradient != 0) { - sx = clipX - x; - w -= clipX - x; - x = clipX; + if (Rectangle::intersectRectangles (clipX, clipY, clipW, clipH, x, y, clipImage.getWidth(), clipImage.getHeight())) + { + ColourGradient g2 (*gradient); + g2.x1 += xOffset - clipX; + g2.x2 += xOffset - clipX; + g2.y1 += yOffset - clipY; + g2.y2 += yOffset - clipY; + + Image temp (g2.isOpaque() ? Image::RGB : Image::ARGB, clipW, clipH, true); + LowLevelGraphicsSoftwareRenderer tempG (temp); + tempG.setGradient (g2); + tempG.fillRect (0, 0, clipW, clipH, false); + + clippedFillAlphaChannelWithImage (clipX, clipY, clipW, clipH, + clipImage, x, y, + temp, clipX, clipY, 1.0f); + } } - - if (y < clipY) + else { - sy = clipY - y; - h -= clipY - y; - y = clipY; - } + int w = clipImage.getWidth(); + int h = clipImage.getHeight(); + int sx = 0; + int sy = 0; - if (x + w > clipX + clipW) - w = clipX + clipW - x; + if (x < clipX) + { + sx = clipX - x; + w -= clipX - x; + x = clipX; + } - if (y + h > clipY + clipH) - h = clipY + clipH - y; + if (y < clipY) + { + sy = clipY - y; + h -= clipY - y; + y = clipY; + } - if (w > 0 && h > 0) - { - int stride, alphaStride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); + if (x + w > clipX + clipW) + w = clipX + clipW - x; - const uint8* const alphaValues - = clipImage.lockPixelDataReadOnly (sx, sy, w, h, alphaStride, pixelStride); + if (y + h > clipY + clipH) + h = clipY + clipH - y; + + if (w > 0 && h > 0) + { + int stride, alphaStride, pixelStride; + uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); + + const uint8* const alphaValues + = clipImage.lockPixelDataReadOnly (sx, sy, w, h, alphaStride, pixelStride); #if JUCE_BIG_ENDIAN - const uint8* const alphas = alphaValues; + const uint8* const alphas = alphaValues; #else - const uint8* const alphas = alphaValues + (clipImage.getFormat() == Image::ARGB ? 3 : 0); + const uint8* const alphas = alphaValues + (clipImage.getFormat() == Image::ARGB ? 3 : 0); #endif - if (image.getFormat() == Image::RGB) - { - blendAlphaMapRGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); + if (image.getFormat() == Image::RGB) + { + blendAlphaMapRGB (pixels, stride, + alphas, w, h, + pixelStride, alphaStride, + colour); + } + else if (image.getFormat() == Image::ARGB) + { + blendAlphaMapARGB (pixels, stride, + alphas, w, h, + pixelStride, alphaStride, + colour); + } + else + { + jassertfalse // not done! + } + + clipImage.releasePixelDataReadOnly (alphaValues); + image.releasePixelDataReadWrite (pixels); } - else if (image.getFormat() == Image::ARGB) - { - blendAlphaMapARGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); - } - else - { - jassertfalse // not done! - } - - clipImage.releasePixelDataReadOnly (alphaValues); - image.releasePixelDataReadWrite (pixels); - } -} - -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithGradient (const Image& alphaChannelImage, int imageX, int imageY, const ColourGradient& gradient) -{ - imageX += xOffset; - imageY += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedFillAlphaChannelWithGradient (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - alphaChannelImage, imageX, imageY, gradient); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithGradient (int x, int y, int w, int h, - const Image& alphaChannelImage, - int imageX, int imageY, const ColourGradient& gradient) -{ - if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, alphaChannelImage.getWidth(), alphaChannelImage.getHeight())) - { - ColourGradient g2 (gradient); - g2.x1 += xOffset - x; - g2.x2 += xOffset - x; - g2.y1 += yOffset - y; - g2.y2 += yOffset - y; - - Image temp (g2.isOpaque() ? Image::RGB : Image::ARGB, w, h, true); - LowLevelGraphicsSoftwareRenderer tempG (temp); - tempG.fillRectWithGradient (0, 0, w, h, g2); - - clippedFillAlphaChannelWithImage (x, y, w, h, - alphaChannelImage, imageX, imageY, - temp, x, y, 1.0f); } } void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float opacity) + const Image& fillerImage, int fillerImageX, int fillerImageY) { alphaImageX += xOffset; alphaImageY += yOffset; @@ -1589,7 +1597,8 @@ void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& a clippedFillAlphaChannelWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), alphaImage, alphaImageX, alphaImageY, - fillerImage, fillerImageX, fillerImageY, opacity); + fillerImage, fillerImageX, fillerImageY, + colour.getFloatAlpha()); } } @@ -1659,7 +1668,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithImage (int x, } //============================================================================== -void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) +void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { dx += xOffset; dy += yOffset; @@ -1669,12 +1678,12 @@ void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int const Rectangle& r = *i.getRectangle(); clippedBlendImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - sourceImage, dx, dy, dw, dh, sx, sy, opacity); + sourceImage, dx, dy, dw, dh, sx, sy); } } void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, int clipW, int clipH, - const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) + const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { if (dx < clipX) { @@ -1699,7 +1708,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, if (dw <= 0 || dh <= 0) return; - const uint8 alpha = (uint8) jlimit (0, 0xff, roundDoubleToInt (opacity * 256.0f)); + const uint8 alpha = (uint8) colour.getAlpha(); if (alpha == 0) return; @@ -1760,9 +1769,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, //============================================================================== void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t, - float opacity, - const Graphics::ResamplingQuality quality) + const AffineTransform& t) { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); @@ -1772,18 +1779,16 @@ void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceIma clippedBlendImageWarping (r.getX(), r.getY(), r.getWidth(), r.getHeight(), sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, - transform, opacity, quality); + transform); } } void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, int destClipY, int destClipW, int destClipH, const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float opacity, - const Graphics::ResamplingQuality quality) + const AffineTransform& transform) { - if (opacity > 0 && destClipW > 0 && destClipH > 0 && ! transform.isSingularity()) + if ((! colour.isTransparent()) && destClipW > 0 && destClipH > 0 && ! transform.isSingularity()) { Rectangle::intersectRectangles (srcClipX, srcClipY, srcClipW, srcClipH, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); @@ -1805,7 +1810,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, 1 + roundDoubleToInt (imW), 1 + roundDoubleToInt (imH))) { - const uint8 alpha = (uint8) jlimit (0, 0xff, roundDoubleToInt (opacity * 256.0f)); + const uint8 alpha = (uint8) colour.getAlpha(); float srcX1 = (float) destClipX; float srcY1 = (float) destClipY; @@ -1832,7 +1837,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelARGB*)0, (PixelARGB*)0); + alpha, interpolationQuality, (PixelARGB*)0, (PixelARGB*)0); } else if (sourceImage.getFormat() == Image::RGB) { @@ -1840,7 +1845,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelARGB*)0, (PixelRGB*)0); + alpha, interpolationQuality, (PixelARGB*)0, (PixelRGB*)0); } else { @@ -1855,7 +1860,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelRGB*)0, (PixelARGB*)0); + alpha, interpolationQuality, (PixelRGB*)0, (PixelARGB*)0); } else if (sourceImage.getFormat() == Image::RGB) { @@ -1863,7 +1868,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, destClipX, destClipY, destClipW, destClipH, srcClipX, srcClipY, srcClipW, srcClipH, srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, quality, (PixelRGB*)0, (PixelRGB*)0); + alpha, interpolationQuality, (PixelRGB*)0, (PixelRGB*)0); } else { @@ -1879,7 +1884,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, } //============================================================================== -void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2) { x1 += xOffset; y1 += yOffset; @@ -1891,11 +1896,11 @@ void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2 const Rectangle& r = *i.getRectangle(); clippedDrawLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x1, y1, x2, y2, colour); + x1, y1, x2, y2); } } -void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2, const Colour& colour) +void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2) { if (clipW > 0 && clipH > 0) { @@ -1904,14 +1909,14 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in if (y2 < y1) swapVariables (y1, y2); - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (x1), y1, y2, colour); + clippedDrawVerticalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (x1), y1, y2); } else if (y1 == y2) { if (x2 < x1) swapVariables (x1, x2); - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (y1), x1, x2, colour); + clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (y1), x1, x2); } else { @@ -1931,7 +1936,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in while (y < endY) { const double x = x1 + gradient * (y - startY); - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, y, x, x + 1.0, colour); + clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, y, x, x + 1.0); ++y; } } @@ -1947,7 +1952,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in while (x < endX) { const double y = y1 + gradient * (x - startX); - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, x, y, y + 1.0, colour); + clippedDrawVerticalLine (clipX, clipY, clipW, clipH, x, y, y + 1.0); ++x; } } @@ -1955,19 +1960,19 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, in } } -void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom, const Colour& col) +void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom) { for (RectangleList::Iterator i (*clip); i.next();) { const Rectangle& r = *i.getRectangle(); clippedDrawVerticalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x + xOffset, top + yOffset, bottom + yOffset, col); + x + xOffset, top + yOffset, bottom + yOffset); } } void LowLevelGraphicsSoftwareRenderer::clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, - const int x, double top, double bottom, const Colour& col) + const int x, double top, double bottom) { jassert (top <= bottom); @@ -1983,23 +1988,23 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawVerticalLine (int clipX, int c bottom = clipY + clipH; if (bottom > top) - drawVertical (x, top, bottom, col); + drawVertical (x, top, bottom); } } -void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right, const Colour& col) +void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right) { for (RectangleList::Iterator i (*clip); i.next();) { const Rectangle& r = *i.getRectangle(); clippedDrawHorizontalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - y + yOffset, left + xOffset, right + xOffset, col); + y + yOffset, left + xOffset, right + xOffset); } } void LowLevelGraphicsSoftwareRenderer::clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, - const int y, double left, double right, const Colour& col) + const int y, double left, double right) { jassert (left <= right); @@ -2015,14 +2020,13 @@ void LowLevelGraphicsSoftwareRenderer::clippedDrawHorizontalLine (int clipX, int right = clipX + clipW; if (right > left) - drawHorizontal (y, left, right, col); + drawHorizontal (y, left, right); } } void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, const double top, - const double bottom, - const Colour& col) + const double bottom) { int wholeStart = (int) top; const int wholeEnd = (int) bottom; @@ -2037,7 +2041,7 @@ void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, uint8* const dstPixels = image.lockPixelDataReadWrite (x, wholeStart, 1, totalPixels, lineStride, dstPixelStride); uint8* dest = dstPixels; - PixelARGB colour (col.getPixelARGB()); + PixelARGB colour (this->colour.getPixelARGB()); if (wholeEnd == wholeStart) { @@ -2123,8 +2127,7 @@ void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, const double top, - const double bottom, - const Colour& col) + const double bottom) { int wholeStart = (int) top; const int wholeEnd = (int) bottom; @@ -2139,7 +2142,7 @@ void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, uint8* const dstPixels = image.lockPixelDataReadWrite (wholeStart, y, totalPixels, 1, lineStride, dstPixelStride); uint8* dest = dstPixels; - PixelARGB colour (col.getPixelARGB()); + PixelARGB colour (this->colour.getPixelARGB()); if (wholeEnd == wholeStart) { @@ -2223,6 +2226,21 @@ void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, image.releasePixelDataReadWrite (dstPixels); } +//============================================================================== +void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) +{ + font = newFont; +} + +void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, float x, float y) +{ + font.renderGlyphIndirectly (*this, glyphNumber, x, y); +} + +void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) +{ + font.renderGlyphIndirectly (*this, glyphNumber, transform); +} END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h index 1881a97595..25a2e42da2 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h @@ -61,32 +61,39 @@ public: bool isClipEmpty() const; //============================================================================== - void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient); + void setColour (const Colour& colour); + void setGradient (const ColourGradient& gradient); + void setOpacity (float opacity); + void setInterpolationQuality (Graphics::ResamplingQuality quality); + + //============================================================================== + void fillRect (int x, int y, int w, int h, const bool replaceExistingContents); + void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality); - void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); - void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality); - void fillAlphaChannelWithColour (const Image& alphaImage, int imageX, int imageY, const Colour& colour); - void fillAlphaChannelWithGradient (const Image& alphaImage, int imageX, int imageY, const ColourGradient& gradient); + void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY); void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + const Image& fillerImage, int fillerImageX, int fillerImageY); //============================================================================== void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, - int sourceX, int sourceY, float alpha); + int sourceX, int sourceY); void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality); + const AffineTransform& transform); //============================================================================== - void drawLine (double x1, double y1, double x2, double y2, const Colour& colour); + void drawLine (double x1, double y1, double x2, double y2); - void drawVerticalLine (const int x, double top, double bottom, const Colour& col); - void drawHorizontalLine (const int x, double top, double bottom, const Colour& col); + void drawVerticalLine (const int x, double top, double bottom); + void drawHorizontalLine (const int x, double top, double bottom); + + //============================================================================== + void setFont (const Font& newFont); + void drawGlyph (int glyphNumber, float x, float y); + void drawGlyph (int glyphNumber, const AffineTransform& transform); //============================================================================== RectangleList* getRawClipRegion() throw() { return clip; } @@ -99,14 +106,24 @@ protected: Image& image; RectangleList* clip; int xOffset, yOffset; + Font font; + Colour colour; + ColourGradient* gradient; + Graphics::ResamplingQuality interpolationQuality; struct SavedState { - SavedState (RectangleList* const clip, const int xOffset, const int yOffset); + SavedState (RectangleList* const clip, const int xOffset, const int yOffset, + const Font& font, const Colour& colour, ColourGradient* gradient, + Graphics::ResamplingQuality interpolationQuality); ~SavedState(); RectangleList* clip; const int xOffset, yOffset; + Font font; + Colour colour; + ColourGradient* gradient; + Graphics::ResamplingQuality interpolationQuality; private: SavedState (const SavedState&); @@ -115,8 +132,8 @@ protected: OwnedArray stateStack; - void drawVertical (const int x, const double top, const double bottom, const Colour& col); - void drawHorizontal (const int y, const double top, const double bottom, const Colour& col); + void drawVertical (const int x, const double top, const double bottom); + void drawHorizontal (const int y, const double top, const double bottom); bool getPathBounds (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, @@ -124,31 +141,27 @@ protected: void clippedFillRectWithColour (const Rectangle& clipRect, int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - void clippedFillPathWithColour (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); - void clippedFillPathWithGradient (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); + void clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality); void clippedFillPathWithImage (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); - void clippedFillAlphaChannelWithColour (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour); - void clippedFillAlphaChannelWithGradient (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient); + void clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY); void clippedFillAlphaChannelWithImage (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + const Image& fillerImage, int fillerImageX, int fillerImageY, const float opacity); //============================================================================== void clippedBlendImage (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY, - float alpha); + int destX, int destY, int destW, int destH, int sourceX, int sourceY); void clippedBlendImageWarping (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality); + const AffineTransform& transform); //============================================================================== - void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2, const Colour& colour); + void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2); - void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom, const Colour& col); - void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom, const Colour& col); + void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); + void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); LowLevelGraphicsSoftwareRenderer (const LowLevelGraphicsSoftwareRenderer& other); const LowLevelGraphicsSoftwareRenderer& operator= (const LowLevelGraphicsSoftwareRenderer&); diff --git a/src/gui/graphics/fonts/juce_Font.cpp b/src/gui/graphics/fonts/juce_Font.cpp index 7ed9ac9cdd..bbdb6e339f 100644 --- a/src/gui/graphics/fonts/juce_Font.cpp +++ b/src/gui/graphics/fonts/juce_Font.cpp @@ -29,6 +29,10 @@ BEGIN_JUCE_NAMESPACE #include "juce_Font.h" #include "juce_GlyphArrangement.h" +#include "../../components/lookandfeel/juce_LookAndFeel.h" +#include "../../graphics/contexts/juce_LowLevelGraphicsContext.h" +#include "../../../utilities/juce_DeletedAtShutdown.h" +#include "../../../core/juce_Singleton.h" //============================================================================== @@ -36,53 +40,27 @@ static const float minFontHeight = 0.1f; static const float maxFontHeight = 10000.0f; static const float defaultFontHeight = 14.0f; +static const tchar* const juce_defaultFontNameSans = T(""); +static const tchar* const juce_defaultFontNameSerif = T(""); +static const tchar* const juce_defaultFontNameMono = T(""); + +void clearUpDefaultFontNames() throw(); // in juce_LookAndFeel.cpp //============================================================================== -Font::Font() throw() - : typefaceName (Typeface::defaultTypefaceNameSans), - height (defaultFontHeight), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (Font::plain) -{ -} - -void Font::resetToDefaultState() throw() -{ - typefaceName = Typeface::defaultTypefaceNameSans; - height = defaultFontHeight; - horizontalScale = 1.0f; - kerning = 0; - ascent = 0; - styleFlags = Font::plain; - typeface = 0; -} - -Font::Font (const float fontHeight, - const int styleFlags_) throw() - : typefaceName (Typeface::defaultTypefaceNameSans), - height (jlimit (minFontHeight, maxFontHeight, fontHeight)), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (styleFlags_) -{ -} - -Font::Font (const String& typefaceName_, - const float fontHeight, - const int styleFlags_) throw() +Font::SharedFontInternal::SharedFontInternal (const String& typefaceName_, const float height_, const float horizontalScale_, + const float kerning_, const float ascent_, const int styleFlags_, + Typeface* const typeface_) throw() : typefaceName (typefaceName_), - height (jlimit (minFontHeight, maxFontHeight, fontHeight)), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (styleFlags_) + height (height_), + horizontalScale (horizontalScale_), + kerning (kerning_), + ascent (ascent_), + styleFlags (styleFlags_), + typeface (typeface_) { } -Font::Font (const Font& other) throw() +Font::SharedFontInternal::SharedFontInternal (const SharedFontInternal& other) throw() : typefaceName (other.typefaceName), height (other.height), horizontalScale (other.horizontalScale), @@ -93,19 +71,36 @@ Font::Font (const Font& other) throw() { } + +//============================================================================== +Font::Font() throw() + : font (new SharedFontInternal (juce_defaultFontNameSans, defaultFontHeight, + 1.0f, 0, 0, Font::plain, 0)) +{ +} + +Font::Font (const float fontHeight, const int styleFlags_) throw() + : font (new SharedFontInternal (juce_defaultFontNameSans, jlimit (minFontHeight, maxFontHeight, fontHeight), + 1.0f, 0, 0, styleFlags_, 0)) +{ +} + +Font::Font (const String& typefaceName_, + const float fontHeight, + const int styleFlags_) throw() + : font (new SharedFontInternal (typefaceName_, jlimit (minFontHeight, maxFontHeight, fontHeight), + 1.0f, 0, 0, styleFlags_, 0)) +{ +} + +Font::Font (const Font& other) throw() + : font (other.font) +{ +} + const Font& Font::operator= (const Font& other) throw() { - if (this != &other) - { - typefaceName = other.typefaceName; - height = other.height; - styleFlags = other.styleFlags; - horizontalScale = other.horizontalScale; - kerning = other.kerning; - ascent = other.ascent; - typeface = other.typeface; - } - + font = other.font; return *this; } @@ -113,26 +108,20 @@ Font::~Font() throw() { } -Font::Font (const Typeface& face) throw() - : height (11.0f), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (plain) +Font::Font (const Typeface::Ptr& typeface) throw() + : font (new SharedFontInternal (typeface->getName(), defaultFontHeight, + 1.0f, 0, 0, Font::plain, typeface)) { - typefaceName = face.getName(); - setBold (face.isBold()); - setItalic (face.isItalic()); - typeface = new Typeface (face); } bool Font::operator== (const Font& other) const throw() { - return height == other.height - && horizontalScale == other.horizontalScale - && kerning == other.kerning - && styleFlags == other.styleFlags - && typefaceName == other.typefaceName; + return font == other.font + || (font->height == other.font->height + && font->styleFlags == other.font->styleFlags + && font->horizontalScale == other.font->horizontalScale + && font->kerning == other.font->kerning + && font->typefaceName == other.font->typefaceName); } bool Font::operator!= (const Font& other) const throw() @@ -140,12 +129,37 @@ bool Font::operator!= (const Font& other) const throw() return ! operator== (other); } +void Font::dupeInternalIfShared() throw() +{ + if (font->getReferenceCount() > 1) + font = new SharedFontInternal (*font); +} + //============================================================================== +const String Font::getDefaultSansSerifFontName() throw() +{ + return juce_defaultFontNameSans; +} + +const String Font::getDefaultSerifFontName() throw() +{ + return juce_defaultFontNameSerif; +} + +const String Font::getDefaultMonospacedFontName() throw() +{ + return juce_defaultFontNameMono; +} + void Font::setTypefaceName (const String& faceName) throw() { - typefaceName = faceName; - typeface = 0; - ascent = 0; + if (faceName != font->typefaceName) + { + dupeInternalIfShared(); + font->typefaceName = faceName; + font->typeface = 0; + font->ascent = 0; + } } //============================================================================== @@ -164,92 +178,114 @@ void Font::setFallbackFontName (const String& name) throw() //============================================================================== void Font::setHeight (float newHeight) throw() { - height = jlimit (minFontHeight, maxFontHeight, newHeight); + newHeight = jlimit (minFontHeight, maxFontHeight, newHeight); + + if (font->height != newHeight) + { + dupeInternalIfShared(); + font->height = newHeight; + } } void Font::setHeightWithoutChangingWidth (float newHeight) throw() { newHeight = jlimit (minFontHeight, maxFontHeight, newHeight); - horizontalScale *= (height / newHeight); - height = newHeight; + + if (font->height != newHeight) + { + dupeInternalIfShared(); + font->horizontalScale *= (font->height / newHeight); + font->height = newHeight; + } } void Font::setStyleFlags (const int newFlags) throw() { - if (styleFlags != newFlags) + if (font->styleFlags != newFlags) { - styleFlags = newFlags; - typeface = 0; - ascent = 0; + dupeInternalIfShared(); + font->styleFlags = newFlags; + font->typeface = 0; + font->ascent = 0; } } -void Font::setSizeAndStyle (const float newHeight, +void Font::setSizeAndStyle (float newHeight, const int newStyleFlags, const float newHorizontalScale, const float newKerningAmount) throw() { - height = jlimit (minFontHeight, maxFontHeight, newHeight); - horizontalScale = newHorizontalScale; - kerning = newKerningAmount; + newHeight = jlimit (minFontHeight, maxFontHeight, newHeight); + + if (font->height != newHeight + || font->horizontalScale != newHorizontalScale + || font->kerning != newKerningAmount) + { + dupeInternalIfShared(); + font->height = newHeight; + font->horizontalScale = newHorizontalScale; + font->kerning = newKerningAmount; + } setStyleFlags (newStyleFlags); } void Font::setHorizontalScale (const float scaleFactor) throw() { - horizontalScale = scaleFactor; + dupeInternalIfShared(); + font->horizontalScale = scaleFactor; } void Font::setExtraKerningFactor (const float extraKerning) throw() { - kerning = extraKerning; + dupeInternalIfShared(); + font->kerning = extraKerning; } void Font::setBold (const bool shouldBeBold) throw() { - setStyleFlags (shouldBeBold ? (styleFlags | bold) - : (styleFlags & ~bold)); + setStyleFlags (shouldBeBold ? (font->styleFlags | bold) + : (font->styleFlags & ~bold)); } bool Font::isBold() const throw() { - return (styleFlags & bold) != 0; + return (font->styleFlags & bold) != 0; } void Font::setItalic (const bool shouldBeItalic) throw() { - setStyleFlags (shouldBeItalic ? (styleFlags | italic) - : (styleFlags & ~italic)); + setStyleFlags (shouldBeItalic ? (font->styleFlags | italic) + : (font->styleFlags & ~italic)); } bool Font::isItalic() const throw() { - return (styleFlags & italic) != 0; + return (font->styleFlags & italic) != 0; } void Font::setUnderline (const bool shouldBeUnderlined) throw() { - setStyleFlags (shouldBeUnderlined ? (styleFlags | underlined) - : (styleFlags & ~underlined)); + setStyleFlags (shouldBeUnderlined ? (font->styleFlags | underlined) + : (font->styleFlags & ~underlined)); } bool Font::isUnderlined() const throw() { - return (styleFlags & underlined) != 0; + return (font->styleFlags & underlined) != 0; } float Font::getAscent() const throw() { - if (ascent == 0) - ascent = getTypeface()->getAscent(); + if (font->ascent == 0) + font->ascent = getTypeface()->getAscent(); - return height * ascent; + return font->height * font->ascent; } float Font::getDescent() const throw() { - return height - getAscent(); + return font->height - getAscent(); } int Font::getStringWidth (const String& text) const throw() @@ -259,35 +295,32 @@ int Font::getStringWidth (const String& text) const throw() float Font::getStringWidthFloat (const String& text) const throw() { - float x = 0.0f; + float w = getTypeface()->getStringWidth (text); - if (text.isNotEmpty()) - { - Typeface* const typeface = getTypeface(); - const juce_wchar* t = (const juce_wchar*) text; + if (font->kerning != 0) + w += font->kerning * text.length(); - do - { - const TypefaceGlyphInfo* const glyph = typeface->getGlyph (*t++); - - if (glyph != 0) - x += kerning + glyph->getHorizontalSpacing (*t); - } - while (*t != 0); - - x *= height; - x *= horizontalScale; - } - - return x; + return w * font->height * font->horizontalScale; } -Typeface* Font::getTypeface() const throw() +void Font::getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) const throw() { - if (typeface == 0) - typeface = Typeface::getTypefaceFor (*this); + getTypeface()->getGlyphPositions (text, glyphs, xOffsets); - return typeface; + 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 + { + for (int i = 0; i < num; ++i) + x[i] *= scale; + } } void Font::findFonts (OwnedArray& destArray) throw() @@ -298,4 +331,355 @@ void Font::findFonts (OwnedArray& destArray) throw() destArray.add (new Font (names[i], defaultFontHeight, Font::plain)); } + +//============================================================================== +class TypefaceCache : public DeletedAtShutdown +{ +public: + TypefaceCache (int numToCache = 10) throw() + : counter (1), + faces (2) + { + while (--numToCache >= 0) + faces.add (new CachedFace()); + } + + ~TypefaceCache() + { + clearUpDefaultFontNames(); + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache) + + const Typeface::Ptr findTypefaceFor (const Font& font) throw() + { + const int flags = font.getStyleFlags() & (Font::bold | Font::italic); + const String faceName (font.getTypefaceName()); + + int i; + for (i = faces.size(); --i >= 0;) + { + CachedFace* const face = faces.getUnchecked(i); + + if (face->flags == flags + && face->typefaceName == faceName) + { + face->lastUsageCount = ++counter; + return face->typeFace; + } + } + + int replaceIndex = 0; + int bestLastUsageCount = INT_MAX; + + for (i = faces.size(); --i >= 0;) + { + const int lu = faces.getUnchecked(i)->lastUsageCount; + + if (bestLastUsageCount > lu) + { + bestLastUsageCount = lu; + replaceIndex = i; + } + } + + CachedFace* const face = faces.getUnchecked (replaceIndex); + face->typefaceName = faceName; + face->flags = flags; + face->lastUsageCount = ++counter; + face->typeFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); + jassert (face->typeFace != 0); // the look and feel must return a typeface! + + return face->typeFace; + } + + juce_UseDebuggingNewOperator + +private: + struct CachedFace + { + CachedFace() throw() + : lastUsageCount (0), flags (-1) + { + } + + String typefaceName; + int lastUsageCount; + int flags; + Typeface::Ptr typeFace; + }; + + int counter; + OwnedArray faces; + + TypefaceCache (const TypefaceCache&); + const TypefaceCache& operator= (const TypefaceCache&); +}; + +juce_ImplementSingleton_SingleThreaded (TypefaceCache) + + +Typeface* Font::getTypeface() const throw() +{ + if (font->typeface == 0) + font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this); + + return font->typeface; +} + + +//============================================================================== +class FontGlyphAlphaMap +{ +public: + //============================================================================== + FontGlyphAlphaMap() throw() + : glyph (0), lastAccessCount (0), + bitmap1 (0), bitmap2 (0) + { + } + + ~FontGlyphAlphaMap() throw() + { + delete bitmap1; + delete bitmap2; + } + + void draw (LowLevelGraphicsContext& g, float x, const float y) const throw() + { + if (bitmap1 != 0) + { + x += xOrigin; + const float xFloor = floorf (x); + const int intX = (int) xFloor; + + g.fillAlphaChannel (((x - xFloor) >= 0.5f && bitmap2 != 0) ? *bitmap2 : *bitmap1, + intX, (int) floorf (y + yOrigin)); + } + } + + void generate (const Font& font_, const int glyph_) throw() + { + font = font_; + glyph = glyph_; + + deleteAndZero (bitmap1); + deleteAndZero (bitmap2); + + Path glyphPath; + font.getTypeface()->getOutlineForGlyph (glyph_, glyphPath); + + if (! glyphPath.isEmpty()) + { + const float fontHeight = font.getHeight(); + const float fontHScale = fontHeight * font.getHorizontalScale(); + + bitmap1 = createAlphaMapFromPath (glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.0f); + + if (fontHScale < 24.0f) + bitmap2 = createAlphaMapFromPath (glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.5f); + } + else + { + xOrigin = yOrigin = 0; + } + } + + int glyph, lastAccessCount; + Font font; + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + Image* bitmap1; + Image* bitmap2; + float xOrigin, yOrigin; + + class AlphaBitmapRenderer + { + public: + AlphaBitmapRenderer (uint8* const data_, const int stride_) throw() + : data (data_), stride (stride_) + { + } + + forcedinline void setEdgeTableYPos (const int y) throw() + { + lineStart = data + (stride * y); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() + { + lineStart [x] = (uint8) alphaLevel; + } + + forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() + { + uint8* d = lineStart + x; + + while (--width >= 0) + *d++ = (uint8) alphaLevel; + } + + private: + uint8* const data; + const int stride; + uint8* lineStart; + + AlphaBitmapRenderer (const AlphaBitmapRenderer&); + const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&); + }; + + Image* createAlphaMapFromPath (const Path& path, + float& topLeftX, float& topLeftY, + float xScale, float yScale, + const float subPixelOffsetX) throw() + { + Image* im = 0; + + float px, py, pw, ph; + path.getBounds (px, py, pw, ph); + + topLeftX = floorf (px * xScale); + topLeftY = floorf (py * yScale); + + const int bitmapWidth = roundFloatToInt (pw * xScale) + 2; + const int bitmapHeight = roundFloatToInt (ph * yScale) + 2; + + im = new Image (Image::SingleChannel, bitmapWidth, bitmapHeight, true); + + EdgeTable edgeTable (0, bitmapHeight, EdgeTable::Oversampling_16times); + + edgeTable.addPath (path, AffineTransform::scale (xScale, yScale) + .translated (subPixelOffsetX - topLeftX, -topLeftY)); + + int stride, pixelStride; + uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, bitmapWidth, bitmapHeight, stride, pixelStride); + + jassert (pixelStride == 1); + AlphaBitmapRenderer renderer (pixels, stride); + edgeTable.iterate (renderer, 0, 0, bitmapWidth, bitmapHeight, 0); + + im->releasePixelDataReadWrite (pixels); + return im; + } + + FontGlyphAlphaMap (const FontGlyphAlphaMap&); + const FontGlyphAlphaMap& operator= (const FontGlyphAlphaMap&); +}; + + +//============================================================================== +class GlyphCache : private DeletedAtShutdown +{ +public: + GlyphCache() throw() + : accessCounter (0) + { + setCacheSize (120); + } + + ~GlyphCache() throw() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); + + //============================================================================== + void drawGlyph (LowLevelGraphicsContext& g, const Font& font, int glyphNumber, float x, float y) throw() + { + ++accessCounter; + + int oldestCounter = INT_MAX; + FontGlyphAlphaMap* oldest = 0; + + for (int i = glyphs.size(); --i >= 0;) + { + FontGlyphAlphaMap* const glyph = glyphs.getUnchecked (i); + + if (glyph->glyph == glyphNumber + && glyph->font == font) + { + ++hits; + glyph->lastAccessCount = accessCounter; + glyph->draw (g, x, y); + return; + } + + if (glyph->lastAccessCount <= oldestCounter) + { + oldestCounter = glyph->lastAccessCount; + oldest = glyph; + } + } + + ++misses; + + if (hits + misses > (glyphs.size() << 4)) + { + if (misses * 2 > hits) + setCacheSize (glyphs.size() + 32); + + hits = 0; + misses = 0; + oldest = glyphs.getUnchecked (0); + } + + jassert (oldest != 0); + oldest->lastAccessCount = accessCounter; + oldest->generate (font, glyphNumber); + oldest->draw (g, x, y); + } + + void setCacheSize (int num) throw() + { + if (glyphs.size() != num) + { + glyphs.clear(); + + while (--num >= 0) + glyphs.add (new FontGlyphAlphaMap()); + + hits = 0; + misses = 0; + } + } + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + OwnedArray glyphs; + int accessCounter, hits, misses; + + GlyphCache (const GlyphCache&); + const GlyphCache& operator= (const GlyphCache&); +}; + +juce_ImplementSingleton_SingleThreaded (GlyphCache); + + +//============================================================================== +void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y) +{ + if (font->height < 80.0f) + GlyphCache::getInstance()->drawGlyph (g, *this, glyphNumber, x, y); + else + renderGlyphIndirectly (g, glyphNumber, AffineTransform::translation (x, y)); +} + +void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform) +{ + Path p; + getTypeface()->getOutlineForGlyph (glyphNumber, p); + + g.fillPath (p, AffineTransform::scale (font->height * font->horizontalScale, font->height) + .followedBy (transform), + EdgeTable::Oversampling_16times); +} + + END_JUCE_NAMESPACE diff --git a/src/gui/graphics/fonts/juce_Font.h b/src/gui/graphics/fonts/juce_Font.h index 1cff092d39..bc3619feb8 100644 --- a/src/gui/graphics/fonts/juce_Font.h +++ b/src/gui/graphics/fonts/juce_Font.h @@ -30,6 +30,7 @@ #include "../../../text/juce_StringArray.h" #include "../../../containers/juce_ReferenceCountedObject.h" #include "../../../containers/juce_OwnedArray.h" +class LowLevelGraphicsContext; //============================================================================== @@ -85,12 +86,8 @@ public: /** Creates a copy of another Font object. */ Font (const Font& other) throw(); - /** Creates a font based on a typeface. - - The font object stores its own internal copy of the typeface, so you can safely - delete the one passed in after calling this. - */ - Font (const Typeface& typeface) throw(); + /** Creates a font for a typeface. */ + Font (const Typeface::Ptr& typeface) throw(); /** Creates a basic sans-serif font at a default height. @@ -114,9 +111,11 @@ public: e.g. "Arial", "Courier", etc. - This may also be set to Typeface::defaultTypefaceNameSans, Typeface::defaultTypefaceNameSerif, - or Typeface::defaultTypefaceNameMono, which are not actual platform-specific font names, but - are generic names that are used to represent the various default fonts. + This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, + but are generic names that are used to represent the various default fonts. + If you need to know the exact typeface name being used, you can call + Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. If a suitable font isn't found on the machine, it'll just use a default instead. */ @@ -126,13 +125,14 @@ public: e.g. "Arial", "Courier", etc. - Note that this may also be one of the values: Typeface::defaultTypefaceNameSans, - Typeface::defaultTypefaceNameSerif, or Typeface::defaultTypefaceNameMono, which are not actual - platform-specific font names, but are generic names that are used to represent the various - default fonts. If you need to know the exact typeface name being used, you can call + This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, + but are generic names that are used to represent the various default fonts. + + If you need to know the exact typeface name being used, you can call Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. */ - const String& getTypefaceName() const throw() { return typefaceName; } + const String& getTypefaceName() const throw() { return font->typefaceName; } //============================================================================== /** Returns a typeface name that represents the default sans-serif font. @@ -140,30 +140,36 @@ public: This is also the typeface that will be used when a font is created without specifying any typeface details. - Note that this method just returns the same value as Typeface::defaultTypefaceNameSans, - which is a generic placeholder string, and not a platform-specific font name. + Note that this method just returns a generic placeholder string that means "the default + sans-serif font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). - @see Typeface::defaultTypefaceNameSans, setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName, + @see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName */ - static const String getDefaultSansSerifFontName() throw() { return Typeface::defaultTypefaceNameSans; } + static const String getDefaultSansSerifFontName() throw(); /** Returns a typeface name that represents the default sans-serif font. - Note that this method just returns the same value as Typeface::defaultTypefaceNameSerif, - which is a generic placeholder string, and not a platform-specific font name. + Note that this method just returns a generic placeholder string that means "the default + serif font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). - @see Typeface::defaultTypefaceNameSerif, setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName */ - static const String getDefaultSerifFontName() throw() { return Typeface::defaultTypefaceNameSerif; } + static const String getDefaultSerifFontName() throw(); /** Returns a typeface name that represents the default sans-serif font. - Note that this method just returns the same value as Typeface::defaultTypefaceNameMono, - which is a generic placeholder string, and not a platform-specific font name. + Note that this method just returns a generic placeholder string that means "the default + monospaced font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). - @see Typeface::defaultTypefaceNameMono, setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName */ - static const String getDefaultMonospacedFontName() throw() { return Typeface::defaultTypefaceNameMono; } + static const String getDefaultMonospacedFontName() throw(); + + /** Returns the typeface names of the default fonts on the current platform. */ + static void getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw(); //============================================================================== /** Returns the total height of this font. @@ -173,7 +179,7 @@ public: @see setHeight, setHeightWithoutChangingWidth, getAscent */ - float getHeight() const throw() { return height; } + float getHeight() const throw() { return font->height; } /** Changes the font's height. @@ -211,7 +217,7 @@ public: @see FontStyleFlags */ - int getStyleFlags() const throw() { return styleFlags; } + int getStyleFlags() const throw() { return font->styleFlags; } /** Changes the font's style. @@ -252,7 +258,7 @@ public: @see setHorizontalScale */ - float getHorizontalScale() const throw() { return horizontalScale; } + float getHorizontalScale() const throw() { return font->horizontalScale; } /** Changes the font's kerning. @@ -271,24 +277,16 @@ public: A value of zero is normal spacing, positive values will spread the letters out more, and negative values make them closer together. */ - float getExtraKerningFactor() const throw() { return kerning; } + float getExtraKerningFactor() const throw() { return font->kerning; } //============================================================================== /** Changes all the font's characteristics with one call. */ - void setSizeAndStyle (const float newHeight, + void setSizeAndStyle (float newHeight, const int newStyleFlags, const float newHorizontalScale, const float newKerningAmount) throw(); - /** Resets this font's characteristics. - - This is basically like saying "myFont = Font();", because it resets the - typeface, size, style, etc to a default state. Not very useful to most - people, its raison d'etre is to help the Graphics class be more efficient. - */ - void resetToDefaultState() throw(); - //============================================================================== /** Returns the total width of a string as it would be drawn using this font. @@ -302,6 +300,31 @@ public: */ float getStringWidthFloat (const String& text) const throw(); + /** Returns the series of glyph numbers and their x offsets needed to represent a string. + + An extra x offset is added at the end of the run, to indicate where the right hand + edge of the last character is. + */ + void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) const throw(); + + //============================================================================== + /** Renders a glyph in a context without using methods other than the context's glyph-rendering + methods. + + For smaller fonts, this uses an internal cache of glyph images to speed things up, and renders + them using the context's image blending methods. For larger fonts, it gets the glyph's path + from the typeface and renders it as a shape. + + This method is primarily called by graphics contexts as a way of drawing a glyph if they can't do + it by native means. + */ + void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y); + + /** Renders a transformed glyph using path-filling techniques rather than calling a context's + actual glyph-rendering methods. + */ + void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform); + //============================================================================== /** Returns the typeface used by this font. @@ -348,11 +371,22 @@ private: friend class FontGlyphAlphaMap; friend class TypefaceCache; - String typefaceName; - float height, horizontalScale, kerning; - mutable float ascent; - int styleFlags; - mutable Typeface::Ptr typeface; + class SharedFontInternal : public ReferenceCountedObject + { + public: + SharedFontInternal (const String& typefaceName, const float height, const float horizontalScale, + const float kerning, const float ascent, const int styleFlags, + Typeface* const typeface) throw(); + SharedFontInternal (const SharedFontInternal& other) throw(); + + String typefaceName; + float height, horizontalScale, kerning, ascent; + int styleFlags; + Typeface::Ptr typeface; + }; + + ReferenceCountedObjectPtr font; + void dupeInternalIfShared() throw(); }; #endif // __JUCE_FONT_JUCEHEADER__ diff --git a/src/gui/graphics/fonts/juce_GlyphArrangement.cpp b/src/gui/graphics/fonts/juce_GlyphArrangement.cpp index ee546f3366..0030220141 100644 --- a/src/gui/graphics/fonts/juce_GlyphArrangement.cpp +++ b/src/gui/graphics/fonts/juce_GlyphArrangement.cpp @@ -34,275 +34,6 @@ BEGIN_JUCE_NAMESPACE #define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth)) -//============================================================================== -class FontGlyphAlphaMap -{ -public: - //============================================================================== - bool draw (const Graphics& g, float x, const float y) const throw() - { - if (bitmap1 == 0) - return false; - - x += xOrigin; - const float xFloor = floorf (x); - const int intX = (int) xFloor; - - g.drawImageAt (((x - xFloor) >= 0.5f && bitmap2 != 0) ? bitmap2 : bitmap1, - intX, (int) floorf (y + yOrigin), true); - - return true; - } - - - juce_UseDebuggingNewOperator - -private: - //============================================================================== - Image* bitmap1; - Image* bitmap2; - float xOrigin, yOrigin; - int lastAccessCount; - Typeface::Ptr typeface; - float height, horizontalScale; - juce_wchar character; - - friend class GlyphCache; - - FontGlyphAlphaMap() throw() - : bitmap1 (0), - bitmap2 (0), - lastAccessCount (0), - height (0), - horizontalScale (0), - character (0) - { - } - - ~FontGlyphAlphaMap() throw() - { - delete bitmap1; - delete bitmap2; - } - - class AlphaBitmapRenderer - { - uint8* const data; - const int stride; - uint8* lineStart; - - AlphaBitmapRenderer (const AlphaBitmapRenderer&); - const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&); - - public: - AlphaBitmapRenderer (uint8* const data_, - const int stride_) throw() - : data (data_), - stride (stride_) - { - } - - forcedinline void setEdgeTableYPos (const int y) throw() - { - lineStart = data + (stride * y); - } - - forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() - { - lineStart [x] = (uint8) alphaLevel; - } - - forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() - { - uint8* d = lineStart + x; - - while (--width >= 0) - *d++ = (uint8) alphaLevel; - } - }; - - Image* createAlphaMapFromPath (const Path& path, - float& topLeftX, float& topLeftY, - float xScale, float yScale, - const float subPixelOffsetX) throw() - { - Image* im = 0; - - float px, py, pw, ph; - path.getBounds (px, py, pw, ph); - - topLeftX = floorf (px * xScale); - topLeftY = floorf (py * yScale); - - int bitmapWidth = roundFloatToInt (pw * xScale) + 2; - int bitmapHeight = roundFloatToInt (ph * yScale) + 2; - - im = new Image (Image::SingleChannel, bitmapWidth, bitmapHeight, true); - - EdgeTable edgeTable (0, bitmapHeight, EdgeTable::Oversampling_16times); - - edgeTable.addPath (path, AffineTransform::scale (xScale, yScale) - .translated (subPixelOffsetX - topLeftX, -topLeftY)); - - int stride, pixelStride; - uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, bitmapWidth, bitmapHeight, stride, pixelStride); - - jassert (pixelStride == 1); - AlphaBitmapRenderer renderer (pixels, stride); - edgeTable.iterate (renderer, 0, 0, bitmapWidth, bitmapHeight, 0); - - im->releasePixelDataReadWrite (pixels); - - return im; - } - - void generate (Typeface* const face, - const juce_wchar character_, - const float fontHeight, - const float fontHorizontalScale) throw() - { - character = character_; - typeface = face; - height = fontHeight; - horizontalScale = fontHorizontalScale; - - const Path* const glyphPath = face->getOutlineForGlyph (character_); - - deleteAndZero (bitmap1); - deleteAndZero (bitmap2); - - const float fontHScale = fontHeight * fontHorizontalScale; - - if (glyphPath != 0 && ! glyphPath->isEmpty()) - { - bitmap1 = createAlphaMapFromPath (*glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.0f); - - if (fontHScale < 24.0f) - bitmap2 = createAlphaMapFromPath (*glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.5f); - } - else - { - xOrigin = yOrigin = 0; - } - } -}; - - -//============================================================================== -static const int defaultNumGlyphsToCache = 120; -class GlyphCache; -static GlyphCache* cacheInstance = 0; - -class GlyphCache : private DeletedAtShutdown -{ -public: - //============================================================================== - static GlyphCache* getInstance() throw() - { - if (cacheInstance == 0) - cacheInstance = new GlyphCache(); - - return cacheInstance; - } - - const FontGlyphAlphaMap& getGlyphFor (Typeface* const typeface, - const float fontHeight, - const float fontHorizontalScale, - const juce_wchar character) throw() - { - ++accessCounter; - - int oldestCounter = INT_MAX; - int oldestIndex = 0; - - for (int i = numGlyphs; --i >= 0;) - { - FontGlyphAlphaMap& g = glyphs[i]; - - if (g.character == character - && g.height == fontHeight - && g.typeface->hashCode() == typeface->hashCode() - && g.horizontalScale == fontHorizontalScale) - { - g.lastAccessCount = accessCounter; - ++hits; - return g; - } - - if (oldestCounter > g.lastAccessCount) - { - oldestCounter = g.lastAccessCount; - oldestIndex = i; - } - } - - ++misses; - - if (hits + misses > (numGlyphs << 4)) - { - if (misses * 2 > hits) - setCacheSize (numGlyphs + 32); - - hits = 0; - misses = 0; - oldestIndex = 0; - } - - FontGlyphAlphaMap& oldest = glyphs [oldestIndex]; - oldest.lastAccessCount = accessCounter; - - oldest.generate (typeface, - character, - fontHeight, - fontHorizontalScale); - - return oldest; - } - - void setCacheSize (const int num) throw() - { - if (numGlyphs != num) - { - numGlyphs = num; - - if (glyphs != 0) - delete[] glyphs; - - glyphs = new FontGlyphAlphaMap [numGlyphs]; - - hits = 0; - misses = 0; - } - } - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - FontGlyphAlphaMap* glyphs; - int numGlyphs, accessCounter; - int hits, misses; - - GlyphCache() throw() - : glyphs (0), - numGlyphs (0), - accessCounter (0) - { - setCacheSize (defaultNumGlyphsToCache); - } - - ~GlyphCache() throw() - { - delete[] glyphs; - - jassert (cacheInstance == this); - cacheInstance = 0; - } - - GlyphCache (const GlyphCache&); - const GlyphCache& operator= (const GlyphCache&); -}; - //============================================================================== PositionedGlyph::PositionedGlyph() throw() @@ -311,47 +42,38 @@ PositionedGlyph::PositionedGlyph() throw() void PositionedGlyph::draw (const Graphics& g) const throw() { - if (! glyphInfo->isWhitespace()) + if (! isWhitespace()) { - if (fontHeight < 100.0f && fontHeight > 0.1f && ! g.isVectorDevice()) - { - const FontGlyphAlphaMap& alphaMap - = GlyphCache::getInstance()->getGlyphFor (glyphInfo->getTypeface(), - fontHeight, - fontHorizontalScale, - getCharacter()); - - alphaMap.draw (g, x, y); - } - else - { - // that's a bit of a dodgy size, isn't it?? - jassert (fontHeight > 0.0f && fontHeight < 4000.0f); - - draw (g, AffineTransform::identity); - } + g.getInternalContext()->setFont (font); + g.getInternalContext()->drawGlyph (glyph, x, y); } } void PositionedGlyph::draw (const Graphics& g, const AffineTransform& transform) const throw() { - if (! glyphInfo->isWhitespace()) + if (! isWhitespace()) { - g.fillPath (glyphInfo->getPath(), - AffineTransform::scale (fontHeight * fontHorizontalScale, fontHeight) - .translated (x, y) - .followedBy (transform)); + g.getInternalContext()->setFont (font); + g.getInternalContext()->drawGlyph (glyph, AffineTransform::translation (x, y) + .followedBy (transform)); } } void PositionedGlyph::createPath (Path& path) const throw() { - if (! glyphInfo->isWhitespace()) + if (! isWhitespace()) { - path.addPath (glyphInfo->getPath(), - AffineTransform::scale (fontHeight * fontHorizontalScale, fontHeight) - .translated (x, y)); + Typeface* const t = font.getTypeface(); + + if (t != 0) + { + Path p; + t->getOutlineForGlyph (glyph, p); + + path.addPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()) + .translated (x, y)); + } } } @@ -359,14 +81,21 @@ bool PositionedGlyph::hitTest (float px, float py) const throw() { if (px >= getLeft() && px < getRight() && py >= getTop() && py < getBottom() - && fontHeight > 0.0f - && ! glyphInfo->isWhitespace()) + && ! isWhitespace()) { - AffineTransform::translation (-x, -y) - .scaled (1.0f / (fontHeight * fontHorizontalScale), 1.0f / fontHeight) - .transformPoint (px, py); + Typeface* const t = font.getTypeface(); - return glyphInfo->getPath().contains (px, py); + if (t != 0) + { + Path p; + t->getOutlineForGlyph (glyph, p); + + AffineTransform::translation (-x, -y) + .scaled (1.0f / (font.getHeight() * font.getHorizontalScale()), 1.0f / font.getHeight()) + .transformPoint (px, py); + + return p.contains (px, py); + } } return false; @@ -382,16 +111,11 @@ void PositionedGlyph::moveBy (const float deltaX, //============================================================================== GlyphArrangement::GlyphArrangement() throw() - : numGlyphs (0), - numAllocated (0), - glyphs (0) + : glyphs (128) { } GlyphArrangement::GlyphArrangement (const GlyphArrangement& other) throw() - : numGlyphs (0), - numAllocated (0), - glyphs (0) { addGlyphArrangement (other); } @@ -409,97 +133,33 @@ const GlyphArrangement& GlyphArrangement::operator= (const GlyphArrangement& oth GlyphArrangement::~GlyphArrangement() throw() { - clear(); - juce_free (glyphs); } //============================================================================== -void GlyphArrangement::ensureNumGlyphsAllocated (const int minGlyphs) throw() -{ - if (numAllocated <= minGlyphs) - { - numAllocated = minGlyphs + 2; - - if (glyphs == 0) - glyphs = (PositionedGlyph*) juce_malloc (numAllocated * sizeof (PositionedGlyph)); - else - glyphs = (PositionedGlyph*) juce_realloc (glyphs, numAllocated * sizeof (PositionedGlyph)); - } -} - -void GlyphArrangement::incGlyphRefCount (const int i) const throw() -{ - jassert (((unsigned int) i) < (unsigned int) numGlyphs); - - if (glyphs[i].glyphInfo != 0 && glyphs[i].glyphInfo->getTypeface() != 0) - glyphs[i].glyphInfo->getTypeface()->incReferenceCount(); -} - -void GlyphArrangement::decGlyphRefCount (const int i) const throw() -{ - if (glyphs[i].glyphInfo != 0 && glyphs[i].glyphInfo->getTypeface() != 0) - glyphs[i].glyphInfo->getTypeface()->decReferenceCount(); -} - void GlyphArrangement::clear() throw() { - for (int i = numGlyphs; --i >= 0;) - decGlyphRefCount (i); - - numGlyphs = 0; + glyphs.clear(); } PositionedGlyph& GlyphArrangement::getGlyph (const int index) const throw() { - jassert (((unsigned int) index) < (unsigned int) numGlyphs); + jassert (((unsigned int) index) < (unsigned int) glyphs.size()); - return glyphs [index]; + return *glyphs [index]; } //============================================================================== void GlyphArrangement::addGlyphArrangement (const GlyphArrangement& other) throw() { - ensureNumGlyphsAllocated (numGlyphs + other.numGlyphs); + glyphs.ensureStorageAllocated (glyphs.size() + other.glyphs.size()); - memcpy (glyphs + numGlyphs, other.glyphs, - other.numGlyphs * sizeof (PositionedGlyph)); - - for (int i = other.numGlyphs; --i >= 0;) - incGlyphRefCount (numGlyphs++); -} - -void GlyphArrangement::removeLast() throw() -{ - if (numGlyphs > 0) - decGlyphRefCount (--numGlyphs); + for (int i = 0; i < other.glyphs.size(); ++i) + glyphs.add (new PositionedGlyph (*other.glyphs.getUnchecked (i))); } void GlyphArrangement::removeRangeOfGlyphs (int startIndex, const int num) throw() { - jassert (startIndex >= 0); - - if (startIndex < 0) - startIndex = 0; - - if (num < 0 || startIndex + num >= numGlyphs) - { - while (numGlyphs > startIndex) - removeLast(); - } - else if (num > 0) - { - int i; - for (i = startIndex; i < startIndex + num; ++i) - decGlyphRefCount (i); - - for (i = numGlyphs - (startIndex + num); --i >= 0;) - { - glyphs [startIndex] = glyphs [startIndex + num]; - ++startIndex; - } - - numGlyphs -= num; - } + glyphs.removeRange (startIndex, num); } //============================================================================== @@ -520,59 +180,41 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font, const float maxWidthPixels, const bool useEllipsis) throw() { - const int textLen = text.length(); + int textLen = text.length(); if (textLen > 0) { - ensureNumGlyphsAllocated (numGlyphs + textLen + 3); // extra chars for ellipsis - - Typeface* const typeface = font.getTypeface(); - const float fontHeight = font.getHeight(); - const float ascent = font.getAscent(); - const float fontHorizontalScale = font.getHorizontalScale(); - const float heightTimesScale = fontHorizontalScale * fontHeight; - const float kerningFactor = font.getExtraKerningFactor(); - const float startX = xOffset; + Array newGlyphs; + Array xOffsets; + font.getGlyphPositions (text, newGlyphs, xOffsets); const juce_wchar* const unicodeText = (const juce_wchar*) text; + textLen = jmin (textLen, newGlyphs.size()); for (int i = 0; i < textLen; ++i) { - const TypefaceGlyphInfo* const glyph = typeface->getGlyph (unicodeText[i]); + const float thisX = xOffsets.getUnchecked (i); + const float nextX = xOffsets.getUnchecked (i + 1); - if (glyph != 0) + if (nextX > maxWidthPixels + 1.0f) { - jassert (numAllocated > numGlyphs); + // curtail the string if it's too wide.. + if (useEllipsis && textLen > 3 && glyphs.size() >= 3) + appendEllipsis (font, xOffset + maxWidthPixels); - ensureNumGlyphsAllocated (numGlyphs); - PositionedGlyph& pg = glyphs [numGlyphs]; - pg.glyphInfo = glyph; - pg.x = xOffset; - pg.y = yOffset; - pg.w = heightTimesScale * glyph->getHorizontalSpacing (0); - pg.fontHeight = fontHeight; - pg.fontAscent = ascent; - pg.fontHorizontalScale = fontHorizontalScale; - pg.isUnderlined = font.isUnderlined(); + break; + } + else + { + PositionedGlyph* const pg = new PositionedGlyph(); + pg->x = xOffset + thisX; + pg->y = yOffset; + pg->w = nextX - thisX; + pg->font = font; + pg->glyph = newGlyphs.getUnchecked(i); + pg->character = unicodeText[i]; - xOffset += heightTimesScale * (kerningFactor + glyph->getHorizontalSpacing (unicodeText [i + 1])); - - if (xOffset - startX > maxWidthPixels + 1.0f) - { - // curtail the string if it's too wide.. - - if (useEllipsis && textLen > 3 && numGlyphs >= 3) - appendEllipsis (font, startX + maxWidthPixels); - - break; - } - else - { - if (glyph->getTypeface() != 0) - glyph->getTypeface()->incReferenceCount(); - - ++numGlyphs; - } + glyphs.add (pg); } } } @@ -580,57 +222,48 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font, void GlyphArrangement::appendEllipsis (const Font& font, const float maxXPixels) throw() { - const TypefaceGlyphInfo* const dotGlyph = font.getTypeface()->getGlyph (T('.')); + //const TypefaceGlyphInfo* const dotGlyph = font.getTypeface()->getGlyph (T('.')); - if (dotGlyph != 0) + if (/*dotGlyph != 0 &&*/ glyphs.size() > 0) { - if (numGlyphs > 0) + //PositionedGlyph* glyph = glyphs.getLast(); + //const float fontHeight = glyph->font.getHeight(); + //const float fontHorizontalScale = glyph->font.getHorizontalScale(); + + Array dotGlyphs; + Array dotXs; + font.getGlyphPositions (T(".."), dotGlyphs, dotXs); + + const float dx = dotXs[1]; + //fontHeight * fontHorizontalScale + // * (font.getExtraKerningFactor() + dotGlyph->getHorizontalSpacing (T('.'))); + + float xOffset = 0.0f, yOffset = 0.0f; + + for (int dotPos = 3; --dotPos >= 0 && glyphs.size() > 0;) { - PositionedGlyph& glyph = glyphs [numGlyphs - 1]; - const float fontHeight = glyph.fontHeight; - const float fontHorizontalScale = glyph.fontHorizontalScale; - const float fontAscent = glyph.fontAscent; + const PositionedGlyph* pg = glyphs.getUnchecked (glyphs.size() - 1); + xOffset = pg->x; + yOffset = pg->y; - const float dx = fontHeight * fontHorizontalScale - * (font.getExtraKerningFactor() + dotGlyph->getHorizontalSpacing (T('.'))); + glyphs.removeLast(); - float xOffset = 0.0f, yOffset = 0.0f; + if (xOffset + dx * 3 <= maxXPixels) + break; + } - for (int dotPos = 3; --dotPos >= 0 && numGlyphs > 0;) - { - removeLast(); + for (int i = 3; --i >= 0;) + { + PositionedGlyph* const pg = new PositionedGlyph(); + pg->x = xOffset; + pg->y = yOffset; + pg->w = dx; + pg->font = font; + pg->character = '.'; + pg->glyph = dotGlyphs.getFirst(); + glyphs.add (pg); - jassert (numAllocated > numGlyphs); - PositionedGlyph& pg = glyphs [numGlyphs]; - xOffset = pg.x; - yOffset = pg.y; - - if (numGlyphs == 0 || xOffset + dx * 3 <= maxXPixels) - break; - } - - for (int i = 3; --i >= 0;) - { - jassert (numAllocated > numGlyphs); - - ensureNumGlyphsAllocated (numGlyphs); - PositionedGlyph& pg = glyphs [numGlyphs]; - pg.glyphInfo = dotGlyph; - pg.x = xOffset; - pg.y = yOffset; - pg.w = dx; - pg.fontHeight = fontHeight; - pg.fontAscent = fontAscent; - pg.fontHorizontalScale = fontHorizontalScale; - pg.isUnderlined = font.isUnderlined(); - - xOffset += dx; - - if (dotGlyph->getTypeface() != 0) - dotGlyph->getTypeface()->incReferenceCount(); - - ++numGlyphs; - } + xOffset += dx; } } } @@ -641,40 +274,42 @@ void GlyphArrangement::addJustifiedText (const Font& font, const float maxLineWidth, const Justification& horizontalLayout) throw() { - int lineStartIndex = numGlyphs; + int lineStartIndex = glyphs.size(); addLineOfText (font, text, x, y); const float originalY = y; - while (lineStartIndex < numGlyphs) + while (lineStartIndex < glyphs.size()) { int i = lineStartIndex; - if (glyphs[i].getCharacter() != T('\n') && glyphs[i].getCharacter() != T('\r')) + if (glyphs.getUnchecked(i)->getCharacter() != T('\n') + && glyphs.getUnchecked(i)->getCharacter() != T('\r')) ++i; - const float lineMaxX = glyphs [lineStartIndex].getLeft() + maxLineWidth; + const float lineMaxX = glyphs.getUnchecked (lineStartIndex)->getLeft() + maxLineWidth; int lastWordBreakIndex = -1; - while (i < numGlyphs) + while (i < glyphs.size()) { - PositionedGlyph& pg = glyphs[i]; - const juce_wchar c = pg.getCharacter(); + const PositionedGlyph* pg = glyphs.getUnchecked (i); + const juce_wchar c = pg->getCharacter(); if (c == T('\r') || c == T('\n')) { ++i; - if (c == T('\r') && i < numGlyphs && glyphs [i].getCharacter() == T('\n')) + if (c == T('\r') && i < glyphs.size() + && glyphs.getUnchecked(i)->getCharacter() == T('\n')) ++i; break; } - else if (pg.isWhitespace()) + else if (pg->isWhitespace()) { lastWordBreakIndex = i + 1; } - else if (SHOULD_WRAP (pg.getRight(), lineMaxX)) + else if (SHOULD_WRAP (pg->getRight(), lineMaxX)) { if (lastWordBreakIndex >= 0) i = lastWordBreakIndex; @@ -685,14 +320,14 @@ void GlyphArrangement::addJustifiedText (const Font& font, ++i; } - const float currentLineStartX = glyphs [lineStartIndex].getLeft(); + const float currentLineStartX = glyphs.getUnchecked (lineStartIndex)->getLeft(); float currentLineEndX = currentLineStartX; for (int j = i; --j >= lineStartIndex;) { - if (! glyphs[j].isWhitespace()) + if (! glyphs.getUnchecked (j)->isWhitespace()) { - currentLineEndX = glyphs[j].getRight(); + currentLineEndX = glyphs.getUnchecked (j)->getRight(); break; } } @@ -743,17 +378,22 @@ void GlyphArrangement::addFittedText (const Font& f, ga.moveRangeOfGlyphs (0, -1, 0.0f, dy); - addGlyphArrangement (ga); + glyphs.ensureStorageAllocated (glyphs.size() + ga.glyphs.size()); + for (int i = 0; i < ga.glyphs.size(); ++i) + glyphs.add (ga.glyphs.getUnchecked (i)); + + ga.glyphs.clear (false); return; } - int startIndex = numGlyphs; + int startIndex = glyphs.size(); addLineOfText (f, text.trim(), x, y); - if (numGlyphs > startIndex) + if (glyphs.size() > startIndex) { - float lineWidth = glyphs[numGlyphs - 1].getRight() - glyphs[startIndex].getLeft(); + float lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() + - glyphs.getUnchecked (startIndex)->getLeft(); if (lineWidth <= 0) return; @@ -762,26 +402,26 @@ void GlyphArrangement::addFittedText (const Font& f, { if (lineWidth > width) { - stretchRangeOfGlyphs (startIndex, numGlyphs - startIndex, + stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex, width / lineWidth); } - justifyGlyphs (startIndex, numGlyphs - startIndex, + justifyGlyphs (startIndex, glyphs.size() - startIndex, x, y, width, height, layout); } else if (maximumLines <= 1) { const float ratio = jmax (minimumHorizontalScale, width / lineWidth); - stretchRangeOfGlyphs (startIndex, numGlyphs - startIndex, ratio); + stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex, ratio); - while (numGlyphs > 0 && glyphs [numGlyphs - 1].x + glyphs [numGlyphs - 1].w >= x + width) - removeLast(); + while (glyphs.size() > 0 && glyphs.getUnchecked (glyphs.size() - 1)->getRight() >= x + width) + glyphs.removeLast(); appendEllipsis (f, x + width); - justifyGlyphs (startIndex, numGlyphs - startIndex, + justifyGlyphs (startIndex, glyphs.size() - startIndex, x, y, width, height, layout); } else @@ -802,7 +442,7 @@ void GlyphArrangement::addFittedText (const Font& f, { ++numLines; - const float newFontHeight = height / (float)numLines; + const float newFontHeight = height / (float) numLines; if (newFontHeight < 8.0f) break; @@ -811,12 +451,13 @@ void GlyphArrangement::addFittedText (const Font& f, { font.setHeight (newFontHeight); - while (numGlyphs > startIndex) - removeLast(); + while (glyphs.size() > startIndex) + glyphs.removeLast(); addLineOfText (font, txt, x, y); - lineWidth = glyphs[numGlyphs - 1].getRight() - glyphs[startIndex].getLeft(); + lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() + - glyphs.getUnchecked (startIndex)->getLeft(); } if (numLines > lineWidth / width) @@ -834,11 +475,11 @@ void GlyphArrangement::addFittedText (const Font& f, { int i = startIndex; lastLineStartIndex = i; - float lineStartX = glyphs[startIndex].getLeft(); + float lineStartX = glyphs.getUnchecked (startIndex)->getLeft(); - while (i < numGlyphs) + while (i < glyphs.size()) { - lineWidth = (glyphs[i].getRight() - lineStartX); + lineWidth = (glyphs.getUnchecked (i)->getRight() - lineStartX); if (lineWidth > widthPerLine) { @@ -846,12 +487,12 @@ void GlyphArrangement::addFittedText (const Font& f, // good place to break it.. const int searchStartIndex = i; - while (i < numGlyphs) + while (i < glyphs.size()) { - if ((glyphs[i].getRight() - lineStartX) * minimumHorizontalScale < width) + if ((glyphs.getUnchecked (i)->getRight() - lineStartX) * minimumHorizontalScale < width) { - if (glyphs[i].isWhitespace() - || glyphs[i].getCharacter() == T('-')) + if (glyphs.getUnchecked (i)->isWhitespace() + || glyphs.getUnchecked (i)->getCharacter() == T('-')) { ++i; break; @@ -864,8 +505,8 @@ void GlyphArrangement::addFittedText (const Font& f, for (int back = 1; back < jmin (5, i - startIndex - 1); ++back) { - if (glyphs[i - back].isWhitespace() - || glyphs[i - back].getCharacter() == T('-')) + if (glyphs.getUnchecked (i - back)->isWhitespace() + || glyphs.getUnchecked (i - back)->getCharacter() == T('-')) { i -= back - 1; break; @@ -885,18 +526,18 @@ void GlyphArrangement::addFittedText (const Font& f, } int wsStart = i; - while (wsStart > 0 && glyphs[wsStart - 1].isWhitespace()) + while (wsStart > 0 && glyphs.getUnchecked (wsStart - 1)->isWhitespace()) --wsStart; int wsEnd = i; - while (wsEnd < numGlyphs && glyphs[wsEnd].isWhitespace()) + while (wsEnd < glyphs.size() && glyphs.getUnchecked (wsEnd)->isWhitespace()) ++wsEnd; removeRangeOfGlyphs (wsStart, wsEnd - wsStart); i = jmax (wsStart, startIndex + 1); - lineWidth = glyphs[i - 1].getRight() - lineStartX; + lineWidth = glyphs.getUnchecked (i - 1)->getRight() - lineStartX; if (lineWidth > width) { @@ -911,21 +552,20 @@ void GlyphArrangement::addFittedText (const Font& f, startIndex = i; lineY += font.getHeight(); - if (startIndex >= numGlyphs) + if (startIndex >= glyphs.size()) break; } - if (startIndex < numGlyphs) + if (startIndex < glyphs.size()) { - while (numGlyphs > startIndex) - removeLast(); + glyphs.removeRange (startIndex, glyphs.size()); if (startIndex - originalStartIndex > 4) { - const float lineStartX = glyphs[lastLineStartIndex].getLeft(); + const float lineStartX = glyphs.getUnchecked (lastLineStartIndex)->getLeft(); appendEllipsis (font, lineStartX + width); - lineWidth = glyphs[startIndex - 1].getRight() - lineStartX; + lineWidth = glyphs.getUnchecked (startIndex - 1)->getRight() - lineStartX; if (lineWidth > width) { @@ -938,7 +578,7 @@ void GlyphArrangement::addFittedText (const Font& f, layout.getOnlyHorizontalFlags() | Justification::verticallyCentred); } - startIndex = numGlyphs; + startIndex = glyphs.size(); } justifyGlyphs (originalStartIndex, startIndex - originalStartIndex, @@ -955,14 +595,11 @@ void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, if (dx != 0.0f || dy != 0.0f) { - if (num < 0 || startIndex + num > numGlyphs) - num = numGlyphs - startIndex; + if (num < 0 || startIndex + num > glyphs.size()) + num = glyphs.size() - startIndex; while (--num >= 0) - { - jassert (((unsigned int) startIndex) <= (unsigned int) numGlyphs); - glyphs [startIndex++].moveBy (dx, dy); - } + glyphs.getUnchecked (startIndex++)->moveBy (dx, dy); } } @@ -971,21 +608,20 @@ void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num, { jassert (startIndex >= 0); - if (num < 0 || startIndex + num > numGlyphs) - num = numGlyphs - startIndex; + if (num < 0 || startIndex + num > glyphs.size()) + num = glyphs.size() - startIndex; if (num > 0) { - const float xAnchor = glyphs[startIndex].getLeft(); + const float xAnchor = glyphs.getUnchecked (startIndex)->getLeft(); while (--num >= 0) { - jassert (((unsigned int) startIndex) <= (unsigned int) numGlyphs); - PositionedGlyph& pg = glyphs[startIndex++]; + PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); - pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor; - pg.fontHorizontalScale *= horizontalScaleFactor; - pg.w *= horizontalScaleFactor; + pg->x = xAnchor + (pg->x - xAnchor) * horizontalScaleFactor; + pg->font.setHorizontalScale (pg->font.getHorizontalScale() * horizontalScaleFactor); + pg->w *= horizontalScaleFactor; } } } @@ -999,8 +635,8 @@ void GlyphArrangement::getBoundingBox (int startIndex, int num, { jassert (startIndex >= 0); - if (num < 0 || startIndex + num > numGlyphs) - num = numGlyphs - startIndex; + if (num < 0 || startIndex + num > glyphs.size()) + num = glyphs.size() - startIndex; left = 0.0f; top = 0.0f; @@ -1010,24 +646,24 @@ void GlyphArrangement::getBoundingBox (int startIndex, int num, while (--num >= 0) { - const PositionedGlyph& pg = glyphs [startIndex++]; + const PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); - if (includeWhitespace || ! pg.isWhitespace()) + if (includeWhitespace || ! pg->isWhitespace()) { if (isFirst) { isFirst = false; - left = pg.getLeft(); - top = pg.getTop(); - right = pg.getRight(); - bottom = pg.getBottom(); + left = pg->getLeft(); + top = pg->getTop(); + right = pg->getRight(); + bottom = pg->getBottom(); } else { - left = jmin (left, pg.getLeft()); - top = jmin (top, pg.getTop()); - right = jmax (right, pg.getRight()); - bottom = jmax (bottom, pg.getBottom()); + left = jmin (left, pg->getLeft()); + top = jmin (top, pg->getTop()); + right = jmax (right, pg->getRight()); + bottom = jmax (bottom, pg->getBottom()); } } } @@ -1041,7 +677,7 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, { jassert (num >= 0 && startIndex >= 0); - if (numGlyphs > 0 && num > 0) + if (glyphs.size() > 0 && num > 0) { float left, top, right, bottom; getBoundingBox (startIndex, num, left, top, right, bottom, @@ -1073,12 +709,12 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, if (justification.testFlags (Justification::horizontallyJustified)) { int lineStart = 0; - float baseY = glyphs [startIndex].getBaselineY(); + float baseY = glyphs.getUnchecked (startIndex)->getBaselineY(); int i; for (i = 0; i < num; ++i) { - const float glyphY = glyphs [startIndex + i].getBaselineY(); + const float glyphY = glyphs.getUnchecked (startIndex + i)->getBaselineY(); if (glyphY != baseY) { @@ -1097,16 +733,16 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, void GlyphArrangement::spreadOutLine (const int start, const int num, const float targetWidth) throw() { - if (start + num < numGlyphs - && glyphs [start + num - 1].getCharacter() != T('\r') - && glyphs [start + num - 1].getCharacter() != T('\n')) + if (start + num < glyphs.size() + && glyphs.getUnchecked (start + num - 1)->getCharacter() != T('\r') + && glyphs.getUnchecked (start + num - 1)->getCharacter() != T('\n')) { int numSpaces = 0; int spacesAtEnd = 0; for (int i = 0; i < num; ++i) { - if (glyphs [start + i].isWhitespace()) + if (glyphs.getUnchecked (start + i)->isWhitespace()) { ++spacesAtEnd; ++numSpaces; @@ -1121,8 +757,8 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa if (numSpaces > 0) { - const float startX = glyphs [start].getLeft(); - const float endX = glyphs [start + num - 1 - spacesAtEnd].getRight(); + const float startX = glyphs.getUnchecked (start)->getLeft(); + const float endX = glyphs.getUnchecked (start + num - 1 - spacesAtEnd)->getRight(); const float extraPaddingBetweenWords = (targetWidth - (endX - startX)) / (float) numSpaces; @@ -1131,9 +767,9 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa for (int i = 0; i < num; ++i) { - glyphs [start + i].moveBy (deltaX, 0.0); + glyphs.getUnchecked (start + i)->moveBy (deltaX, 0.0); - if (glyphs [start + i].isWhitespace()) + if (glyphs.getUnchecked (start + i)->isWhitespace()) deltaX += extraPaddingBetweenWords; } } @@ -1143,74 +779,64 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa //============================================================================== void GlyphArrangement::draw (const Graphics& g) const throw() { - for (int i = 0; i < numGlyphs; ++i) + for (int i = 0; i < glyphs.size(); ++i) { - glyphs[i].draw (g); + const PositionedGlyph* const pg = glyphs.getUnchecked(i); - if (glyphs[i].isUnderlined) + if (pg->font.isUnderlined()) { - const float lineThickness = (glyphs[i].fontHeight - glyphs[i].fontAscent) * 0.3f; + const float lineThickness = (pg->font.getDescent()) * 0.3f; - juce_wchar nextChar = 0; + float nextX = pg->x + pg->w; - if (i < numGlyphs - 1 - && glyphs[i + 1].y == glyphs[i].y) - { - nextChar = glyphs[i + 1].glyphInfo->getCharacter(); - } + if (i < glyphs.size() - 1 && glyphs.getUnchecked (i + 1)->y == pg->y) + nextX = glyphs.getUnchecked (i + 1)->x; - g.fillRect (glyphs[i].x, - glyphs[i].y + lineThickness * 2.0f, - glyphs[i].fontHeight - * glyphs[i].fontHorizontalScale - * glyphs[i].glyphInfo->getHorizontalSpacing (nextChar), - lineThickness); + g.fillRect (pg->x, pg->y + lineThickness * 2.0f, + nextX - pg->x, lineThickness); } + + pg->draw (g); } } void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const throw() { - for (int i = 0; i < numGlyphs; ++i) + for (int i = 0; i < glyphs.size(); ++i) { - glyphs[i].draw (g, transform); + const PositionedGlyph* const pg = glyphs.getUnchecked(i); - if (glyphs[i].isUnderlined) + if (pg->font.isUnderlined()) { - const float lineThickness = (glyphs[i].fontHeight - glyphs[i].fontAscent) * 0.3f; + const float lineThickness = (pg->font.getDescent()) * 0.3f; - juce_wchar nextChar = 0; + float nextX = pg->x + pg->w; - if (i < numGlyphs - 1 - && glyphs[i + 1].y == glyphs[i].y) - { - nextChar = glyphs[i + 1].glyphInfo->getCharacter(); - } + if (i < glyphs.size() - 1 && glyphs.getUnchecked (i + 1)->y == pg->y) + nextX = glyphs.getUnchecked (i + 1)->x; Path p; - p.addLineSegment (glyphs[i].x, - glyphs[i].y + lineThickness * 2.5f, - glyphs[i].x + glyphs[i].fontHeight - * glyphs[i].fontHorizontalScale - * glyphs[i].glyphInfo->getHorizontalSpacing (nextChar), - glyphs[i].y + lineThickness * 2.5f, + p.addLineSegment (pg->x, pg->y + lineThickness * 2.0f, + nextX, pg->y + lineThickness * 2.0f, lineThickness); g.fillPath (p, transform); } + + pg->draw (g, transform); } } void GlyphArrangement::createPath (Path& path) const throw() { - for (int i = 0; i < numGlyphs; ++i) - glyphs[i].createPath (path); + for (int i = 0; i < glyphs.size(); ++i) + glyphs.getUnchecked (i)->createPath (path); } int GlyphArrangement::findGlyphIndexAt (float x, float y) const throw() { - for (int i = 0; i < numGlyphs; ++i) - if (glyphs[i].hitTest (x, y)) + for (int i = 0; i < glyphs.size(); ++i) + if (glyphs.getUnchecked (i)->hitTest (x, y)) return i; return -1; diff --git a/src/gui/graphics/fonts/juce_GlyphArrangement.h b/src/gui/graphics/fonts/juce_GlyphArrangement.h index 410ab2fd5e..270d578a56 100644 --- a/src/gui/graphics/fonts/juce_GlyphArrangement.h +++ b/src/gui/graphics/fonts/juce_GlyphArrangement.h @@ -42,9 +42,9 @@ class JUCE_API PositionedGlyph public: //============================================================================== /** Returns the character the glyph represents. */ - juce_wchar getCharacter() const throw() { return glyphInfo->getCharacter(); } + juce_wchar getCharacter() const throw() { return character; } /** Checks whether the glyph is actually empty. */ - bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (glyphInfo->getCharacter()); } + bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (character); } /** Returns the position of the glyph's left-hand edge. */ float getLeft() const throw() { return x; } @@ -53,9 +53,9 @@ public: /** Returns the y position of the glyph's baseline. */ float getBaselineY() const throw() { return y; } /** Returns the y position of the top of the glyph. */ - float getTop() const throw() { return y - fontAscent; } + float getTop() const throw() { return y - font.getAscent(); } /** Returns the y position of the bottom of the glyph. */ - float getBottom() const throw() { return y + fontHeight - fontAscent; } + float getBottom() const throw() { return y + font.getDescent(); } //============================================================================== /** Shifts the glyph's position by a relative amount. */ @@ -85,9 +85,9 @@ private: //============================================================================== friend class GlyphArrangement; float x, y, w; - float fontHeight, fontAscent, fontHorizontalScale; - bool isUnderlined; - const TypefaceGlyphInfo* glyphInfo; + Font font; + juce_wchar character; + int glyph; PositionedGlyph() throw(); }; @@ -124,7 +124,7 @@ public: //============================================================================== /** Returns the total number of glyphs in the arrangement. */ - int getNumGlyphs() const throw() { return numGlyphs; } + int getNumGlyphs() const throw() { return glyphs.size(); } /** Returns one of the glyphs from the arrangement. @@ -310,16 +310,9 @@ public: juce_UseDebuggingNewOperator private: - int numGlyphs, numAllocated; - PositionedGlyph* glyphs; + OwnedArray glyphs; - void ensureNumGlyphsAllocated (int minGlyphs) throw(); - void removeLast() throw(); void appendEllipsis (const Font& font, const float maxXPixels) throw(); - - void incGlyphRefCount (const int index) const throw(); - void decGlyphRefCount (const int index) const throw(); - void spreadOutLine (const int start, const int numGlyphs, const float targetWidth) throw(); }; diff --git a/src/gui/graphics/fonts/juce_Typeface.cpp b/src/gui/graphics/fonts/juce_Typeface.cpp index e2a415120c..7716c4b375 100644 --- a/src/gui/graphics/fonts/juce_Typeface.cpp +++ b/src/gui/graphics/fonts/juce_Typeface.cpp @@ -27,177 +27,93 @@ BEGIN_JUCE_NAMESPACE - #include "juce_Typeface.h" #include "juce_Font.h" -#include "../../components/lookandfeel/juce_LookAndFeel.h" #include "../../../io/streams/juce_GZIPDecompressorInputStream.h" #include "../../../io/streams/juce_GZIPCompressorOutputStream.h" #include "../../../io/streams/juce_BufferedInputStream.h" -#include "../../../utilities/juce_DeletedAtShutdown.h" //============================================================================== -TypefaceGlyphInfo::TypefaceGlyphInfo (const juce_wchar character_, - const Path& shape, - const float horizontalSeparation, - Typeface* const typeface_) throw() - : character (character_), - path (shape), - width (horizontalSeparation), - typeface (typeface_) +Typeface::Typeface (const String& name_) throw() + : name (name_) { } -TypefaceGlyphInfo::~TypefaceGlyphInfo() throw() -{ -} - -float TypefaceGlyphInfo::getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw() -{ - if (subsequentCharacter != 0) - { - const KerningPair* const pairs = (const KerningPair*) kerningPairs.getData(); - const int numPairs = getNumKerningPairs(); - - for (int i = 0; i < numPairs; ++i) - if (pairs [i].character2 == subsequentCharacter) - return width + pairs [i].kerningAmount; - } - - return width; -} - -void TypefaceGlyphInfo::addKerningPair (const juce_wchar subsequentCharacter, - const float extraKerningAmount) throw() -{ - const int numPairs = getNumKerningPairs(); - kerningPairs.setSize ((numPairs + 1) * sizeof (KerningPair)); - - KerningPair& p = getKerningPair (numPairs); - p.character2 = subsequentCharacter; - p.kerningAmount = extraKerningAmount; -} - -TypefaceGlyphInfo::KerningPair& TypefaceGlyphInfo::getKerningPair (const int index) const throw() -{ - return ((KerningPair*) kerningPairs.getData()) [index]; -} - -int TypefaceGlyphInfo::getNumKerningPairs() const throw() -{ - return kerningPairs.getSize() / sizeof (KerningPair); -} - - -//============================================================================== -const tchar* Typeface::defaultTypefaceNameSans = T(""); -const tchar* Typeface::defaultTypefaceNameSerif = T(""); -const tchar* Typeface::defaultTypefaceNameMono = T(""); - -//============================================================================== -Typeface::Typeface() throw() - : hash (0), - isFullyPopulated (false) -{ - zeromem (lookupTable, sizeof (lookupTable)); -} - -Typeface::Typeface (const Typeface& other) - : typefaceName (other.typefaceName), - ascent (other.ascent), - bold (other.bold), - italic (other.italic), - isFullyPopulated (other.isFullyPopulated), - defaultCharacter (other.defaultCharacter) -{ - zeromem (lookupTable, sizeof (lookupTable)); - - for (int i = 0; i < other.glyphs.size(); ++i) - addGlyphCopy ((const TypefaceGlyphInfo*) other.glyphs.getUnchecked(i)); - - updateHashCode(); -} - -Typeface::Typeface (const String& faceName, - const bool bold, - const bool italic) - : isFullyPopulated (false) -{ - zeromem (lookupTable, sizeof (lookupTable)); - - initialiseTypefaceCharacteristics (faceName, bold, italic, false); - - updateHashCode(); -} - Typeface::~Typeface() +{ +} + +//============================================================================== +class CustomTypefaceGlyphInfo +{ +public: + CustomTypefaceGlyphInfo (const juce_wchar character_, const Path& path_, const float width_) throw() + : character (character_), path (path_), width (width_) + { + } + + ~CustomTypefaceGlyphInfo() throw() + { + } + + struct KerningPair + { + juce_wchar character2; + float kerningAmount; + }; + + void addKerningPair (const juce_wchar subsequentCharacter, + const float extraKerningAmount) throw() + { + KerningPair kp; + kp.character2 = subsequentCharacter; + kp.kerningAmount = extraKerningAmount; + kerningPairs.add (kp); + } + + float getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw() + { + if (subsequentCharacter != 0) + { + for (int i = kerningPairs.size(); --i >= 0;) + if (kerningPairs.getReference(i).character2 == subsequentCharacter) + return width + kerningPairs.getReference(i).kerningAmount; + } + + return width; + } + + const juce_wchar character; + const Path path; + float width; + Array kerningPairs; + + juce_UseDebuggingNewOperator + +private: + CustomTypefaceGlyphInfo (const CustomTypefaceGlyphInfo&); + const CustomTypefaceGlyphInfo& operator= (const CustomTypefaceGlyphInfo&); +}; + +//============================================================================== +CustomTypeface::CustomTypeface() + : Typeface (String::empty) { clear(); } -const Typeface& Typeface::operator= (const Typeface& other) throw() +CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) + : Typeface (String::empty) { - if (this != &other) - { - clear(); - - typefaceName = other.typefaceName; - ascent = other.ascent; - bold = other.bold; - italic = other.italic; - isFullyPopulated = other.isFullyPopulated; - defaultCharacter = other.defaultCharacter; - - for (int i = 0; i < other.glyphs.size(); ++i) - addGlyphCopy ((const TypefaceGlyphInfo*) other.glyphs.getUnchecked(i)); - - updateHashCode(); - } - - return *this; -} - -void Typeface::updateHashCode() throw() -{ - hash = typefaceName.hashCode(); - - if (bold) - hash ^= 0xffff; - - if (italic) - hash ^= 0xffff0000; -} - -void Typeface::clear() throw() -{ - zeromem (lookupTable, sizeof (lookupTable)); - typefaceName = String::empty; - bold = false; - italic = false; - - for (int i = glyphs.size(); --i >= 0;) - { - TypefaceGlyphInfo* const g = (TypefaceGlyphInfo*) (glyphs.getUnchecked(i)); - delete g; - } - - glyphs.clear(); - updateHashCode(); -} - - -Typeface::Typeface (InputStream& serialisedTypefaceStream) -{ - zeromem (lookupTable, sizeof (lookupTable)); - isFullyPopulated = true; + clear(); GZIPDecompressorInputStream gzin (&serialisedTypefaceStream, false); BufferedInputStream in (&gzin, 32768, false); - typefaceName = in.readString(); - bold = in.readBool(); - italic = in.readBool(); + name = in.readString(); + isBold = in.readBool(); + isItalic = in.readBool(); ascent = in.readFloat(); defaultCharacter = (juce_wchar) in.readShort(); @@ -222,17 +138,146 @@ Typeface::Typeface (InputStream& serialisedTypefaceStream) addKerningPair (char1, char2, in.readFloat()); } - - updateHashCode(); } -void Typeface::serialise (OutputStream& outputStream) +CustomTypeface::~CustomTypeface() +{ +} + +//============================================================================== +void CustomTypeface::clear() +{ + defaultCharacter = 0; + ascent = 1.0f; + isBold = isItalic = false; + zeromem (lookupTable, sizeof (lookupTable)); + glyphs.clear(); +} + +void CustomTypeface::setCharacteristics (const String& name_, const float ascent_, const bool isBold_, + const bool isItalic_, const juce_wchar defaultCharacter_) throw() +{ + name = name_; + defaultCharacter = defaultCharacter_; + ascent = ascent_; + isBold = isBold_; + isItalic = isItalic_; +} + +void CustomTypeface::addGlyph (const juce_wchar character, const Path& path, const float width) throw() +{ + // Check that you're not trying to add the same character twice.. + jassert (findGlyph (character, false) == 0); + + if (((unsigned int) character) < (unsigned int) numElementsInArray (lookupTable)) + lookupTable [character] = (short) glyphs.size(); + + glyphs.add (new CustomTypefaceGlyphInfo (character, path, width)); +} + +void CustomTypeface::addKerningPair (const juce_wchar char1, const juce_wchar char2, const float extraAmount) throw() +{ + if (extraAmount != 0) + { + CustomTypefaceGlyphInfo* const g = findGlyph (char1, true); + jassert (g != 0); // can only add kerning pairs for characters that exist! + + if (g != 0) + g->addKerningPair (char2, extraAmount); + } +} + +CustomTypefaceGlyphInfo* CustomTypeface::findGlyph (const juce_wchar character, const bool loadIfNeeded) throw() +{ + if (((unsigned int) character) < (unsigned int) numElementsInArray (lookupTable) && lookupTable [character] > 0) + return glyphs [(int) lookupTable [(int) character]]; + + for (int i = 0; i < glyphs.size(); ++i) + { + CustomTypefaceGlyphInfo* const g = glyphs.getUnchecked(i); + if (g->character == character) + return g; + } + + if (loadIfNeeded && loadGlyphIfPossible (character)) + return findGlyph (character, false); + + return 0; +} + +CustomTypefaceGlyphInfo* CustomTypeface::findGlyphSubstituting (const juce_wchar character) throw() +{ + CustomTypefaceGlyphInfo* glyph = findGlyph (character, true); + + if (glyph == 0) + { + if (CharacterFunctions::isWhitespace (character) && character != L' ') + glyph = findGlyph (L' ', true); + + if (glyph == 0) + { + const Font fallbackFont (Font::getFallbackFontName(), 10, 0); + Typeface* const fallbackTypeface = fallbackFont.getTypeface(); + if (fallbackTypeface != 0 && fallbackTypeface != this) + { + //xxx + } + + if (glyph == 0) + glyph = findGlyph (defaultCharacter, true); + } + } + + return glyph; +} + +bool CustomTypeface::loadGlyphIfPossible (const juce_wchar characterNeeded) +{ + return false; +} + +void CustomTypeface::addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) throw() +{ + for (int i = 0; i < numCharacters; ++i) + { + const juce_wchar c = characterStartIndex + i; + + Array glyphIndexes; + Array offsets; + typefaceToCopy.getGlyphPositions (String::charToString (c), glyphIndexes, offsets); + + const int glyphIndex = glyphIndexes.getFirst(); + + if (glyphIndex >= 0 && glyphIndexes.size() > 0) + { + const float glyphWidth = offsets[1]; + + Path p; + typefaceToCopy.getOutlineForGlyph (glyphIndex, p); + + addGlyph (c, p, glyphWidth); + + for (int j = glyphs.size() - 1; --j >= 0;) + { + const juce_wchar char2 = glyphs.getUnchecked (j)->character; + glyphIndexes.clearQuick(); + offsets.clearQuick(); + typefaceToCopy.getGlyphPositions (String::charToString (c) + String::charToString (char2), glyphIndexes, offsets); + + if (offsets.size() > 1) + addKerningPair (c, char2, offsets[1] - glyphWidth); + } + } + } +} + +bool CustomTypeface::writeToStream (OutputStream& outputStream) { GZIPCompressorOutputStream out (&outputStream); - out.writeString (typefaceName); - out.writeBool (bold); - out.writeBool (italic); + out.writeString (name); + out.writeBool (isBold); + out.writeBool (isItalic); out.writeFloat (ascent); out.writeShort ((short) (unsigned short) defaultCharacter); out.writeInt (glyphs.size()); @@ -241,280 +286,89 @@ void Typeface::serialise (OutputStream& outputStream) for (i = 0; i < glyphs.size(); ++i) { - const TypefaceGlyphInfo& g = *(const TypefaceGlyphInfo*)(glyphs.getUnchecked (i)); - out.writeShort ((short) (unsigned short) g.character); - out.writeFloat (g.width); - g.path.writePathToStream (out); + const CustomTypefaceGlyphInfo* const g = glyphs.getUnchecked (i); + out.writeShort ((short) (unsigned short) g->character); + out.writeFloat (g->width); + g->path.writePathToStream (out); - numKerningPairs += g.getNumKerningPairs(); + numKerningPairs += g->kerningPairs.size(); } out.writeInt (numKerningPairs); for (i = 0; i < glyphs.size(); ++i) { - const TypefaceGlyphInfo& g = *(const TypefaceGlyphInfo*)(glyphs.getUnchecked (i)); + const CustomTypefaceGlyphInfo* const g = glyphs.getUnchecked (i); - for (int j = 0; j < g.getNumKerningPairs(); ++j) + for (int j = 0; j < g->kerningPairs.size(); ++j) { - const TypefaceGlyphInfo::KerningPair& p = g.getKerningPair (j); - out.writeShort ((short) (unsigned short) g.character); + const CustomTypefaceGlyphInfo::KerningPair& p = g->kerningPairs.getReference (j); + out.writeShort ((short) (unsigned short) g->character); out.writeShort ((short) (unsigned short) p.character2); out.writeFloat (p.kerningAmount); } } -} -const Path* Typeface::getOutlineForGlyph (const juce_wchar character) throw() -{ - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) getGlyph (character); - - if (g != 0) - return &(g->path); - else - return 0; -} - -const TypefaceGlyphInfo* Typeface::getGlyph (const juce_wchar character) throw() -{ - if (((unsigned int) character) < 128 && lookupTable [character] > 0) - return (const TypefaceGlyphInfo*) glyphs [(int) lookupTable [character]]; - - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - if (g->character == character) - return g; - } - - if ((! isFullyPopulated) - && findAndAddSystemGlyph (character)) - { - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - if (g->character == character) - return g; - } - } - - if (CharacterFunctions::isWhitespace (character) && character != L' ') - { - const TypefaceGlyphInfo* spaceGlyph = getGlyph (L' '); - - if (spaceGlyph != 0) - { - // Add a copy of the empty glyph, mapped onto this character - addGlyph (character, spaceGlyph->getPath(), spaceGlyph->getHorizontalSpacing (0)); - spaceGlyph = (const TypefaceGlyphInfo*) glyphs [(int) lookupTable [character]]; - } - - return spaceGlyph; - } - else if (character != defaultCharacter) - { - const Font fallbackFont (Font::getFallbackFontName(), 10, 0); - Typeface* const fallbackTypeface = fallbackFont.getTypeface(); - - if (fallbackTypeface != 0 && fallbackTypeface != this) - return fallbackTypeface->getGlyph (character); - - return getGlyph (defaultCharacter); - } - - return 0; -} - -void Typeface::addGlyph (const juce_wchar character, - const Path& path, - const float horizontalSpacing) throw() -{ -#ifdef JUCE_DEBUG - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - if (g->character == character) - jassertfalse; - } -#endif - - if (((unsigned int) character) < 128) - lookupTable [character] = (short) glyphs.size(); - - glyphs.add (new TypefaceGlyphInfo (character, - path, - horizontalSpacing, - this)); -} - -void Typeface::addGlyphCopy (const TypefaceGlyphInfo* const glyphInfoToCopy) throw() -{ - if (glyphInfoToCopy != 0) - { - if (glyphInfoToCopy->character > 0 && glyphInfoToCopy->character < 128) - lookupTable [glyphInfoToCopy->character] = (short) glyphs.size(); - - TypefaceGlyphInfo* const newOne - = new TypefaceGlyphInfo (glyphInfoToCopy->character, - glyphInfoToCopy->path, - glyphInfoToCopy->width, - this); - - newOne->kerningPairs = glyphInfoToCopy->kerningPairs; - glyphs.add (newOne); - } -} - -void Typeface::addKerningPair (const juce_wchar char1, - const juce_wchar char2, - const float extraAmount) throw() -{ - TypefaceGlyphInfo* const g = (TypefaceGlyphInfo*) getGlyph (char1); - - if (g != 0) - g->addKerningPair (char2, extraAmount); -} - -void Typeface::setName (const String& name) throw() -{ - typefaceName = name; - updateHashCode(); -} - -void Typeface::setAscent (const float newAscent) throw() -{ - ascent = newAscent; -} - -void Typeface::setDefaultCharacter (const juce_wchar newDefaultCharacter) throw() -{ - defaultCharacter = newDefaultCharacter; -} - -void Typeface::setBold (const bool shouldBeBold) throw() -{ - bold = shouldBeBold; - updateHashCode(); -} - -void Typeface::setItalic (const bool shouldBeItalic) throw() -{ - italic = shouldBeItalic; - updateHashCode(); + return true; } //============================================================================== -class TypefaceCache; -static TypefaceCache* typefaceCacheInstance = 0; - -void clearUpDefaultFontNames() throw(); // in juce_LookAndFeel.cpp - - -//============================================================================== -class TypefaceCache : private DeletedAtShutdown +float CustomTypeface::getAscent() const { -private: - //============================================================================== - struct CachedFace - { - CachedFace() throw() - : lastUsageCount (0), - flags (0) - { - } + return ascent; +} - String typefaceName; - int lastUsageCount; - int flags; - Typeface::Ptr typeFace; - }; - - int counter; - OwnedArray faces; - - TypefaceCache (const TypefaceCache&); - const TypefaceCache& operator= (const TypefaceCache&); - -public: - //============================================================================== - TypefaceCache (int numToCache = 10) - : counter (1), - faces (2) - { - while (--numToCache >= 0) - { - CachedFace* const face = new CachedFace(); - face->typeFace = new Typeface(); - faces.add (face); - } - } - - ~TypefaceCache() - { - faces.clear(); - jassert (typefaceCacheInstance == this); - typefaceCacheInstance = 0; - clearUpDefaultFontNames(); - } - - //============================================================================== - static TypefaceCache* getInstance() throw() - { - if (typefaceCacheInstance == 0) - typefaceCacheInstance = new TypefaceCache(); - - return typefaceCacheInstance; - } - - //============================================================================== - const Typeface::Ptr findTypefaceFor (const Font& font) throw() - { - const int flags = font.getStyleFlags() & (Font::bold | Font::italic); - - int i; - for (i = faces.size(); --i >= 0;) - { - CachedFace* const face = faces.getUnchecked(i); - - if (face->flags == flags - && face->typefaceName == font.getTypefaceName()) - { - face->lastUsageCount = ++counter; - return face->typeFace; - } - } - - int replaceIndex = 0; - int bestLastUsageCount = INT_MAX; - - for (i = faces.size(); --i >= 0;) - { - const int lu = faces.getUnchecked(i)->lastUsageCount; - - if (bestLastUsageCount > lu) - { - bestLastUsageCount = lu; - replaceIndex = i; - } - } - - CachedFace* const face = faces.getUnchecked (replaceIndex); - - face->typefaceName = font.getTypefaceName(); - face->flags = flags; - face->lastUsageCount = ++counter; - face->typeFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); - - return face->typeFace; - } -}; - -const Typeface::Ptr Typeface::getTypefaceFor (const Font& font) throw() +float CustomTypeface::getDescent() const { - return TypefaceCache::getInstance()->findTypefaceFor (font); + return 1.0f - ascent; +} + +float CustomTypeface::getStringWidth (const String& text) +{ + float x = 0; + const juce_wchar* t = (const juce_wchar*) text; + + while (*t != 0) + { + const CustomTypefaceGlyphInfo* const glyph = findGlyphSubstituting (*t++); + + if (glyph != 0) + x += glyph->getHorizontalSpacing (*t); + } + + return x; +} + +void CustomTypeface::getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets) +{ + xOffsets.add (0); + float x = 0; + const juce_wchar* t = (const juce_wchar*) text; + + while (*t != 0) + { + const juce_wchar c = *t++; + const CustomTypefaceGlyphInfo* const glyph = findGlyphSubstituting (c); + + if (glyph != 0) + { + x += glyph->getHorizontalSpacing (*t); + glyphs.add ((int) glyph->character); + xOffsets.add (x); + } + } +} + +bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) +{ + const CustomTypefaceGlyphInfo* const glyph = findGlyphSubstituting ((juce_wchar) glyphNumber); + if (glyph != 0) + { + path = glyph->path; + return true; + } + + return false; } END_JUCE_NAMESPACE diff --git a/src/gui/graphics/fonts/juce_Typeface.h b/src/gui/graphics/fonts/juce_Typeface.h index fe5db962be..eb9461ab05 100644 --- a/src/gui/graphics/fonts/juce_Typeface.h +++ b/src/gui/graphics/fonts/juce_Typeface.h @@ -27,306 +27,207 @@ #define __JUCE_TYPEFACE_JUCEHEADER__ #include "../../../containers/juce_ReferenceCountedObject.h" -#include "../../../containers/juce_VoidArray.h" +#include "../../../containers/juce_OwnedArray.h" #include "../../../io/streams/juce_InputStream.h" #include "../../../io/streams/juce_OutputStream.h" #include "../geometry/juce_Path.h" class Font; -class Typeface; +class CustomTypefaceGlyphInfo; //============================================================================== -/** - Stores information about the shape and kerning of one of the glyphs in a Typeface. +/** A typeface represents a size-independent font. - @see Typeface, PositionedGlyph, GlyphArrangement -*/ -class JUCE_API TypefaceGlyphInfo -{ -public: - //============================================================================== - /** Returns the path that describes the glyph's outline. + This base class is abstract, but calling createSystemTypefaceFor() will return + a platform-specific subclass that can be used. - This is normalised to a height of 1.0, and its origin is the - left-hand edge of the glyph's baseline. - */ - const Path& getPath() const throw() { return path; } + The CustomTypeface subclass allow you to build your own typeface, and to + load and save it in the Juce typeface format. - /** Returns the unicode character that this glyph represents. */ - juce_wchar getCharacter() const throw() { return character; } - - bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (character); } - - /** Returns the distance to leave between this and a following character. - - The value returned is expressed as a proportion of the font's height. - */ - float getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw(); - - /** Returns the typeface that this glyph belongs to. */ - Typeface* getTypeface() const throw() { return typeface; } - - -private: - //============================================================================== - friend class Typeface; - - struct KerningPair - { - juce_wchar character2; - float kerningAmount; - }; - - const juce_wchar character; - const Path path; - float width; - MemoryBlock kerningPairs; - Typeface* const typeface; - - TypefaceGlyphInfo (const juce_wchar character, - const Path& shape, - const float horizontalSeparation, - Typeface* const typeface) throw(); - ~TypefaceGlyphInfo() throw(); - - KerningPair& getKerningPair (const int index) const throw(); - int getNumKerningPairs() const throw(); - - void addKerningPair (const juce_wchar subsequentCharacter, - const float extraKerningAmount) throw(); - - const TypefaceGlyphInfo& operator= (const TypefaceGlyphInfo&); -}; - - -//============================================================================== -/** - Represents a size-independent system font. - - A Font object represents a particular Typeface along with a specific size, - style, kerning, scale, etc, wheras the Typeface is just a generalised description - of the shapes of the glyphs and their properties. + Normally you should never need to deal directly with Typeface objects - the Font + class does everything you typically need for rendering text. + @see CustomTypeface, Font */ class JUCE_API Typeface : public ReferenceCountedObject { public: //============================================================================== - /** Tries to load a named system font and to initialise all the glyphs - appropriately from it. - - @param faceName the name of the typeface, e.g. "Times" - @param bold whether to try to find a bold version of the font (may not always be available) - @param italic whether to try to find an italicised version of the font (may not always be available) - */ - Typeface (const String& faceName, - const bool bold, - const bool italic); - - /** Creates a copy of another typeface */ - Typeface (const Typeface& other); - - /** Destructor. */ - ~Typeface(); - - /** Copies another typeface over this one. */ - const Typeface& operator= (const Typeface& other) throw(); - - /** Returns a unique ID for the typeface. - - This is based on the name and style, so can be used to compare two Typeface objects. - */ - int hashCode() const throw() { return hash; } - - //============================================================================== - /** Returns the name of the typeface, e.g. "Times", "Verdana", etc */ - const String& getName() const throw() { return typefaceName; } - - /** Returns the font's ascent as a proportion of its height. */ - float getAscent() const throw() { return ascent; } - - /** Returns true if the font is flagged as being bold. */ - bool isBold() const throw() { return bold; } - - /** Returns true if the typeface's 'italic' flag is set. */ - bool isItalic() const throw() { return italic; } - - //============================================================================== - /** Finds the Path that describes the outline shape of a character. - - The height of the path is normalised to 1.0 (i.e. a distance of 1.0 is the - height of the font). - - This may return 0 if the typeface has no characters, but if the character - that is asked for is not found, it will first try to return a default - character instead. - */ - const Path* getOutlineForGlyph (const juce_wchar character) throw(); - - /** Tries to find the information describing a glyph for this character. - - If there isn't a glyph specifically for the character it will return - a default glyph instead; if the typeface is empty, it may return a null - pointer. - */ - const TypefaceGlyphInfo* getGlyph (const juce_wchar character) throw(); - - //============================================================================== - /** Deletes all the glyphs and kerning data fom the typeface. */ - void clear() throw(); - - /** Adds a glyph to the typeface. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void addGlyph (const juce_wchar character, - const Path& path, - const float horizontalSpacing) throw(); - - /** Adds a kerning distance to the typeface. - - The extra amount passed in is expressed as a proportion of the font's - height, normalised to 1.0. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void addKerningPair (const juce_wchar firstChar, - const juce_wchar secondChar, - const float extraAmount) throw(); - - /** Sets the typeface's name. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. Calling this method won't actually affect - the underlying font being used. - */ - void setName (const String& name) throw(); - - /** Sets the font's ascent value, as a proportion of the font height. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void setAscent (const float newAscent) throw(); - - /** Sets the typeface's 'bold' flag. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void setBold (const bool shouldBeBold) throw(); - - /** Sets the typeface's 'italic' flag. - - This is typically only called by the platform-specific code that generates - the typeface from a system font. - */ - void setItalic (const bool shouldBeItalic) throw(); - - /** Changes the character index to use as the default character. - - This is the character that gets returned for characters which don't have a - glyph set for them. - */ - void setDefaultCharacter (const juce_wchar newDefaultCharacter) throw(); - - //============================================================================== - /** Creates a typeface from data created using Typeface::serialise(). - - This will attempt to load a compressed typeface that was created using - the Typeface::serialise() method. This is handy if you want to store - a typeface in your application as a binary blob, and use it without - having to actually install it on the computer. - - @see Typeface::serialise() - */ - Typeface (InputStream& serialisedTypefaceStream); - - /** Writes the typeface to a stream (using a proprietary format). - - This lets you save a typeface and reload it using the - Typeface::Typeface (InputStream&) constructor. The data's saved in - a compressed format. - - @see Typeface::Typeface (InputStream&) - */ - void serialise (OutputStream& outputStream); - - //============================================================================== - /** A name that represents the default sans-serif typeface name. - - Note that this is NOT the platform-specific typeface name (e.g. "Times"), but - is a generic string that represents whatever that font is, such as "DefaultSans". - - If you try to create a typeface with this name, the global default LookAndFeel - object will be asked to provide an appropriate typeface. - */ - static const tchar* defaultTypefaceNameSans; - - /** A name that represents the default serif typeface name. - - Note that this is NOT the platform-specific typeface name (e.g. "Times"), but - is a generic string that represents it, such as "DefaultSans". - - If you try to create a typeface with this name, the global default LookAndFeel - object will be asked to provide an appropriate typeface. - */ - static const tchar* defaultTypefaceNameSerif; - - /** A name that represents the default monospaced typeface name. - - Note that this is NOT the platform-specific typeface name (e.g. "Times"), but - is a generic string that represents it, such as "DefaultSans". - - If you try to create a typeface with this name, the global default LookAndFeel - object will be asked to provide an appropriate typeface. - */ - static const tchar* defaultTypefaceNameMono; - - //============================================================================== - /** A handy typedef to make it easy to use ref counted pointers to this class. */ + /** A handy typedef for a pointer to a typeface. */ typedef ReferenceCountedObjectPtr Ptr; + //============================================================================== + /** Returns the name of the typeface. + @see Font::getTypefaceName + */ + const String getName() const throw() { return name; } + + //============================================================================== + /** Creates a new system typeface. */ + static const Ptr createSystemTypefaceFor (const Font& font); + + //============================================================================== + /** Destructor. */ + virtual ~Typeface(); + + /** Returns the ascent of the font, as a proportion of its height. + The height is considered to always be normalised as 1.0, so this will be a + value less that 1.0, indicating the proportion of the font that lies above + its baseline. + */ + virtual float getAscent() const = 0; + + /** Returns the descent of the font, as a proportion of its height. + The height is considered to always be normalised as 1.0, so this will be a + value less that 1.0, indicating the proportion of the font that lies below + its baseline. + */ + virtual float getDescent() const = 0; + + /** Measures the width of a line of text. + + The distance returned is based on the font having an normalised height of 1.0. + + You should never need to call this directly! Use Font::getStringWidth() instead! + */ + virtual float getStringWidth (const String& text) = 0; + + /** Converts a line of text into its glyph numbers and their positions. + + The distances returned are based on the font having an normalised height of 1.0. + + You should never need to call this directly! Use Font::getGlyphPositions() instead! + */ + virtual void getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets) = 0; + + /** Returns the outline for a glyph. + + The path returned will be normalised to a font height of 1.0. + */ + virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; + + //============================================================================== juce_UseDebuggingNewOperator +protected: + String name; + + Typeface (const String& name) throw(); + private: - VoidArray glyphs; - short lookupTable [128]; - - String typefaceName; - int hash; - float ascent; // as a proportion of the height - bool bold, italic, isFullyPopulated; - juce_wchar defaultCharacter; // the char to use if a matching glyph can't be found. - - Typeface() throw(); - void addGlyphCopy (const TypefaceGlyphInfo* const glyphInfoToCopy) throw(); - - friend class Font; - friend class TypefaceCache; - friend class FontGlyphAlphaMap; - - static const Ptr getTypefaceFor (const Font& font) throw(); - - // this is a platform-dependent method that will look for the given typeface - // and set up its kerning tables, etc. accordingly. - // If addAllGlyphsToFont is true, it should also add all the glyphs in the font - // to the typeface immediately, rather than having to add them later on-demand. - void initialiseTypefaceCharacteristics (const String& fontName, - bool bold, bool italic, - bool addAllGlyphsToFont) throw(); - - // platform-specific routine to look up and add a glyph to this typeface - bool findAndAddSystemGlyph (juce_wchar character) throw(); - - void updateHashCode() throw(); - - friend class LookAndFeel; - static void getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw(); + Typeface (const Typeface&); + const Typeface& operator= (const Typeface&); }; +//============================================================================== +/** A typeface that can be populated with custom glyphs. + + You can create a CustomTypeface if you need one that contains your own glyphs, + or if you need to load a typeface from a Juce-formatted binary stream. + + If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface() + to copy glyphs into this face. + + @see Typeface, Font +*/ +class JUCE_API CustomTypeface : public Typeface +{ +public: + //============================================================================== + /** Creates a new, empty typeface. */ + CustomTypeface(); + + /** Loads a typeface from a previously saved stream. + The stream must have been created by writeToStream(). + @see writeToStream + */ + CustomTypeface (InputStream& serialisedTypefaceStream); + + /** Destructor. */ + ~CustomTypeface(); + + //============================================================================== + /** Resets this typeface, deleting all its glyphs and settings. */ + void clear(); + + /** Sets the vital statistics for the typeface. + @param name the typeface's name + @param ascent the ascent - this is normalised to a height of 1.0 and this is + the value that will be returned by Typeface::getAscent(). The + descent is assumed to be (1.0 - ascent) + @param isBold should be true if the typeface is bold + @param isItalic should be true if the typeface is italic + @param defaultCharacter the character to be used as a replacement if there's + no glyph available for the character that's being drawn + */ + void setCharacteristics (const String& name, const float ascent, + const bool isBold, const bool isItalic, + const juce_wchar defaultCharacter) throw(); + + /** Adds a glyph to the typeface. + + The path that is passed in is normalised so that the font height is 1.0, and its + origin is the anchor point of the character on its baseline. + + The width is the nominal width of the character, and any extra kerning values that + are specified will be added to this width. + */ + void addGlyph (const juce_wchar character, const Path& path, const float width) throw(); + + /** Specifies an extra kerning amount to be used between a pair of characters. + The amount will be added to the nominal width of the first character when laying out a string. + */ + void addKerningPair (const juce_wchar char1, const juce_wchar char2, const float extraAmount) throw(); + + /** Adds a range of glyphs from another typeface. + This will attempt to pull in the paths and kerning information from another typeface and + add it to this one. + */ + void addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) throw(); + + /** Saves this typeface as a Juce-formatted font file. + A CustomTypeface can be created to reload the data that is written - see the CustomTypeface + constructor. + */ + bool writeToStream (OutputStream& outputStream); + + //============================================================================== + // The following methods implement the basic Typeface behaviour. + float getAscent() const; + float getDescent() const; + float getStringWidth (const String& text); + void getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets); + bool getOutlineForGlyph (int glyphNumber, Path& path); + int getGlyphForCharacter (juce_wchar character); + + //============================================================================== + juce_UseDebuggingNewOperator + +protected: + juce_wchar defaultCharacter; + float ascent; + bool isBold, isItalic; + + //============================================================================== + /** If a subclass overrides this, it can load glyphs into the font on-demand. + When methods such as getGlyphPositions() or getOutlineForGlyph() are asked for a + particular character and there's no corresponding glyph, they'll call this + method so that a subclass can try to add that glyph, returning true if it + manages to do so. + */ + virtual bool loadGlyphIfPossible (const juce_wchar characterNeeded); + +private: + //============================================================================== + OwnedArray glyphs; + short lookupTable [128]; + + CustomTypeface (const CustomTypeface&); + const CustomTypeface& operator= (const CustomTypeface&); + + CustomTypefaceGlyphInfo* findGlyph (const juce_wchar character, const bool loadIfNeeded) throw(); + CustomTypefaceGlyphInfo* findGlyphSubstituting (const juce_wchar character) throw(); +}; #endif // __JUCE_TYPEFACE_JUCEHEADER__ diff --git a/src/native/juce_mac_NativeCode.mm b/src/native/juce_mac_NativeCode.mm index bb0fb119bc..af014c44bd 100644 --- a/src/native/juce_mac_NativeCode.mm +++ b/src/native/juce_mac_NativeCode.mm @@ -97,8 +97,8 @@ BEGIN_JUCE_NAMESPACE #if ! JUCE_ONLY_BUILD_CORE_LIBRARY #if JUCE_IPHONE + #include "mac/juce_mac_Fonts.mm" #include "mac/juce_iphone_UIViewComponentPeer.mm" - #include "mac/juce_iphone_Fonts.mm" #include "mac/juce_iphone_MessageManager.mm" #include "mac/juce_mac_FileChooser.mm" #include "mac/juce_mac_OpenGLComponent.mm" @@ -107,6 +107,7 @@ BEGIN_JUCE_NAMESPACE #include "mac/juce_iphone_Audio.cpp" #include "mac/juce_mac_CoreMidi.cpp" #else + #include "mac/juce_mac_Fonts.mm" // (must go before juce_mac_CoreGraphicsContext.mm) #include "mac/juce_mac_CoreGraphicsContext.mm" #include "mac/juce_mac_NSViewComponentPeer.mm" #include "mac/juce_mac_MouseCursor.mm" @@ -117,7 +118,6 @@ BEGIN_JUCE_NAMESPACE #include "mac/juce_mac_FileChooser.mm" #include "mac/juce_mac_QuickTimeMovieComponent.mm" #include "mac/juce_mac_AudioCDBurner.mm" - #include "mac/juce_mac_Fonts.mm" #include "mac/juce_mac_MessageManager.mm" #include "mac/juce_mac_WebBrowserComponent.mm" #include "mac/juce_mac_CoreAudio.cpp" diff --git a/src/native/linux/juce_linux_Fonts.cpp b/src/native/linux/juce_linux_Fonts.cpp index a6787164ed..ba4a517ea4 100644 --- a/src/native/linux/juce_linux_Fonts.cpp +++ b/src/native/linux/juce_linux_Fonts.cpp @@ -47,40 +47,35 @@ public: }; //============================================================================== - FreeTypeFontFace (const String& familyName) + FreeTypeFontFace (const String& familyName) throw() : hasSerif (false), monospaced (false) { family = familyName; } - void setFileName (const String& name, - const int faceIndex, - FontStyle style) + void setFileName (const String& name, const int faceIndex, FontStyle style) throw() { - if (names[(int) style].fileName.isEmpty()) + if (names [(int) style].fileName.isEmpty()) { - names[(int) style].fileName = name; - names[(int) style].faceIndex = faceIndex; + names [(int) style].fileName = name; + names [(int) style].faceIndex = faceIndex; } } - const String& getFamilyName() const throw() - { - return family; - } + const String& getFamilyName() const throw() { return family; } - const String& getFileName (int style, int* faceIndex) const throw() + const String& getFileName (const int style, int& faceIndex) const throw() { - *faceIndex = names [style].faceIndex; + faceIndex = names[style].faceIndex; return names[style].fileName; } - void setMonospaced (bool mono) { monospaced = mono; } - bool getMonospaced () const throw() { return monospaced; } + void setMonospaced (bool mono) throw() { monospaced = mono; } + bool getMonospaced() const throw() { return monospaced; } - void setSerif (const bool serif) { hasSerif = serif; } - bool getSerif () const throw() { return hasSerif; } + void setSerif (const bool serif) throw() { hasSerif = serif; } + bool getSerif() const throw() { return hasSerif; } private: //============================================================================== @@ -203,23 +198,13 @@ public: style |= (int) FreeTypeFontFace::Italic; newFace->setFileName (possible.getFullPathName(), faceIndex, (FreeTypeFontFace::FontStyle) style); - - if ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0) - newFace->setMonospaced (true); - else - newFace->setMonospaced (false); + newFace->setMonospaced ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0); // Surely there must be a better way to do this? - if (String (face->family_name).containsIgnoreCase (T("Sans")) - || String (face->family_name).containsIgnoreCase (T("Verdana")) - || String (face->family_name).containsIgnoreCase (T("Arial"))) - { - newFace->setSerif (false); - } - else - { - newFace->setSerif (true); - } + const String name (face->family_name); + newFace->setSerif (! (name.containsIgnoreCase (T("Sans")) + || name.containsIgnoreCase (T("Verdana")) + || name.containsIgnoreCase (T("Arial")))); } FT_Done_Face (face); @@ -237,7 +222,7 @@ public: const bool bold, const bool italic) throw() { - FT_Face face = NULL; + FT_Face face = 0; if (fontName == lastFontName && bold == lastBold && italic == lastItalic) { @@ -245,10 +230,10 @@ public: } else { - if (lastFace) + if (lastFace != 0) { FT_Done_Face (lastFace); - lastFace = NULL; + lastFace = 0; } lastFontName = fontName; @@ -268,25 +253,25 @@ public: style |= (int) FreeTypeFontFace::Italic; int faceIndex; - String fileName (ftFace->getFileName (style, &faceIndex)); + String fileName (ftFace->getFileName (style, faceIndex)); if (fileName.isEmpty()) { style ^= (int) FreeTypeFontFace::Bold; - fileName = ftFace->getFileName (style, &faceIndex); + fileName = ftFace->getFileName (style, faceIndex); if (fileName.isEmpty()) { style ^= (int) FreeTypeFontFace::Bold; style ^= (int) FreeTypeFontFace::Italic; - fileName = ftFace->getFileName (style, &faceIndex); + fileName = ftFace->getFileName (style, faceIndex); if (! fileName.length()) { style ^= (int) FreeTypeFontFace::Bold; - fileName = ftFace->getFileName (style, &faceIndex); + fileName = ftFace->getFileName (style, faceIndex); } } } @@ -301,10 +286,11 @@ public: } } } + return face; } - bool addGlyph (FT_Face face, Typeface& dest, uint32 character) throw() + bool addGlyph (FT_Face face, CustomTypeface& dest, uint32 character) throw() { const unsigned int glyphIndex = FT_Get_Char_Index (face, character); const float height = (float) (face->ascender - face->descender); @@ -315,10 +301,8 @@ public: #define CONVERTX(val) (scaleX * (val).x) #define CONVERTY(val) (scaleY * (val).y) - if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE - | FT_LOAD_NO_BITMAP - | FT_LOAD_IGNORE_TRANSFORM) != 0 - || face->glyph->format != ft_glyph_format_outline) + if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) != 0 + || face->glyph->format != ft_glyph_format_outline) { return false; } @@ -407,7 +391,7 @@ public: destShape.closeSubPath(); } - dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance/height); + dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance / height); if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) addKerning (face, dest, character, glyphIndex); @@ -415,7 +399,7 @@ public: return true; } - void addKerning (FT_Face face, Typeface& dest, const uint32 character, const uint32 glyphIndex) throw() + void addKerning (FT_Face face, CustomTypeface& dest, const uint32 character, const uint32 glyphIndex) throw() { const float height = (float) (face->ascender - face->descender); @@ -439,7 +423,7 @@ public: // Add a glyph to a font bool addGlyphToFont (const uint32 character, const tchar* fontName, bool bold, bool italic, - Typeface& dest) throw() + CustomTypeface& dest) throw() { FT_Face face = createFT_Face (fontName, bold, italic); @@ -449,49 +433,6 @@ public: return false; } - // Create a Typeface object for given name/style - bool createTypeface (const String& fontName, - const bool bold, const bool italic, - Typeface& dest, - const bool addAllGlyphs) throw() - { - dest.clear(); - dest.setName (fontName); - dest.setBold (bold); - dest.setItalic (italic); - - FT_Face face = createFT_Face (fontName, bold, italic); - - if (face == 0) - { -#ifdef JUCE_DEBUG - String msg (T("Failed to create typeface: ")); - msg << fontName << " " << (bold ? 'B' : ' ') << (italic ? 'I' : ' '); - DBG (msg); -#endif - return face; - } - - const float height = (float) (face->ascender - face->descender); - - dest.setAscent (face->ascender / height); - dest.setDefaultCharacter (L' '); - - if (addAllGlyphs) - { - uint32 glyphIndex; - uint32 charCode = FT_Get_First_Char (face, &glyphIndex); - - while (glyphIndex != 0) - { - addGlyph (face, dest, charCode); - charCode = FT_Get_Next_Char (face, charCode, &glyphIndex); - } - } - - return true; - } - //============================================================================== void getFamilyNames (StringArray& familyNames) const throw() { @@ -535,20 +476,44 @@ juce_ImplementSingleton_SingleThreaded (FreeTypeInterface) //============================================================================== -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, bool italic, - bool addAllGlyphsToFont) throw() +class FreetypeTypeface : public CustomTypeface { - FreeTypeInterface::getInstance() - ->createTypeface (fontName, bold, italic, *this, addAllGlyphsToFont); -} - -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() -{ - return FreeTypeInterface::getInstance() - ->addGlyphToFont (character, getName(), isBold(), isItalic(), *this); +public: + FreetypeTypeface (const Font& font) + { + FT_Face face = FreeTypeInterface::getInstance() + ->createFT_Face (font.getTypefaceName(), font.isBold(), font.isItalic()); + + if (face == 0) + { +#ifdef JUCE_DEBUG + String msg (T("Failed to create typeface: ")); + msg << font.getTypefaceName() << " " << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' '); + DBG (msg); +#endif + } + else + { + setCharacteristics (font.getTypefaceName(), + face->ascender / (float) (face->ascender - face->descender), + font.isBold(), font.isItalic(), + L' '); + } + } + + bool loadGlyphIfPossible (const juce_wchar character) + { + return FreeTypeInterface::getInstance() + ->addGlyphToFont (character, name, isBold, isItalic, *this); + } +}; + +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) +{ + return new FreetypeTypeface (font); } +//============================================================================== const StringArray Font::findAllTypefaceNames() throw() { StringArray s; @@ -607,7 +572,7 @@ static const String linux_getDefaultMonospacedFontName() return pickBestFont (allFonts, "Bitstream Vera Sans Mono, Courier, Sans Mono, Mono"); } -void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() { defaultSans = linux_getDefaultSansSerifFontName(); defaultSerif = linux_getDefaultSerifFontName(); diff --git a/src/native/mac/juce_iphone_Fonts.mm b/src/native/mac/juce_iphone_Fonts.mm deleted file mode 100644 index 3c16a6c3df..0000000000 --- a/src/native/mac/juce_iphone_Fonts.mm +++ /dev/null @@ -1,352 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -// (This file gets included by juce_mac_NativeCode.mm, rather than being -// compiled on its own). -#if JUCE_INCLUDED_FILE - - -//============================================================================== -class FontHelper -{ - UIFont* font; - -public: - String name; - bool isBold, isItalic, needsItalicTransform; - float fontSize, totalSize, ascent; - int refCount; - - FontHelper (const String& name_, - const bool bold_, - const bool italic_, - const float size_) - : font (0), - name (name_), - isBold (bold_), - isItalic (italic_), - needsItalicTransform (false), - fontSize (size_), - refCount (1) - { - //attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] - // forKey: NSLigatureAttributeName] retain]; - - font = [UIFont fontWithName: juceStringToNS (name_) size: size_]; - - if (italic_) - { -/* NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; - - if (newFont == font) - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - - font = newFont;*/ - } - -// if (bold_) - // font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; - - [font retain]; - - ascent = fabsf (font.ascender); - totalSize = ascent + fabsf (font.descender); - } - - ~FontHelper() - { - [font release]; - } - - bool getPathAndKerning (const juce_wchar char1, - const juce_wchar char2, - Path* path, - float& kerning, - float* ascent, - float* descent) - { - const ScopedAutoReleasePool pool; - - return false; -/* if (font == 0 - || ! [[font coveredCharacterSet] longCharacterIsMember: (UTF32Char) char1]) - return false; - - String chars; - chars << ' ' << char1 << char2; - - NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) - attributes: attributes] autorelease]; - NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; - NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; - [layoutManager addTextContainer: textContainer]; - [textStorage addLayoutManager: layoutManager]; - [textStorage setFont: font]; - - unsigned int glyphIndex = [layoutManager glyphRangeForCharacterRange: NSMakeRange (1, 1) - actualCharacterRange: 0].location; - NSPoint p1 = [layoutManager locationForGlyphAtIndex: glyphIndex]; - NSPoint p2 = [layoutManager locationForGlyphAtIndex: glyphIndex + 1]; - kerning = p2.x - p1.x; - - if (ascent != 0) - *ascent = this->ascent; - - if (descent != 0) - *descent = fabsf ([font descender]); - - if (path != 0) - { - NSBezierPath* bez = [NSBezierPath bezierPath]; - [bez moveToPoint: NSMakePoint (0, 0)]; - [bez appendBezierPathWithGlyph: [layoutManager glyphAtIndex: glyphIndex] - inFont: font]; - - for (int i = 0; i < [bez elementCount]; ++i) - { - NSPoint p[3]; - switch ([bez elementAtIndex: i associatedPoints: p]) - { - case NSMoveToBezierPathElement: - path->startNewSubPath (p[0].x, -p[0].y); - break; - case NSLineToBezierPathElement: - path->lineTo (p[0].x, -p[0].y); - break; - case NSCurveToBezierPathElement: - path->cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); - break; - case NSClosePathBezierPathElement: - path->closeSubPath(); - break; - default: - jassertfalse - break; - } - } - - if (needsItalicTransform) - path->applyTransform (AffineTransform::identity.sheared (-0.15, 0)); - } - - return kerning != 0;*/ - } - - juce_wchar getDefaultChar() - { - return 0; - } -}; - -//============================================================================== -class FontHelperCache : public Timer, - public DeletedAtShutdown -{ - VoidArray cache; - -public: - FontHelperCache() - { - } - - ~FontHelperCache() - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - delete f; - } - - clearSingletonInstance(); - } - - FontHelper* getFont (const String& name, - const bool bold, - const bool italic, - const float size = 1024) - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - - if (f->name == name - && f->isBold == bold - && f->isItalic == italic - && f->fontSize == size) - { - f->refCount++; - return f; - } - } - - FontHelper* const f = new FontHelper (name, bold, italic, size); - cache.add (f); - return f; - } - - void releaseFont (FontHelper* f) - { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f2 = (FontHelper*) cache.getUnchecked(i); - - if (f == f2) - { - f->refCount--; - - if (f->refCount == 0) - startTimer (5000); - - break; - } - } - } - - void timerCallback() - { - stopTimer(); - - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - - if (f->refCount == 0) - { - cache.remove (i); - delete f; - } - } - - if (cache.size() == 0) - delete this; - } - - juce_DeclareSingleton_SingleThreaded_Minimal (FontHelperCache) -}; - -juce_ImplementSingleton_SingleThreaded (FontHelperCache) - -//============================================================================== -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, - bool italic, - bool addAllGlyphsToFont) throw() -{ - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (fontName, bold, italic); - - clear(); - setAscent (helper->ascent / helper->totalSize); - setName (fontName); - setDefaultCharacter (helper->getDefaultChar()); - setBold (bold); - setItalic (italic); - - if (addAllGlyphsToFont) - { - //xxx - jassertfalse - } - - FontHelperCache::getInstance()->releaseFont (helper); -} - -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() -{ - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - if (character == 0) - return false; - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (getName(), isBold(), isItalic()); - - Path path; - float width; - bool foundOne = false; - - if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0)) - { - path.applyTransform (AffineTransform::scale (1.0f / helper->totalSize, - 1.0f / helper->totalSize)); - - addGlyph (character, path, width / helper->totalSize); - - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - float kerning; - if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0)) - { - kerning = (kerning - width) / helper->totalSize; - - if (kerning != 0) - addKerningPair (character, g->getCharacter(), kerning); - } - - if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0)) - { - kerning = kerning / helper->totalSize - g->width; - - if (kerning != 0) - addKerningPair (g->getCharacter(), character, kerning); - } - } - - foundOne = true; - } - - FontHelperCache::getInstance()->releaseFont (helper); - return foundOne; -} - -//============================================================================== -const StringArray Font::findAllTypefaceNames() throw() -{ - StringArray names; - - const ScopedAutoReleasePool pool; - NSArray* fonts = [UIFont familyNames]; - - for (unsigned int i = 0; i < [fonts count]; ++i) - names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); - - names.sort (true); - return names; -} - -void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() -{ - defaultSans = "Lucida Grande"; - defaultSerif = "Times New Roman"; - defaultFixed = "Monaco"; -} - -#endif diff --git a/src/native/mac/juce_mac_CoreGraphicsContext.mm b/src/native/mac/juce_mac_CoreGraphicsContext.mm index b454a4aebc..d5cd5924cb 100644 --- a/src/native/mac/juce_mac_CoreGraphicsContext.mm +++ b/src/native/mac/juce_mac_CoreGraphicsContext.mm @@ -37,11 +37,15 @@ public: flipHeight (flipHeight_) { CGContextRetain (context); + CGContextSetShouldSmoothFonts (context, true); + CGContextSetBlendMode (context, kCGBlendModeNormal); + state = new SavedState(); } ~CoreGraphicsContext() { CGContextRelease (context); + delete state; } //============================================================================== @@ -84,11 +88,25 @@ public: void saveState() { CGContextSaveGState (context); + stateStack.add (new SavedState (*state)); } void restoreState() { CGContextRestoreGState (context); + + SavedState* const top = stateStack.getLast(); + + if (top != 0) + { + delete state; + state = top; + stateStack.removeLast (1, false); + } + else + { + jassertfalse // trying to pop with an empty stack! + } } bool clipRegionIntersects (int x, int y, int w, int h) @@ -112,95 +130,117 @@ public: } //============================================================================== - void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) + void setColour (const Colour& colour) { - CGContextSetBlendMode (context, replaceExistingContents ? kCGBlendModeCopy : kCGBlendModeNormal); + state->colour = colour; + deleteAndZero (state->gradient); + + CGContextSetRGBFillColor (context, + colour.getFloatRed(), colour.getFloatGreen(), + colour.getFloatBlue(), colour.getFloatAlpha()); CGContextSetAlpha (context, 1.0f); - setColour (colour); - CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); } - void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) + void setGradient (const ColourGradient& gradient) { - CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); - flip(); - drawGradient (gradient); - CGContextRestoreGState (context); + if (state->gradient == 0) + state->gradient = new ColourGradient (gradient); + else + *state->gradient = gradient; } - void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel /*quality*/) + void setOpacity (float opacity) { - CGContextSetAlpha (context, 1.0f); - CGContextSaveGState (context); - flip(); - applyTransform (transform); - createPath (path); - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); - CGContextFillPath (context); - CGContextRestoreGState (context); + setColour (state->colour.withAlpha (opacity)); } - void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) + void setInterpolationQuality (Graphics::ResamplingQuality quality) + { + CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality + ? kCGInterpolationLow + : kCGInterpolationHigh); + } + + //============================================================================== + void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) + { + if (replaceExistingContents) + { + CGContextSetBlendMode (context, kCGBlendModeCopy); + fillRect (x, y, w, h, false); + CGContextSetBlendMode (context, kCGBlendModeNormal); + } + else + { + if (state->gradient == 0) + { + CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + } + else + { + CGContextSaveGState (context); + CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + flip(); + drawGradient(); + CGContextRestoreGState (context); + } + } + } + + void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality) { CGContextSaveGState (context); - createPath (path, transform); - CGContextClip (context); - flip(); - applyTransform (gradient.transform); - drawGradient (gradient); + + if (state->gradient == 0) + { + flip(); + applyTransform (transform); + createPath (path); + CGContextFillPath (context); + } + else + { + createPath (path, transform); + CGContextClip (context); + flip(); + applyTransform (state->gradient->transform); + drawGradient(); + } + CGContextRestoreGState (context); } void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel /*quality*/) + const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality) { CGContextSaveGState (context); createPath (path, transform); CGContextClip (context); - blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0, alpha); + blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0); CGContextRestoreGState (context); } - void fillAlphaChannelWithColour (const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour) + void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) { Image* singleChannelImage = createAlphaChannelImage (alphaImage); CGImageRef image = createImage (*singleChannelImage, true); + CGContextSaveGState (context); CGContextSetAlpha (context, 1.0f); - CGContextSaveGState (context); + CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), alphaImage.getWidth(), alphaImage.getHeight()); CGContextClipToMask (context, r, image); - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); - CGContextFillRect (context, r); + + fillRect (alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight(), false); + CGContextRestoreGState (context); - - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - void fillAlphaChannelWithGradient (const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - flip(); - drawGradient (gradient); - CGContextRestoreGState (context); - CGImageRelease (image); deleteAlphaChannelImage (alphaImage, singleChannelImage); } void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha) + const Image& fillerImage, int fillerImageX, int fillerImageY) { Image* singleChannelImage = createAlphaChannelImage (alphaImage); CGImageRef image = createImage (*singleChannelImage, true); @@ -212,7 +252,7 @@ public: blendImage (fillerImage, fillerImageX, fillerImageY, fillerImage.getWidth(), fillerImage.getHeight(), - 0, 0, alpha); + 0, 0); CGContextRestoreGState (context); @@ -222,15 +262,13 @@ public: //============================================================================== void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY, - float alpha) + int destX, int destY, int destW, int destH, int sourceX, int sourceY) { - CGContextSetBlendMode (context, kCGBlendModeNormal); CGImageRef image = createImage (sourceImage, false); CGContextSaveGState (context); CGContextClipToRect (context, CGRectMake (destX, flipHeight - (destY + destH), destW, destH)); - CGContextSetAlpha (context, alpha); + CGContextSetAlpha (context, state->colour.getFloatAlpha()); CGContextDrawImage (context, CGRectMake (destX - sourceX, flipHeight - ((destY - sourceY) + sourceImage.getHeight()), sourceImage.getWidth(), @@ -242,24 +280,18 @@ public: void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform, - float alpha, const Graphics::ResamplingQuality quality) + const AffineTransform& transform) { - CGContextSetBlendMode (context, kCGBlendModeNormal); CGImageRef fullImage = createImage (sourceImage, false); CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), srcClipW, srcClipH)); CGImageRelease (fullImage); CGContextSaveGState (context); - CGContextSetAlpha (context, alpha); flip(); applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); - CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality - ? kCGInterpolationLow - : kCGInterpolationHigh); - + CGContextSetAlpha (context, state->colour.getFloatAlpha()); CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()), image); @@ -268,43 +300,123 @@ public: } //============================================================================== - void drawLine (double x1, double y1, double x2, double y2, const Colour& colour) + void drawLine (double x1, double y1, double x2, double y2) { - CGContextSetAlpha (context, 1.0f); - CGContextSetRGBStrokeColor (context, colour.getFloatRed(), colour.getFloatGreen(), - colour.getFloatBlue(), colour.getFloatAlpha()); CGContextSetLineCap (context, kCGLineCapSquare); CGContextSetLineWidth (context, 1.0f); + CGContextSetRGBStrokeColor (context, + state->colour.getFloatRed(), state->colour.getFloatGreen(), + state->colour.getFloatBlue(), state->colour.getFloatAlpha()); CGPoint line[] = { { x1 + 0.5f, flipHeight - (y1 + 0.5f) }, { x2 + 0.5f, flipHeight - (y2 + 0.5f) } }; + CGContextStrokeLineSegments (context, line, 1); } - void drawVerticalLine (const int x, double top, double bottom, const Colour& colour) + void drawVerticalLine (const int x, double top, double bottom) { - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); CGContextFillRect (context, CGRectMake (x, flipHeight - bottom, 1.0f, bottom - top)); } - void drawHorizontalLine (const int y, double left, double right, const Colour& colour) + void drawHorizontalLine (const int y, double left, double right) { - setColour (colour); - CGContextSetBlendMode (context, kCGBlendModeNormal); CGContextFillRect (context, CGRectMake (left, y, right - left, 1.0f)); } + void setFont (const Font& newFont) + { + if (state->font != newFont) + { + state->fontRef = 0; + state->font = newFont; + + MacTypeface* mf = dynamic_cast ((Typeface*) state->font.getTypeface()); + + if (mf != 0) + { + state->fontRef = mf->fontRef; + CGContextSetFont (context, state->fontRef); + CGContextSetFontSize (context, state->font.getHeight() * mf->fontHeightToCGSizeFactor); + + state->fontTransform = mf->renderingTransform; + state->fontTransform.a *= state->font.getHorizontalScale(); + CGContextSetTextMatrix (context, state->fontTransform); + } + } + } + + void drawGlyph (int glyphNumber, float x, float y) + { + if (state->fontRef != 0) + { + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, x, flipHeight - y, &g, 1); + } + else + { + state->font.renderGlyphIndirectly (*this, glyphNumber, x, y); + } + } + + void drawGlyph (int glyphNumber, const AffineTransform& transform) + { + if (state->fontRef != 0) + { + CGContextSaveGState (context); + flip(); + applyTransform (transform); + + CGAffineTransform t = state->fontTransform; + t.d = -t.d; + CGContextSetTextMatrix (context, t); + + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); + + CGContextSetTextMatrix (context, state->fontTransform); + CGContextRestoreGState (context); + } + else + { + state->font.renderGlyphIndirectly (*this, glyphNumber, transform); + } + } + private: CGContextRef context; const float flipHeight; - void setColour (const Colour& colour) const throw() + struct SavedState { - CGContextSetRGBFillColor (context, - colour.getFloatRed(), colour.getFloatGreen(), - colour.getFloatBlue(), colour.getFloatAlpha()); - } + SavedState() throw() + : gradient (0), font (1.0f), fontRef (0), + fontTransform (CGAffineTransformIdentity) + { + } + + SavedState (const SavedState& other) throw() + : colour (other.colour), + gradient (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0), + font (other.font), fontRef (other.fontRef), + fontTransform (other.fontTransform) + { + } + + ~SavedState() throw() + { + delete gradient; + } + + Colour colour; + ColourGradient* gradient; + Font font; + CGFontRef fontRef; + CGAffineTransform fontTransform; + }; + + SavedState* state; + OwnedArray stateStack; static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) { @@ -316,26 +428,26 @@ private: outData[3] = c.getFloatAlpha(); } - CGShadingRef createGradient (const ColourGradient& gradient) const throw() + static CGShadingRef createGradient (const ColourGradient* const gradient) throw() { CGShadingRef result = 0; CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); CGFunctionCallbacks callbacks = { 0, gradientCallback, 0 }; - CGFunctionRef function = CGFunctionCreate ((void*) &gradient, 1, 0, 4, 0, &callbacks); - CGPoint p1 = CGPointMake (gradient.x1, gradient.y1); + CGFunctionRef function = CGFunctionCreate ((void*) gradient, 1, 0, 4, 0, &callbacks); + CGPoint p1 = CGPointMake (gradient->x1, gradient->y1); - if (gradient.isRadial) + if (gradient->isRadial) { result = CGShadingCreateRadial (colourSpace, p1, 0, - p1, hypotf (gradient.x1 - gradient.x2, gradient.y1 - gradient.y2), + p1, hypotf (gradient->x1 - gradient->x2, gradient->y1 - gradient->y2), function, true, true); } else { result = CGShadingCreateAxial (colourSpace, p1, - CGPointMake (gradient.x2, gradient.y2), + CGPointMake (gradient->x2, gradient->y2), function, true, true); } @@ -344,11 +456,10 @@ private: return result; } - void drawGradient (const ColourGradient& gradient) const throw() + void drawGradient() const throw() { - CGContextSetBlendMode (context, kCGBlendModeNormal); CGContextSetAlpha (context, 1.0f); - CGShadingRef shading = createGradient (gradient); + CGShadingRef shading = createGradient (state->gradient); CGContextDrawShading (context, shading); CGShadingRelease (shading); } diff --git a/src/native/mac/juce_mac_Fonts.mm b/src/native/mac/juce_mac_Fonts.mm index ae957799e6..06e4c6c8f5 100644 --- a/src/native/mac/juce_mac_Fonts.mm +++ b/src/native/mac/juce_mac_Fonts.mm @@ -29,303 +29,338 @@ //============================================================================== -class FontHelper +class MacTypeface : public Typeface { - NSFont* font; - public: - String name; - bool isBold, isItalic, needsItalicTransform; - float fontSize, totalSize, ascent; - int refCount; - NSMutableDictionary* attributes; - - FontHelper (const String& name_, - const bool bold_, - const bool italic_, - const float size_) - : font (0), - name (name_), - isBold (bold_), - isItalic (italic_), - needsItalicTransform (false), - fontSize (size_), - refCount (1) - { - attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] - forKey: NSLigatureAttributeName] retain]; - - font = [NSFont fontWithName: juceStringToNS (name_) size: size_]; - - if (italic_) - { - NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; - - if (newFont == font) - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - - font = newFont; - } - - if (bold_) - font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; - - [font retain]; - - ascent = fabsf ([font ascender]); - totalSize = ascent + fabsf ([font descender]); - } - - ~FontHelper() - { - [font release]; - [attributes release]; - } - - bool getPathAndKerning (const juce_wchar char1, - const juce_wchar char2, - Path* path, - float& kerning, - float* ascent, - float* descent) + //============================================================================== + MacTypeface (const Font& font) + : Typeface (font.getTypefaceName()), + charToGlyphMapper (0) { const ScopedAutoReleasePool pool; + renderingTransform = CGAffineTransformIdentity; - if (font == 0 - || ! [[font coveredCharacterSet] longCharacterIsMember: (UTF32Char) char1]) - return false; + bool needsItalicTransform = false; - String chars; - chars << ' ' << char1 << char2; +#if JUCE_IPHONE + NSString* fontName = juceStringToNS (font.getTypefaceName()); - NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) - attributes: attributes] autorelease]; - NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; - NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; - [layoutManager addTextContainer: textContainer]; - [textStorage addLayoutManager: layoutManager]; - [textStorage setFont: font]; - - unsigned int glyphIndex = [layoutManager glyphRangeForCharacterRange: NSMakeRange (1, 1) - actualCharacterRange: 0].location; - NSPoint p1 = [layoutManager locationForGlyphAtIndex: glyphIndex]; - NSPoint p2 = [layoutManager locationForGlyphAtIndex: glyphIndex + 1]; - kerning = p2.x - p1.x; - - if (ascent != 0) - *ascent = this->ascent; - - if (descent != 0) - *descent = fabsf ([font descender]); - - if (path != 0) + if (font.isItalic() || font.isBold()) { - NSBezierPath* bez = [NSBezierPath bezierPath]; - [bez moveToPoint: NSMakePoint (0, 0)]; - [bez appendBezierPathWithGlyph: [layoutManager glyphAtIndex: glyphIndex] - inFont: font]; + NSArray* familyFonts = [UIFont fontNamesForFamilyName: juceStringToNS (font.getTypefaceName())]; - for (int i = 0; i < [bez elementCount]; ++i) + for (NSString* i in familyFonts) { - NSPoint p[3]; - switch ([bez elementAtIndex: i associatedPoints: p]) + const String fn (nsStringToJuce (i)); + const String afterDash (fn.fromFirstOccurrenceOf (T("-"), false, false)); + + const bool probablyBold = afterDash.containsIgnoreCase (T("bold")) || fn.endsWithIgnoreCase (T("bold")); + const bool probablyItalic = afterDash.containsIgnoreCase (T("oblique")) + || afterDash.containsIgnoreCase (T("italic")) + || fn.endsWithIgnoreCase (T("oblique")) + || fn.endsWithIgnoreCase (T("italic")); + + if (probablyBold == font.isBold() + && probablyItalic == font.isItalic()) { - case NSMoveToBezierPathElement: - path->startNewSubPath (p[0].x, -p[0].y); - break; - case NSLineToBezierPathElement: - path->lineTo (p[0].x, -p[0].y); - break; - case NSCurveToBezierPathElement: - path->cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); - break; - case NSClosePathBezierPathElement: - path->closeSubPath(); - break; - default: - jassertfalse + fontName = i; + needsItalicTransform = false; break; } + else if (probablyBold && (! probablyItalic) && probablyBold == font.isBold()) + { + fontName = i; + needsItalicTransform = true; // not ideal, so carry on in case we find a better one + } } if (needsItalicTransform) - path->applyTransform (AffineTransform::identity.sheared (-0.15, 0)); + renderingTransform.c = 0.15f; } - return kerning != 0; - } + fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); - juce_wchar getDefaultChar() - { - return 0; - } -}; +#else + nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; -//============================================================================== -class FontHelperCache : public Timer, - public DeletedAtShutdown -{ - VoidArray cache; - -public: - FontHelperCache() - { - } - - ~FontHelperCache() - { - for (int i = cache.size(); --i >= 0;) + if (font.isItalic()) { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); - delete f; + NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: nsFont + toHaveTrait: NSItalicFontMask]; + + if (newFont == nsFont) + needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. + + nsFont = newFont; } - clearSingletonInstance(); + if (font.isBold()) + nsFont = [[NSFontManager sharedFontManager] convertFont: nsFont toHaveTrait: NSBoldFontMask]; + + [nsFont retain]; + + ascent = fabsf ([nsFont ascender]); + float totalSize = ascent + fabsf ([nsFont descender]); + ascent /= totalSize; + + pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); + + if (needsItalicTransform) + { + pathTransform = pathTransform.sheared (-0.15, 0); + renderingTransform.c = 0.15f; + } + + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); +#endif + + const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; } - FontHelper* getFont (const String& name, - const bool bold, - const bool italic, - const float size = 1024) + ~MacTypeface() { - for (int i = cache.size(); --i >= 0;) - { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); + [nsFont release]; + CGFontRelease (fontRef); + delete charToGlyphMapper; + } - if (f->name == name - && f->isBold == bold - && f->isItalic == italic - && f->fontSize == size) + float getAscent() const + { + return ascent; + } + + float getDescent() const + { + return 1.0f - ascent; + } + + float getStringWidth (const String& text) + { + if (fontRef == 0) + return 0; + + Array glyphs (128); + createGlyphsForString (text, glyphs); + + if (glyphs.size() == 0) + return 0; + + 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]; + + juce_free (advances); + return x * unitsToHeightScaleFactor; + } + + void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) + { + if (fontRef == 0) + return; + + createGlyphsForString (text, glyphs); + + xOffsets.add (0); + if (glyphs.size() == 0) + return; + + int* const advances = (int*) juce_malloc (glyphs.size() * 2 * sizeof (int)); + + if (CGFontGetGlyphAdvances (fontRef, (CGGlyph*) &glyphs.getReference(0), glyphs.size() * 2, advances)) + { + int x = 0; + for (int i = 0; i < glyphs.size(); ++i) { - f->refCount++; - return f; + x += advances [i * 2]; + xOffsets.add (x * unitsToHeightScaleFactor); } } - FontHelper* const f = new FontHelper (name, bold, italic, size); - cache.add (f); - return f; + juce_free (advances); } - void releaseFont (FontHelper* f) + bool getOutlineForGlyph (int glyphNumber, Path& path) { - for (int i = cache.size(); --i >= 0;) +#if JUCE_IPHONE + return false; +#else + if (nsFont == 0) + return false; + + // we might need to apply a transform to the path, so it mustn't have anything else in it + jassert (path.isEmpty()); + + const ScopedAutoReleasePool pool; + + NSBezierPath* bez = [NSBezierPath bezierPath]; + [bez moveToPoint: NSMakePoint (0, 0)]; + [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber + inFont: nsFont]; + + for (int i = 0; i < [bez elementCount]; ++i) { - FontHelper* const f2 = (FontHelper*) cache.getUnchecked(i); - - if (f == f2) + NSPoint p[3]; + switch ([bez elementAtIndex: i associatedPoints: p]) { - f->refCount--; - - if (f->refCount == 0) - startTimer (5000); - + case NSMoveToBezierPathElement: + path.startNewSubPath (p[0].x, -p[0].y); + break; + case NSLineToBezierPathElement: + path.lineTo (p[0].x, -p[0].y); + break; + case NSCurveToBezierPathElement: + path.cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); + break; + case NSClosePathBezierPathElement: + path.closeSubPath(); + break; + default: + jassertfalse break; } } + + path.applyTransform (pathTransform); + return true; +#endif } - void timerCallback() + //============================================================================== + juce_UseDebuggingNewOperator + + CGFontRef fontRef; + float fontHeightToCGSizeFactor; + CGAffineTransform renderingTransform; + +private: + float ascent, unitsToHeightScaleFactor; + +#if JUCE_IPHONE + +#else + NSFont* nsFont; + AffineTransform pathTransform; +#endif + + void createGlyphsForString (const String& text, Array & dest) throw() { - stopTimer(); + if (charToGlyphMapper == 0) + charToGlyphMapper = new CharToGlyphMapper (fontRef); - for (int i = cache.size(); --i >= 0;) + const juce_wchar* t = (const juce_wchar*) text; + + while (*t != 0) + dest.add (charToGlyphMapper->getGlyphForCharacter (*t++)); + } + + // Reads a CGFontRef's character map table to convert unicode into glyph numbers + class CharToGlyphMapper + { + public: + CharToGlyphMapper (CGFontRef fontRef) throw() + : segCount (0), endCode (0), startCode (0), idDelta (0), + idRangeOffset (0), glyphIndexes (0) { - FontHelper* const f = (FontHelper*) cache.getUnchecked(i); + CFDataRef cmapTable = CGFontCopyTableForTag (fontRef, 'cmap'); - if (f->refCount == 0) + if (cmapTable != 0) { - cache.remove (i); - delete f; + const int numSubtables = getValue16 (cmapTable, 2); + + for (int i = 0; i < numSubtables; ++i) + { + if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0 + { + const int offset = getValue32 (cmapTable, i * 8 + 8); + + if (getValue16 (cmapTable, offset) == 4) // check that it's format 4.. + { + const int length = getValue16 (cmapTable, offset + 2); + const int segCountX2 = getValue16 (cmapTable, offset + 6); + segCount = segCountX2 / 2; + const int endCodeOffset = offset + 14; + const int startCodeOffset = endCodeOffset + 2 + segCountX2; + const int idDeltaOffset = startCodeOffset + segCountX2; + const int idRangeOffsetOffset = idDeltaOffset + segCountX2; + const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2; + + endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2); + startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2); + idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2); + idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2); + glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset); + } + + break; + } + } + + CFRelease (cmapTable); } } - if (cache.size() == 0) - delete this; - } + ~CharToGlyphMapper() throw() + { + if (endCode != 0) + { + CFRelease (endCode); + CFRelease (startCode); + CFRelease (idDelta); + CFRelease (idRangeOffset); + CFRelease (glyphIndexes); + } + } - juce_DeclareSingleton_SingleThreaded_Minimal (FontHelperCache) + int getGlyphForCharacter (const juce_wchar c) const throw() + { + for (int i = 0; i < segCount; ++i) + { + if (getValue16 (endCode, i * 2) >= c) + { + const int start = getValue16 (startCode, i * 2); + if (start > c) + break; + + const int delta = getValue16 (idDelta, i * 2); + const int rangeOffset = getValue16 (idRangeOffset, i * 2); + + if (rangeOffset == 0) + return delta + c; + else + return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i))); + } + } + + // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts! + return jmax (-1, c - 29); + } + + private: + int segCount; + CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes; + + static uint16 getValue16 (CFDataRef data, const int index) throw() + { + return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index)); + } + + static uint32 getValue32 (CFDataRef data, const int index) throw() + { + return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index)); + } + }; + + CharToGlyphMapper* charToGlyphMapper; }; -juce_ImplementSingleton_SingleThreaded (FontHelperCache) - -//============================================================================== -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, - bool italic, - bool addAllGlyphsToFont) throw() +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (fontName, bold, italic); - - clear(); - setAscent (helper->ascent / helper->totalSize); - setName (fontName); - setDefaultCharacter (helper->getDefaultChar()); - setBold (bold); - setItalic (italic); - - if (addAllGlyphsToFont) - { - //xxx - jassertfalse - } - - FontHelperCache::getInstance()->releaseFont (helper); -} - -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() -{ - // This method is only safe to be called from the normal UI thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - if (character == 0) - return false; - - FontHelper* const helper = FontHelperCache::getInstance() - ->getFont (getName(), isBold(), isItalic()); - - Path path; - float width; - bool foundOne = false; - - if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0)) - { - path.applyTransform (AffineTransform::scale (1.0f / helper->totalSize, - 1.0f / helper->totalSize)); - - addGlyph (character, path, width / helper->totalSize); - - for (int i = 0; i < glyphs.size(); ++i) - { - const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); - - float kerning; - if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0)) - { - kerning = (kerning - width) / helper->totalSize; - - if (kerning != 0) - addKerningPair (character, g->getCharacter(), kerning); - } - - if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0)) - { - kerning = kerning / helper->totalSize - g->width; - - if (kerning != 0) - addKerningPair (g->getCharacter(), character, kerning); - } - } - - foundOne = true; - } - - FontHelperCache::getInstance()->releaseFont (helper); - return foundOne; + return new MacTypeface (font); } //============================================================================== @@ -334,7 +369,12 @@ const StringArray Font::findAllTypefaceNames() throw() StringArray names; const ScopedAutoReleasePool pool; + +#if JUCE_IPHONE + NSArray* fonts = [UIFont familyNames]; +#else NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies]; +#endif for (unsigned int i = 0; i < [fonts count]; ++i) names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); @@ -343,7 +383,7 @@ const StringArray Font::findAllTypefaceNames() throw() return names; } -void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() { defaultSans = "Lucida Grande"; defaultSerif = "Times New Roman"; diff --git a/src/native/windows/juce_win32_Fonts.cpp b/src/native/windows/juce_win32_Fonts.cpp index c3719dc905..c2406f686d 100644 --- a/src/native/windows/juce_win32_Fonts.cpp +++ b/src/native/windows/juce_win32_Fonts.cpp @@ -104,9 +104,7 @@ const StringArray Font::findAllTypefaceNames() throw() extern bool juce_IsRunningInWine() throw(); -void Typeface::getDefaultFontNames (String& defaultSans, - String& defaultSerif, - String& defaultFixed) throw() +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() { if (juce_IsRunningInWine()) { @@ -123,30 +121,15 @@ void Typeface::getDefaultFontNames (String& defaultSans, } } + //============================================================================== class FontDCHolder : private DeletedAtShutdown { - HDC dc; - String fontName; - KERNINGPAIR* kps; - int numKPs; - bool bold, italic; - int size; - - FontDCHolder (const FontDCHolder&); - const FontDCHolder& operator= (const FontDCHolder&); - public: - HFONT fontH; - //============================================================================== FontDCHolder() throw() - : dc (0), - kps (0), - numKPs (0), - bold (false), - italic (false), - size (0) + : dc (0), kps (0), numKPs (0), size (0), + bold (false), italic (false) { } @@ -165,10 +148,7 @@ public: juce_DeclareSingleton_SingleThreaded_Minimal (FontDCHolder); //============================================================================== - HDC loadFont (const String& fontName_, - const bool bold_, - const bool italic_, - const int size_) throw() + HDC loadFont (const String& fontName_, const bool bold_, const bool italic_, const int size_) throw() { if (fontName != fontName_ || bold != bold_ || italic != italic_ || size != size_) { @@ -181,7 +161,6 @@ public: { DeleteDC (dc); DeleteObject (fontH); - juce_free (kps); kps = 0; } @@ -253,243 +232,170 @@ public: numKPs_ = numKPs; return kps; } + + +private: + //============================================================================== + HFONT fontH; + HDC dc; + String fontName; + KERNINGPAIR* kps; + int numKPs, size; + bool bold, italic; + + FontDCHolder (const FontDCHolder&); + const FontDCHolder& operator= (const FontDCHolder&); }; juce_ImplementSingleton_SingleThreaded (FontDCHolder); //============================================================================== -static bool addGlyphToTypeface (HDC dc, - juce_wchar character, - Typeface& dest, - bool addKerning) +class WindowsTypeface : public CustomTypeface { - Path destShape; - GLYPHMETRICS gm; - - float height; - BOOL ok = false; - +public: + WindowsTypeface (const Font& font) { - const WCHAR charToTest[] = { (WCHAR) character, 0 }; - WORD index = 0; + HDC dc = FontDCHolder::getInstance()->loadFont (font.getTypefaceName(), + font.isBold(), font.isItalic(), 0); - if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR - && index == 0xffff) - { - return false; - } + TEXTMETRIC tm; + tm.tmAscent = tm.tmHeight = 1; + tm.tmDefaultChar = 0; + GetTextMetrics (dc, &tm); + + setCharacteristics (font.getTypefaceName(), + tm.tmAscent / (float) tm.tmHeight, + font.isBold(), font.isItalic(), + tm.tmDefaultChar); } - TEXTMETRIC tm; - ok = GetTextMetrics (dc, &tm); - - height = (float) tm.tmHeight; - - if (! ok) + bool loadGlyphIfPossible (const juce_wchar character) { - dest.addGlyph (character, destShape, 0); - return true; - } + HDC dc = FontDCHolder::getInstance()->loadFont (name, isBold, isItalic, 0); - const float scaleX = 1.0f / height; - const float scaleY = -1.0f / height; - static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + GLYPHMETRICS gm; - const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE, - &gm, 0, 0, &identityMatrix); - - if (bufSize > 0) - { - char* const data = (char*) juce_malloc (bufSize); - - GetGlyphOutline (dc, character, GGO_NATIVE, &gm, - bufSize, data, &identityMatrix); - - const TTPOLYGONHEADER* pheader = (TTPOLYGONHEADER*) data; - - while ((char*) pheader < data + bufSize) { - #define remapX(v) (scaleX * (v).x.value) - #define remapY(v) (scaleY * (v).y.value) + const WCHAR charToTest[] = { (WCHAR) character, 0 }; + WORD index = 0; - float x = remapX (pheader->pfxStart); - float y = remapY (pheader->pfxStart); - - destShape.startNewSubPath (x, y); - - const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); - const char* const curveEnd = ((const char*) pheader) + pheader->cb; - - while ((const char*) curve < curveEnd) + if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR + && index == 0xffff) { - if (curve->wType == TT_PRIM_LINE) + return false; + } + } + + Path glyphPath; + + TEXTMETRIC tm; + if (! GetTextMetrics (dc, &tm)) + { + addGlyph (character, glyphPath, 0); + return true; + } + + const float height = (float) tm.tmHeight; + static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + + const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE, + &gm, 0, 0, &identityMatrix); + + if (bufSize > 0) + { + char* const data = (char*) juce_malloc (bufSize); + + GetGlyphOutline (dc, character, GGO_NATIVE, &gm, + bufSize, data, &identityMatrix); + + const TTPOLYGONHEADER* pheader = (TTPOLYGONHEADER*) data; + + const float scaleX = 1.0f / height; + const float scaleY = -1.0f / height; + + while ((char*) pheader < data + bufSize) + { + #define remapX(v) (scaleX * (v).x.value) + #define remapY(v) (scaleY * (v).y.value) + + float x = remapX (pheader->pfxStart); + float y = remapY (pheader->pfxStart); + + glyphPath.startNewSubPath (x, y); + + const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); + const char* const curveEnd = ((const char*) pheader) + pheader->cb; + + while ((const char*) curve < curveEnd) { - for (int i = 0; i < curve->cpfx; ++i) + if (curve->wType == TT_PRIM_LINE) { - x = remapX (curve->apfx [i]); - y = remapY (curve->apfx [i]); + for (int i = 0; i < curve->cpfx; ++i) + { + x = remapX (curve->apfx [i]); + y = remapY (curve->apfx [i]); - destShape.lineTo (x, y); + glyphPath.lineTo (x, y); + } } - } - else if (curve->wType == TT_PRIM_QSPLINE) - { - for (int i = 0; i < curve->cpfx - 1; ++i) + else if (curve->wType == TT_PRIM_QSPLINE) { - const float x2 = remapX (curve->apfx [i]); - const float y2 = remapY (curve->apfx [i]); - float x3, y3; - - if (i < curve->cpfx - 2) + for (int i = 0; i < curve->cpfx - 1; ++i) { - x3 = 0.5f * (x2 + remapX (curve->apfx [i + 1])); - y3 = 0.5f * (y2 + remapY (curve->apfx [i + 1])); - } - else - { - x3 = remapX (curve->apfx [i + 1]); - y3 = remapY (curve->apfx [i + 1]); - } + const float x2 = remapX (curve->apfx [i]); + const float y2 = remapY (curve->apfx [i]); + float x3, y3; - destShape.quadraticTo (x2, y2, x3, y3); + if (i < curve->cpfx - 2) + { + x3 = 0.5f * (x2 + remapX (curve->apfx [i + 1])); + y3 = 0.5f * (y2 + remapY (curve->apfx [i + 1])); + } + else + { + x3 = remapX (curve->apfx [i + 1]); + y3 = remapY (curve->apfx [i + 1]); + } - x = x3; - y = y3; + glyphPath.quadraticTo (x2, y2, x3, y3); + + x = x3; + y = y3; + } } + + curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); } - curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); + pheader = (const TTPOLYGONHEADER*) curve; + + glyphPath.closeSubPath(); } - pheader = (const TTPOLYGONHEADER*) curve; - - destShape.closeSubPath(); + juce_free (data); } - juce_free (data); - } + addGlyph (character, glyphPath, gm.gmCellIncX / height); - dest.addGlyph (character, destShape, gm.gmCellIncX / height); - - if (addKerning) - { int numKPs; const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs); for (int i = 0; i < numKPs; ++i) { if (kps[i].wFirst == character) - { - dest.addKerningPair (kps[i].wFirst, - kps[i].wSecond, - kps[i].iKernAmount / height); - } - } - } - - return true; -} - -//============================================================================== -bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() -{ - HDC dc = FontDCHolder::getInstance()->loadFont (getName(), isBold(), isItalic(), 0); - return addGlyphToTypeface (dc, character, *this, true); -} - -/*Image* Typeface::renderGlyphToImage (juce_wchar character, float& topLeftX, float& topLeftY) -{ - HDC dc = FontDCHolder::getInstance()->loadFont (getName(), isBold(), isItalic(), hintingSize); - - int bufSize; - GLYPHMETRICS gm; - - const UINT format = GGO_GRAY2_BITMAP; - const int shift = 6; - - if (wGetGlyphOutlineW != 0) - bufSize = wGetGlyphOutlineW (dc, character, format, &gm, 0, 0, &identityMatrix); - else - bufSize = GetGlyphOutline (dc, character, format, &gm, 0, 0, &identityMatrix); - - Image* im = new Image (Image::SingleChannel, jmax (1, gm.gmBlackBoxX), jmax (1, gm.gmBlackBoxY), true); - - if (bufSize > 0) - { - topLeftX = (float) gm.gmptGlyphOrigin.x; - topLeftY = (float) -gm.gmptGlyphOrigin.y; - - uint8* const data = (uint8*) juce_calloc (bufSize); - - if (wGetGlyphOutlineW != 0) - wGetGlyphOutlineW (dc, character, format, &gm, bufSize, data, &identityMatrix); - else - GetGlyphOutline (dc, character, format, &gm, bufSize, data, &identityMatrix); - - const int stride = ((gm.gmBlackBoxX + 3) & ~3); - - for (int y = gm.gmBlackBoxY; --y >= 0;) - { - for (int x = gm.gmBlackBoxX; --x >= 0;) - { - const int level = data [x + y * stride] << shift; - - if (level > 0) - im->setPixelAt (x, y, Colour ((uint8) 0xff, (uint8) 0xff, (uint8) 0xff, (uint8) jmin (0xff, level))); - } + addKerningPair (kps[i].wFirst, kps[i].wSecond, + kps[i].iKernAmount / height); } - juce_free (data); + return true; } +}; - return im; -}*/ - -//============================================================================== -void Typeface::initialiseTypefaceCharacteristics (const String& fontName, - bool bold, - bool italic, - bool addAllGlyphsToFont) throw() +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { - clear(); - - HDC dc = FontDCHolder::getInstance()->loadFont (fontName, bold, italic, 0); - - float height; - int firstChar, lastChar; - - { - TEXTMETRIC tm; - GetTextMetrics (dc, &tm); - - height = (float) tm.tmHeight; - firstChar = tm.tmFirstChar; - lastChar = tm.tmLastChar; - - setAscent (tm.tmAscent / height); - setDefaultCharacter (tm.tmDefaultChar); - } - - setName (fontName); - setBold (bold); - setItalic (italic); - - if (addAllGlyphsToFont) - { - for (int character = firstChar; character <= lastChar; ++character) - addGlyphToTypeface (dc, (juce_wchar) character, *this, false); - - int numKPs; - const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs); - - for (int i = 0; i < numKPs; ++i) - { - addKerningPair (kps[i].wFirst, - kps[i].wSecond, - kps[i].iKernAmount / height); - } - } + return new WindowsTypeface (font); } + #endif