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