From 1629f9f66ae0385bcb675aa43c8f7c7df809650e Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Fri, 26 Nov 2010 17:29:27 +0000 Subject: [PATCH] Created a new method Graphics::beginTransparencyLayer(), to help with compositing semi-transparent rendering. --- juce_amalgamated.cpp | 1576 +++++++++-------- juce_amalgamated.h | 325 ++-- src/containers/juce_Array.h | 2 +- src/core/juce_StandardHeader.h | 2 +- src/gui/components/buttons/juce_Button.cpp | 2 +- src/gui/components/juce_Component.cpp | 16 +- src/gui/graphics/contexts/juce_EdgeTable.cpp | 14 +- src/gui/graphics/contexts/juce_EdgeTable.h | 14 +- src/gui/graphics/contexts/juce_Graphics.cpp | 11 + src/gui/graphics/contexts/juce_Graphics.h | 17 + .../contexts/juce_LowLevelGraphicsContext.h | 3 + ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 8 + .../juce_LowLevelGraphicsPostScriptRenderer.h | 3 + .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 147 +- .../juce_LowLevelGraphicsSoftwareRenderer.h | 3 + src/gui/graphics/drawables/juce_Drawable.cpp | 4 +- .../drawables/juce_DrawableComposite.cpp | 21 +- .../graphics/drawables/juce_DrawableShape.cpp | 4 +- .../mac/juce_mac_CoreGraphicsContext.mm | 13 + .../juce_win32_Direct2DGraphicsContext.cpp | 10 + 20 files changed, 1202 insertions(+), 993 deletions(-) diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 043ec27b0c..18021842f7 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -15077,6 +15077,23 @@ void XmlDocument::skipHeader() if (input == 0) return; + #if JUCE_DEBUG + const String header (found, input - found); + const String encoding (header.fromFirstOccurrenceOf ("encoding", false, true) + .fromFirstOccurrenceOf ("=", false, false) + .fromFirstOccurrenceOf ("\"", false, false) + .upToFirstOccurrenceOf ("\"", false, false).trim()); + + /* If you load an XML document with a non-UTF encoding type, it may have been + loaded wrongly.. Since all the files are read via the normal juce file streams, + they're treated as UTF-8, so by the time it gets to the parser, the encoding will + have been lost. Best plan is to stick to utf-8 or if you have specific files to + read, use your own code to convert them to a unicode String, and pass that to the + XML parser. + */ + jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-")); + #endif + input += 2; } @@ -30836,6 +30853,7 @@ PluginDescription::~PluginDescription() PluginDescription::PluginDescription (const PluginDescription& other) : name (other.name), + descriptiveName (other.descriptiveName), pluginFormatName (other.pluginFormatName), category (other.category), manufacturerName (other.manufacturerName), @@ -30852,6 +30870,7 @@ PluginDescription::PluginDescription (const PluginDescription& other) PluginDescription& PluginDescription::operator= (const PluginDescription& other) { name = other.name; + descriptiveName = other.descriptiveName; pluginFormatName = other.pluginFormatName; category = other.category; manufacturerName = other.manufacturerName; @@ -30884,6 +30903,9 @@ XmlElement* PluginDescription::createXml() const { XmlElement* const e = new XmlElement ("PLUGIN"); e->setAttribute ("name", name); + if (descriptiveName != name) + e->setAttribute ("descriptiveName", descriptiveName); + e->setAttribute ("format", pluginFormatName); e->setAttribute ("category", category); e->setAttribute ("manufacturer", manufacturerName); @@ -30903,6 +30925,7 @@ bool PluginDescription::loadFromXml (const XmlElement& xml) if (xml.hasTagName ("PLUGIN")) { name = xml.getStringAttribute ("name"); + descriptiveName = xml.getStringAttribute ("name", name); pluginFormatName = xml.getStringAttribute ("format"); category = xml.getStringAttribute ("category"); manufacturerName = xml.getStringAttribute ("manufacturer"); @@ -31332,7 +31355,7 @@ namespace AudioUnitFormatHelpers { static int insideCallback = 0; - static const String osTypeToString (OSType type) + const String osTypeToString (OSType type) { char s[4]; s[0] = (char) (((uint32) type) >> 24); @@ -31342,7 +31365,7 @@ namespace AudioUnitFormatHelpers return String (s, 4); } - static OSType stringToOSType (const String& s1) + OSType stringToOSType (const String& s1) { const String s (s1 + " "); @@ -31354,7 +31377,7 @@ namespace AudioUnitFormatHelpers static const char* auIdentifierPrefix = "AudioUnit:"; - static const String createAUPluginIdentifier (const ComponentDescription& desc) + const String createAUPluginIdentifier (const ComponentDescription& desc) { jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong.. jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto @@ -31378,7 +31401,7 @@ namespace AudioUnitFormatHelpers return s; } - static void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer) + void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer) { Handle componentNameHandle = NewHandle (sizeof (void*)); Handle componentInfoHandle = NewHandle (sizeof (void*)); @@ -31415,8 +31438,8 @@ namespace AudioUnitFormatHelpers } } - static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc, - String& name, String& version, String& manufacturer) + bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc, + String& name, String& version, String& manufacturer) { zerostruct (desc); @@ -31466,6 +31489,7 @@ public: void fillInPluginDescription (PluginDescription& desc) const { desc.name = pluginName; + desc.descriptiveName = pluginName; desc.fileOrIdentifier = AudioUnitFormatHelpers::createAUPluginIdentifier (componentDesc); desc.uid = ((int) componentDesc.componentType) ^ ((int) componentDesc.componentSubType) @@ -32412,21 +32436,12 @@ const String AudioUnitPluginInstance::getCategory() const switch (componentDesc.componentType) { - case kAudioUnitType_Effect: - case kAudioUnitType_MusicEffect: - result = "Effect"; - break; - case kAudioUnitType_MusicDevice: - result = "Synth"; - break; - case kAudioUnitType_Generator: - result = "Generator"; - break; - case kAudioUnitType_Panner: - result = "Panner"; - break; - default: - break; + case kAudioUnitType_Effect: + case kAudioUnitType_MusicEffect: result = "Effect"; break; + case kAudioUnitType_MusicDevice: result = "Synth"; break; + case kAudioUnitType_Generator: result = "Generator"; break; + case kAudioUnitType_Panner: result = "Panner"; break; + default: break; } return result; @@ -33621,6 +33636,18 @@ public: void fillInPluginDescription (PluginDescription& desc) const { desc.name = name; + + { + char buffer [512]; + zerostruct (buffer); + dispatch (effGetEffectName, 0, 0, buffer, 0); + + desc.descriptiveName = String (buffer).trim(); + + if (desc.descriptiveName.isEmpty()) + desc.descriptiveName = name; + } + desc.fileOrIdentifier = module->file.getFullPathName(); desc.uid = getUID(); desc.lastFileModTime = module->file.getLastModificationTime(); @@ -33830,20 +33857,6 @@ void VSTPluginInstance::initialise() dispatch (effIdentify, 0, 0, 0, 0); - // this code would ask the plugin for its name, but so few plugins - // actually bother implementing this correctly, that it's better to - // just ignore it and use the file name instead. -/* { - char buffer [256]; - zerostruct (buffer); - dispatch (effGetEffectName, 0, 0, buffer, 0); - - name = String (buffer).trim(); - if (name.isEmpty()) - name = module->pluginName; - } -*/ - if (getSampleRate() > 0) dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); @@ -35679,6 +35692,7 @@ void VSTPluginFormat::findAllTypesForFile (OwnedArray & resul { desc.uid = uid; desc.name = shellEffectName; + desc.descriptiveName = shellEffectName; bool alreadyThere = false; @@ -38391,7 +38405,7 @@ AsyncUpdater::~AsyncUpdater() // pending on the main event thread - that's pretty dodgy threading, as the callback could // happen after this destructor has finished. You should either use a MessageManagerLock while // deleting this object, or find some other way to avoid such a race condition. - jassert (/*(! isUpdatePending()) ||*/ MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); pendingMessage = 0; } @@ -39013,10 +39027,7 @@ MessageManager::~MessageManager() throw() doPlatformSpecificShutdown(); - /* If you hit this assertion, then you've probably leaked some kind of MessageListener object. - This could also be caused by leaking AsyncUpdaters, ChangeBroadcasters, and various other types - of event class. - */ + // If you hit this assertion, then you've probably leaked some kind of MessageListener object.. jassert (messageListeners.size() == 0); jassert (instance == this); @@ -40917,6 +40928,10 @@ bool Component::isTransformed() const throw() void Component::setTransform (const AffineTransform& newTransform) { + // If you pass in a transform with no inverse, the component will have no dimensions, + // and there will be all sorts of maths errors when converting coordinates. + jassert (! newTransform.isSingularity()); + if (newTransform.isIdentity()) { if (affineTransform_ != 0) @@ -40924,6 +40939,8 @@ void Component::setTransform (const AffineTransform& newTransform) repaint(); affineTransform_ = 0; repaint(); + + sendMovedResizedMessages (false, false); } } else if (affineTransform_ == 0) @@ -40931,12 +40948,14 @@ void Component::setTransform (const AffineTransform& newTransform) repaint(); affineTransform_ = new AffineTransform (newTransform); repaint(); + sendMovedResizedMessages (false, false); } else if (*affineTransform_ != newTransform) { repaint(); *affineTransform_ = newTransform; repaint(); + sendMovedResizedMessages (false, false); } } @@ -41537,7 +41556,7 @@ void Component::paintComponentAndChildren (Graphics& g) g.saveState(); g.addTransform (*child.affineTransform_); - if (child.flags.dontClipGraphicsFlag || g.reduceClipRegion (child.getBounds())) + if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds())) child.paintWithinParentContext (g); g.restoreState(); @@ -41605,17 +41624,9 @@ void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel) { if (componentTransparency < 255) { - Image temp (flags.opaqueFlag ? Image::RGB : Image::ARGB, - getWidth(), getHeight(), ! flags.opaqueFlag, Image::NativeImage); - - { - Graphics tempG (temp); - tempG.reduceClipRegion (g.getClipBounds()); - paintEntireComponent (tempG, true); - } - - g.setColour (Colours::black.withAlpha (getAlpha())); - g.drawImageAt (temp, 0, 0); + g.beginTransparencyLayer (getAlpha()); + paintComponentAndChildren (g); + g.endTransparencyLayer(); } } else @@ -41770,6 +41781,12 @@ const Rectangle Component::getLocalBounds() const throw() return Rectangle (getWidth(), getHeight()); } +const Rectangle Component::getBoundsInParent() const throw() +{ + return affineTransform_ == 0 ? bounds_ + : bounds_.toFloat().transformed (*affineTransform_).getSmallestIntegerContainer(); +} + void Component::getVisibleArea (RectangleList& result, const bool includeSiblings) const { result.clear(); @@ -43772,7 +43789,7 @@ void Button::mouseUp (const MouseEvent& e) internalClickCallback (e.mods); } -void Button::mouseDrag (const MouseEvent& e) +void Button::mouseDrag (const MouseEvent&) { const ButtonState oldState = buttonState; updateState (isMouseOver(), true); @@ -44033,6 +44050,7 @@ DrawableButton::DrawableButton (const String& name, const DrawableButton::ButtonStyle buttonStyle) : Button (name), style (buttonStyle), + currentImage (0), edgeIndent (3) { if (buttonStyle == ImageOnButtonBackground) @@ -44048,11 +44066,6 @@ DrawableButton::DrawableButton (const String& name, } DrawableButton::~DrawableButton() -{ - deleteImages(); -} - -void DrawableButton::deleteImages() { } @@ -44065,35 +44078,18 @@ void DrawableButton::setImages (const Drawable* normal, const Drawable* downOn, const Drawable* disabledOn) { - deleteImages(); - jassert (normal != 0); // you really need to give it at least a normal image.. - if (normal != 0) - normalImage = normal->createCopy(); + if (normal != 0) normalImage = normal->createCopy(); + if (over != 0) overImage = over->createCopy(); + if (down != 0) downImage = down->createCopy(); + if (disabled != 0) disabledImage = disabled->createCopy(); + if (normalOn != 0) normalImageOn = normalOn->createCopy(); + if (overOn != 0) overImageOn = overOn->createCopy(); + if (downOn != 0) downImageOn = downOn->createCopy(); + if (disabledOn != 0) disabledImageOn = disabledOn->createCopy(); - if (over != 0) - overImage = over->createCopy(); - - if (down != 0) - downImage = down->createCopy(); - - if (disabled != 0) - disabledImage = disabled->createCopy(); - - if (normalOn != 0) - normalImageOn = normalOn->createCopy(); - - if (overOn != 0) - overImageOn = overOn->createCopy(); - - if (downOn != 0) - downImageOn = downOn->createCopy(); - - if (disabledOn != 0) - disabledImageOn = disabledOn->createCopy(); - - repaint(); + buttonStateChanged(); } void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle) @@ -44101,7 +44097,7 @@ void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle) if (style != newStyle) { style = newStyle; - repaint(); + buttonStateChanged(); } } @@ -44128,21 +44124,88 @@ void DrawableButton::setEdgeIndent (const int numPixelsIndent) { edgeIndent = numPixelsIndent; repaint(); + resized(); +} + +void DrawableButton::resized() +{ + Button::resized(); + + if (style == ImageRaw) + { + currentImage->setOriginWithOriginalSize (Point()); + } + else if (currentImage != 0) + { + Rectangle imageSpace; + + if (style == ImageOnButtonBackground) + { + imageSpace = getLocalBounds().reduced (getWidth() / 4, getHeight() / 4); + } + else + { + const int textH = (style == ImageAboveTextLabel) + ? jmin (16, proportionOfHeight (0.25f)) + : 0; + + const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); + const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); + + imageSpace.setBounds (indentX, indentY, + getWidth() - indentX * 2, + getHeight() - indentY * 2 - textH); + } + + currentImage->setTransformToFit (imageSpace.toFloat(), RectanglePlacement::centred); + } +} + +void DrawableButton::buttonStateChanged() +{ + repaint(); + + Drawable* imageToDraw = 0; + float opacity = 1.0f; + + if (isEnabled()) + { + imageToDraw = getCurrentImage(); + } + else + { + imageToDraw = getToggleState() ? disabledImageOn + : disabledImage; + + if (imageToDraw == 0) + { + opacity = 0.4f; + imageToDraw = getNormalImage(); + } + } + + if (imageToDraw != currentImage) + { + removeChildComponent (currentImage); + currentImage = imageToDraw; + + if (currentImage != 0) + { + addAndMakeVisible (currentImage); + DrawableButton::resized(); + } + } + + if (currentImage != 0) + currentImage->setAlpha (opacity); } void DrawableButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { - Rectangle imageSpace; - if (style == ImageOnButtonBackground) { - const int insetX = getWidth() / 4; - const int insetY = getHeight() / 4; - - imageSpace.setBounds (insetX, insetY, getWidth() - insetX * 2, getHeight() - insetY * 2); - getLookAndFeel().drawButtonBackground (g, *this, getBackgroundColour(), isMouseOverButton, @@ -44156,13 +44219,6 @@ void DrawableButton::paintButton (Graphics& g, ? jmin (16, proportionOfHeight (0.25f)) : 0; - const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); - const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); - - imageSpace.setBounds (indentX, indentY, - getWidth() - indentX * 2, - getHeight() - indentY * 2 - textH); - if (textH > 0) { g.setFont ((float) textH); @@ -44176,38 +44232,9 @@ void DrawableButton::paintButton (Graphics& g, Justification::centred, 1); } } - - g.setImageResamplingQuality (Graphics::mediumResamplingQuality); - g.setOpacity (1.0f); - - const Drawable* imageToDraw = 0; - - if (isEnabled()) - { - imageToDraw = getCurrentImage(); - } - else - { - imageToDraw = getToggleState() ? disabledImageOn - : disabledImage; - - if (imageToDraw == 0) - { - g.setOpacity (0.4f); - imageToDraw = getNormalImage(); - } - } - - if (imageToDraw != 0) - { - if (style == ImageRaw) - imageToDraw->draw (g, 1.0f); - else - imageToDraw->drawWithin (g, imageSpace.toFloat(), RectanglePlacement::centred, 1.0f); - } } -const Drawable* DrawableButton::getCurrentImage() const throw() +Drawable* DrawableButton::getCurrentImage() const throw() { if (isDown()) return getDownImage(); @@ -44218,15 +44245,15 @@ const Drawable* DrawableButton::getCurrentImage() const throw() return getNormalImage(); } -const Drawable* DrawableButton::getNormalImage() const throw() +Drawable* DrawableButton::getNormalImage() const throw() { return (getToggleState() && normalImageOn != 0) ? normalImageOn : normalImage; } -const Drawable* DrawableButton::getOverImage() const throw() +Drawable* DrawableButton::getOverImage() const throw() { - const Drawable* d = normalImage; + Drawable* d = normalImage; if (getToggleState()) { @@ -44246,9 +44273,9 @@ const Drawable* DrawableButton::getOverImage() const throw() return d; } -const Drawable* DrawableButton::getDownImage() const throw() +Drawable* DrawableButton::getDownImage() const throw() { - const Drawable* d = normalImage; + Drawable* d = normalImage; if (getToggleState()) { @@ -44731,13 +44758,12 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_ToolbarButton.cpp ***/ BEGIN_JUCE_NAMESPACE -ToolbarButton::ToolbarButton (const int itemId_, - const String& buttonText, - Drawable* const normalImage_, - Drawable* const toggledOnImage_) +ToolbarButton::ToolbarButton (const int itemId_, const String& buttonText, + Drawable* const normalImage_, Drawable* const toggledOnImage_) : ToolbarItemComponent (itemId_, buttonText, true), normalImage (normalImage_), - toggledOnImage (toggledOnImage_) + toggledOnImage (toggledOnImage_), + currentImage (0) { jassert (normalImage_ != 0); } @@ -44746,47 +44772,61 @@ ToolbarButton::~ToolbarButton() { } -bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, - bool /*isToolbarVertical*/, - int& preferredSize, - int& minSize, int& maxSize) +bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, bool /*isToolbarVertical*/, int& preferredSize, int& minSize, int& maxSize) { preferredSize = minSize = maxSize = toolbarDepth; return true; } -void ToolbarButton::paintButtonArea (Graphics& g, - int width, int height, - bool /*isMouseOver*/, - bool /*isMouseDown*/) +void ToolbarButton::paintButtonArea (Graphics&, int /*width*/, int /*height*/, bool /*isMouseOver*/, bool /*isMouseDown*/) +{ +} + +void ToolbarButton::contentAreaChanged (const Rectangle&) +{ + buttonStateChanged(); +} + +void ToolbarButton::updateDrawable() +{ + if (currentImage != 0) + { + currentImage->setTransformToFit (getContentArea().toFloat(), RectanglePlacement::centred); + currentImage->setAlpha (isEnabled() ? 1.0f : 0.5f); + } +} + +void ToolbarButton::resized() +{ + ToolbarItemComponent::resized(); + updateDrawable(); +} + +void ToolbarButton::enablementChanged() +{ + ToolbarItemComponent::enablementChanged(); + updateDrawable(); +} + +void ToolbarButton::buttonStateChanged() { Drawable* d = normalImage; if (getToggleState() && toggledOnImage != 0) d = toggledOnImage; - const Rectangle area (0.0f, 0.0f, (float) width, (float) height); - - if (! isEnabled()) + if (d != currentImage) { - Image im (Image::ARGB, width, height, true); + removeChildComponent (currentImage); + currentImage = d; + if (d != 0) { - Graphics g2 (im); - d->drawWithin (g2, area, RectanglePlacement::centred, 1.0f); + enablementChanged(); + addAndMakeVisible (d); + updateDrawable(); } - - im.desaturate(); - g.drawImageAt (im, 0, 0); } - else - { - d->drawWithin (g, area, RectanglePlacement::centred, 1.0f); - } -} - -void ToolbarButton::contentAreaChanged (const Rectangle&) -{ } END_JUCE_NAMESPACE @@ -55708,10 +55748,6 @@ public: setMouseCursor (MouseCursor::DraggingHandCursor); } - ~ItemDragAndDropOverlayComponent() - { - } - void paint (Graphics& g) { ToolbarItemComponent* const tc = dynamic_cast (getParentComponent()); @@ -68598,8 +68634,8 @@ void MenuBarComponent::showMenu (int index) const Rectangle itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight()); - m.showMenu (itemPos + getScreenPosition(), - 0, itemPos.getWidth(), 0, 0, true, this, + m.showMenu (localAreaToGlobal (itemPos), + 0, itemPos.getWidth(), 0, 0, this, new AsyncCallback (this, index)); } } @@ -70232,7 +70268,6 @@ int PopupMenu::showMenu (const Rectangle& target, const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, - const bool alignToRectangle, Component* const componentAttachedTo, ModalComponentManager::Callback* userCallback) { @@ -70247,7 +70282,7 @@ int PopupMenu::showMenu (const Rectangle& target, callback->component = Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(), 0, target, minimumWidth, maximumNumColumns > 0 ? maximumNumColumns : 7, - standardItemHeight, alignToRectangle, itemIdThatMustBeVisible, + standardItemHeight, ! target.isEmpty(), itemIdThatMustBeVisible, &callback->managerOfChosenCommand, componentAttachedTo); if (callback->component == 0) @@ -70283,23 +70318,20 @@ int PopupMenu::show (const int itemIdThatMustBeVisible, const int standardItemHeight, ModalComponentManager::Callback* callback) { - const Point mousePos (Desktop::getMousePosition()); - - return showAt (mousePos.getX(), mousePos.getY(), - itemIdThatMustBeVisible, - minimumWidth, maximumNumColumns, - standardItemHeight, callback); + return showMenu (Rectangle().withPosition (Desktop::getMousePosition()), + itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, + standardItemHeight, 0, callback); } -int PopupMenu::showAt (const int screenX, const int screenY, +int PopupMenu::showAt (const Rectangle& screenAreaToAttachTo, const int itemIdThatMustBeVisible, const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { - return showMenu (Rectangle (screenX, screenY, 1, 1), + return showMenu (screenAreaToAttachTo, itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, - standardItemHeight, false, 0, callback); + standardItemHeight, 0, callback); } int PopupMenu::showAt (Component* componentToAttachTo, @@ -70311,16 +70343,12 @@ int PopupMenu::showAt (Component* componentToAttachTo, if (componentToAttachTo != 0) { return showMenu (componentToAttachTo->getScreenBounds(), - itemIdThatMustBeVisible, - minimumWidth, - maximumNumColumns, - standardItemHeight, - true, componentToAttachTo, callback); + itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, + standardItemHeight, componentToAttachTo, callback); } else { - return show (itemIdThatMustBeVisible, - minimumWidth, maximumNumColumns, + return show (itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, standardItemHeight, callback); } } @@ -78371,6 +78399,12 @@ ResizableWindow::ResizableWindow (const String& name, ResizableWindow::~ResizableWindow() { + // Don't delete or remove the resizer components yourself! They're managed by the + // ResizableWindow, and you should leave them alone! You may have deleted them + // accidentally by careless use of deleteAllChildren()..? + jassert (resizableCorner == 0 || getIndexOfChildComponent (resizableCorner) >= 0); + jassert (resizableBorder == 0 || getIndexOfChildComponent (resizableBorder) >= 0); + resizableCorner = 0; resizableBorder = 0; contentComponent.deleteAndZero(); @@ -81432,7 +81466,7 @@ void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) throw() } } -void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw() +void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) { if (newNumEdgesPerLine != maxEdgesPerLine) { @@ -81450,7 +81484,7 @@ void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw() } } -void EdgeTable::optimiseTable() throw() +void EdgeTable::optimiseTable() { int maxLineElements = 0; @@ -81460,7 +81494,7 @@ void EdgeTable::optimiseTable() throw() remapTableForNumEdges (maxLineElements); } -void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw() +void EdgeTable::addEdgePoint (const int x, const int y, const int winding) { jassert (y >= 0 && y < bounds.getHeight()); @@ -81524,7 +81558,7 @@ void EdgeTable::translate (float dx, const int dy) throw() } } -void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) throw() +void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) { jassert (y >= 0 && y < bounds.getHeight()); @@ -81689,7 +81723,7 @@ void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) } } -void EdgeTable::clipToRectangle (const Rectangle& r) throw() +void EdgeTable::clipToRectangle (const Rectangle& r) { const Rectangle clipped (r.getIntersection (bounds)); @@ -81731,7 +81765,7 @@ void EdgeTable::clipToRectangle (const Rectangle& r) throw() } } -void EdgeTable::excludeRectangle (const Rectangle& r) throw() +void EdgeTable::excludeRectangle (const Rectangle& r) { const Rectangle clipped (r.getIntersection (bounds)); @@ -81790,7 +81824,7 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) } } -void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) throw() +void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) { y -= bounds.getY(); @@ -82104,6 +82138,17 @@ bool Graphics::clipRegionIntersects (const Rectangle& area) const return context->clipRegionIntersects (area); } +void Graphics::beginTransparencyLayer (float layerOpacity) +{ + saveStateIfPending(); + context->beginTransparencyLayer (layerOpacity); +} + +void Graphics::endTransparencyLayer() +{ + context->endTransparencyLayer(); +} + void Graphics::setColour (const Colour& newColour) { saveStateIfPending(); @@ -82833,6 +82878,14 @@ void LowLevelGraphicsPostScriptRenderer::restoreState() stateStack.removeLast(); } +void LowLevelGraphicsPostScriptRenderer::beginTransparencyLayer (float) +{ +} + +void LowLevelGraphicsPostScriptRenderer::endTransparencyLayer() +{ +} + void LowLevelGraphicsPostScriptRenderer::writeClip() { if (needToClip) @@ -84173,6 +84226,7 @@ public: virtual const Ptr clipToPath (const Path& p, const AffineTransform& transform) = 0; virtual const Ptr clipToEdgeTable (const EdgeTable& et) = 0; virtual const Ptr clipToImageAlpha (const Image& image, const AffineTransform& t, const bool betterQuality) = 0; + virtual const Ptr translated (const Point& delta) = 0; virtual bool clipRegionIntersects (const Rectangle& r) const = 0; virtual const Rectangle getClipBounds() const = 0; @@ -84454,6 +84508,12 @@ public: return edgeTable.isEmpty() ? 0 : this; } + const Ptr translated (const Point& delta) + { + edgeTable.translate ((float) delta.getX(), delta.getY()); + return edgeTable.isEmpty() ? 0 : this; + } + bool clipRegionIntersects (const Rectangle& r) const { return edgeTable.getMaximumBounds().intersects (r); @@ -84604,6 +84664,12 @@ public: return Ptr (new ClipRegion_EdgeTable (clip))->clipToImageAlpha (image, transform, betterQuality); } + const Ptr translated (const Point& delta) + { + clip.offsetAll (delta.getX(), delta.getY()); + return clip.isEmpty() ? 0 : this; + } + bool clipRegionIntersects (const Rectangle& r) const { return clip.intersects (r); @@ -84910,21 +84976,25 @@ private: class LowLevelGraphicsSoftwareRenderer::SavedState { public: - SavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_) - : clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), - xOffset (xOffset_), yOffset (yOffset_), isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) + SavedState (const Image& image_, const Rectangle& clip_, const int xOffset_, const int yOffset_) + : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), + xOffset (xOffset_), yOffset (yOffset_), compositionAlpha (1.0f), + isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) { } - SavedState (const RectangleList& clip_, const int xOffset_, const int yOffset_) - : clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), - xOffset (xOffset_), yOffset (yOffset_), isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) + SavedState (const Image& image_, const RectangleList& clip_, const int xOffset_, const int yOffset_) + : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), + xOffset (xOffset_), yOffset (yOffset_), compositionAlpha (1.0f), + isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) { } SavedState (const SavedState& other) - : clip (other.clip), complexTransform (other.complexTransform), xOffset (other.xOffset), yOffset (other.yOffset), - isOnlyTranslated (other.isOnlyTranslated), font (other.font), fillType (other.fillType), interpolationQuality (other.interpolationQuality) + : image (other.image), clip (other.clip), complexTransform (other.complexTransform), + xOffset (other.xOffset), yOffset (other.yOffset), compositionAlpha (other.compositionAlpha), + isOnlyTranslated (other.isOnlyTranslated), font (other.font), fillType (other.fillType), + interpolationQuality (other.interpolationQuality) { } @@ -85059,6 +85129,11 @@ public: return false; } + const Rectangle getUntransformedClipBounds() const + { + return clip != 0 ? clip->getClipBounds() : Rectangle(); + } + const Rectangle getClipBounds() const { if (clip != 0) @@ -85072,7 +85147,40 @@ public: return Rectangle(); } - void fillRect (Image& image, const Rectangle& r, const bool replaceContents) + SavedState* beginTransparencyLayer (float opacity) + { + const Rectangle clip (getUntransformedClipBounds()); + + SavedState* s = new SavedState (*this); + s->image = Image (Image::ARGB, clip.getWidth(), clip.getHeight(), true); + s->compositionAlpha = opacity; + + if (s->isOnlyTranslated) + { + s->xOffset -= clip.getX(); + s->yOffset -= clip.getY(); + } + else + { + s->complexTransform = s->complexTransform.followedBy (AffineTransform::translation ((float) -clip.getX(), (float) -clip.getY())); + } + + s->cloneClipIfMultiplyReferenced(); + s->clip = s->clip->translated (-clip.getPosition()); + return s; + } + + void endTransparencyLayer (SavedState& layerState) + { + const Rectangle clip (getUntransformedClipBounds()); + + const ScopedPointer g (image.createLowLevelContext()); + g->setOpacity (layerState.compositionAlpha); + g->drawImage (layerState.image, AffineTransform::translation ((float) clip.getX(), + (float) clip.getY()), false); + } + + void fillRect (const Rectangle& r, const bool replaceContents) { if (clip != 0) { @@ -85089,19 +85197,19 @@ public: const Rectangle clipped (totalClip.getIntersection (r.translated (xOffset, yOffset))); if (! clipped.isEmpty()) - fillShape (image, new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false); + fillShape (new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false); } } else { Path p; p.addRectangle (r); - fillPath (image, p, AffineTransform::identity); + fillPath (p, AffineTransform::identity); } } } - void fillRect (Image& image, const Rectangle& r) + void fillRect (const Rectangle& r) { if (clip != 0) { @@ -85118,25 +85226,25 @@ public: const Rectangle clipped (totalClip.getIntersection (r.translated ((float) xOffset, (float) yOffset))); if (! clipped.isEmpty()) - fillShape (image, new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false); + fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false); } } else { Path p; p.addRectangle (r); - fillPath (image, p, AffineTransform::identity); + fillPath (p, AffineTransform::identity); } } } - void fillPath (Image& image, const Path& path, const AffineTransform& transform) + void fillPath (const Path& path, const AffineTransform& transform) { if (clip != 0) - fillShape (image, new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, getTransformWith (transform)), false); + fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, getTransformWith (transform)), false); } - void fillEdgeTable (Image& image, const EdgeTable& edgeTable, const float x, const int y) + void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y) { jassert (isOnlyTranslated); @@ -85145,11 +85253,11 @@ public: SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (edgeTable); SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill (edgeTableClip); edgeTableClip->edgeTable.translate (x + xOffset, y + yOffset); - fillShape (image, shapeToFill, false); + fillShape (shapeToFill, false); } } - void fillShape (Image& image, SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents) + void fillShape (SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents) { jassert (clip != 0); @@ -85181,7 +85289,7 @@ public: } else if (fillType.isTiledImage()) { - renderImage (image, fillType.image, fillType.transform, shapeToFill); + renderImage (fillType.image, fillType.transform, shapeToFill); } else { @@ -85190,11 +85298,11 @@ public: } } - void renderImage (Image& destImage, const Image& sourceImage, const AffineTransform& t, const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion) + void renderImage (const Image& sourceImage, const AffineTransform& t, const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion) { const AffineTransform transform (getTransformWith (t)); - const Image::BitmapData destData (destImage, true); + const Image::BitmapData destData (image, true); const Image::BitmapData srcData (sourceImage, false); const int alpha = fillType.colour.getAlpha(); const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); @@ -85216,7 +85324,7 @@ public: } else { - SoftwareRendererClasses::ClipRegionBase::Ptr c (new SoftwareRendererClasses::ClipRegion_EdgeTable (Rectangle (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()).getIntersection (destImage.getBounds()))); + SoftwareRendererClasses::ClipRegionBase::Ptr c (new SoftwareRendererClasses::ClipRegion_EdgeTable (Rectangle (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()).getIntersection (image.getBounds()))); c = clip->applyClipTo (c); if (c != 0) @@ -85247,11 +85355,13 @@ public: } } + Image image; SoftwareRendererClasses::ClipRegionBase::Ptr clip; private: AffineTransform complexTransform; int xOffset, yOffset; + float compositionAlpha; public: bool isOnlyTranslated; @@ -85287,16 +85397,16 @@ private: }; LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image_) - : image (image_) + : image (image_), + currentState (new SavedState (image_, image_.getBounds(), 0, 0)) { - currentState = new SavedState (image_.getBounds(), 0, 0); } LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image_, const int xOffset, const int yOffset, const RectangleList& initialClip) - : image (image_) + : image (image_), + currentState (new SavedState (image_, initialClip, xOffset, yOffset)) { - currentState = new SavedState (initialClip, xOffset, yOffset); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -85378,6 +85488,19 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() } } +void LowLevelGraphicsSoftwareRenderer::beginTransparencyLayer (float opacity) +{ + saveState(); + currentState = currentState->beginTransparencyLayer (opacity); +} + +void LowLevelGraphicsSoftwareRenderer::endTransparencyLayer() +{ + const ScopedPointer layer (currentState); + restoreState(); + currentState->endTransparencyLayer (*layer); +} + void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType) { currentState->fillType = fillType; @@ -85395,18 +85518,17 @@ void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::Resamp void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents) { - currentState->fillRect (image, r, replaceExistingContents); + currentState->fillRect (r, replaceExistingContents); } void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform) { - currentState->fillPath (image, path, transform); + currentState->fillPath (path, transform); } void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform, const bool fillEntireClipAsTiles) { - currentState->renderImage (image, sourceImage, transform, - fillEntireClipAsTiles ? currentState->clip : 0); + currentState->renderImage (sourceImage, transform, fillEntireClipAsTiles ? currentState->clip : 0); } void LowLevelGraphicsSoftwareRenderer::drawLine (const Line & line) @@ -85419,13 +85541,13 @@ void LowLevelGraphicsSoftwareRenderer::drawLine (const Line & line) void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, float top, float bottom) { if (bottom > top) - currentState->fillRect (image, Rectangle ((float) x, top, 1.0f, bottom - top)); + currentState->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, float left, float right) { if (right > left) - currentState->fillRect (image, Rectangle (left, (float) y, right - left, 1.0f)); + currentState->fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } class LowLevelGraphicsSoftwareRenderer::CachedGlyph @@ -85434,10 +85556,10 @@ public: CachedGlyph() : glyph (0), lastAccessCount (0) {} ~CachedGlyph() {} - void draw (SavedState& state, Image& image, const float x, const float y) const + void draw (SavedState& state, const float x, const float y) const { if (edgeTable != 0) - state.fillEdgeTable (image, *edgeTable, x, roundToInt (y)); + state.fillEdgeTable (*edgeTable, x, roundToInt (y)); } void generate (const Font& newFont, const int glyphNumber) @@ -85492,7 +85614,7 @@ public: juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); - void drawGlyph (SavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) + void drawGlyph (SavedState& state, const Font& font, const int glyphNumber, float x, float y) { ++accessCounter; int oldestCounter = std::numeric_limits::max(); @@ -85506,7 +85628,7 @@ public: { ++hits; glyph->lastAccessCount = accessCounter; - glyph->draw (state, image, x, y); + glyph->draw (state, x, y); return; } @@ -85532,7 +85654,7 @@ public: jassert (oldest != 0); oldest->lastAccessCount = accessCounter; oldest->generate (font, glyphNumber); - oldest->draw (state, image, x, y); + oldest->draw (state, x, y); } juce_UseDebuggingNewOperator @@ -85564,7 +85686,7 @@ void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineT if (transform.isOnlyTranslation() && currentState->isOnlyTranslated) { - GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber, + GlyphCache::getInstance()->drawGlyph (*currentState, f, glyphNumber, transform.getTranslationX(), transform.getTranslationY()); } @@ -85692,41 +85814,87 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_Drawable.cpp ***/ BEGIN_JUCE_NAMESPACE -Drawable::RenderingContext::RenderingContext (Graphics& g_, - const AffineTransform& transform_, - const float opacity_) throw() - : g (g_), - transform (transform_), - opacity (opacity_) -{ -} - Drawable::Drawable() - : parent (0) { + setInterceptsMouseClicks (false, false); + setPaintingIsUnclipped (true); } Drawable::~Drawable() { } -void Drawable::draw (Graphics& g, const float opacity, const AffineTransform& transform) const +void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const { - render (RenderingContext (g, transform, opacity)); + const_cast (this)->nonConstDraw (g, opacity, transform); } -void Drawable::drawAt (Graphics& g, const float x, const float y, const float opacity) const +void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform) +{ + g.saveState(); + const float oldOpacity = getAlpha(); + setAlpha (opacity); + g.addTransform (AffineTransform::translation ((float) -originRelativeToComponent.getX(), + (float) -originRelativeToComponent.getY()) + .followedBy (getTransform()) + .followedBy (transform)); + + paintEntireComponent (g, false); + setAlpha (oldOpacity); + g.restoreState(); +} + +void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const { draw (g, opacity, AffineTransform::translation (x, y)); } -void Drawable::drawWithin (Graphics& g, - const Rectangle& destArea, - const RectanglePlacement& placement, - const float opacity) const +void Drawable::drawWithin (Graphics& g, const Rectangle& destArea, const RectanglePlacement& placement, float opacity) const { - if (! destArea.isEmpty()) - draw (g, opacity, placement.getTransformToFit (getBounds(), destArea)); + draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea)); +} + +DrawableComposite* Drawable::getParent() const +{ + return dynamic_cast (getParentComponent()); +} + +void Drawable::transformContextToCorrectOrigin (Graphics& g) +{ + g.setOrigin (originRelativeToComponent.getX(), + originRelativeToComponent.getY()); +} + +void Drawable::markerHasMoved() +{ +} + +void Drawable::parentHierarchyChanged() +{ + setBoundsToEnclose (getDrawableBounds()); +} + +void Drawable::setBoundsToEnclose (const Rectangle& area) +{ + Drawable* const parent = getParent(); + Point parentOrigin; + if (parent != 0) + parentOrigin = parent->originRelativeToComponent; + + const Rectangle newBounds (area.getSmallestIntegerContainer() + parentOrigin); + originRelativeToComponent = parentOrigin - newBounds.getPosition(); + setBounds (newBounds); +} + +void Drawable::setOriginWithOriginalSize (const Point& originWithinParent) +{ + setTransform (AffineTransform::translation (originWithinParent.getX(), originWithinParent.getY())); +} + +void Drawable::setTransformToFit (const Rectangle& area, const RectanglePlacement& placement) +{ + if (! area.isEmpty()) + setTransform (placement.getTransformToFit (getDrawableBounds(), area)); } Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes) @@ -85785,20 +85953,17 @@ Drawable* Drawable::createChildFromValueTree (DrawableComposite* parent, const V const Identifier type (tree.getType()); Drawable* d = 0; - if (type == DrawablePath::valueTreeType) - d = new DrawablePath(); - else if (type == DrawableComposite::valueTreeType) - d = new DrawableComposite(); - else if (type == DrawableRectangle::valueTreeType) - d = new DrawableRectangle(); - else if (type == DrawableImage::valueTreeType) - d = new DrawableImage(); - else if (type == DrawableText::valueTreeType) - d = new DrawableText(); + if (type == DrawablePath::valueTreeType) d = new DrawablePath(); + else if (type == DrawableComposite::valueTreeType) d = new DrawableComposite(); + else if (type == DrawableRectangle::valueTreeType) d = new DrawableRectangle(); + else if (type == DrawableImage::valueTreeType) d = new DrawableImage(); + else if (type == DrawableText::valueTreeType) d = new DrawableText(); if (d != 0) { - d->parent = parent; + if (parent != 0) + parent->insertDrawable (d); + d->refreshFromValueTree (tree, imageProvider); } @@ -85839,18 +86004,14 @@ BEGIN_JUCE_NAMESPACE DrawableShape::DrawableShape() : strokeType (0.0f), mainFill (Colours::black), - strokeFill (Colours::black), - pathNeedsUpdating (true), - strokeNeedsUpdating (true) + strokeFill (Colours::black) { } DrawableShape::DrawableShape (const DrawableShape& other) : strokeType (other.strokeType), mainFill (other.mainFill), - strokeFill (other.strokeFill), - pathNeedsUpdating (true), - strokeNeedsUpdating (true) + strokeFill (other.strokeFill) { } @@ -85871,7 +86032,7 @@ void DrawableShape::setStrokeFill (const FillType& newFill) void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType) { strokeType = newStrokeType; - strokeNeedsUpdating = true; + strokeChanged(); } void DrawableShape::setStrokeThickness (const float newThickness) @@ -85884,18 +86045,6 @@ bool DrawableShape::isStrokeVisible() const throw() return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible(); } -void DrawableShape::setBrush (const Drawable::RenderingContext& context, const FillType& type) -{ - FillType f (type); - if (f.isGradient()) - f.gradient->multiplyOpacity (context.opacity); - else - f.setOpacity (f.getOpacity() * context.opacity); - - f.transform = f.transform.followedBy (context.transform); - context.g.setFillType (f); -} - bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, Expression::EvaluationContext* /*nameFinder*/, ImageProvider* imageProvider) @@ -85903,7 +86052,7 @@ bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, bool hasChanged = false; { - const FillType f (newState.getMainFill (parent, imageProvider)); + const FillType f (newState.getMainFill (getParent(), imageProvider)); if (mainFill != f) { @@ -85913,7 +86062,7 @@ bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, } { - const FillType f (newState.getStrokeFill (parent, imageProvider)); + const FillType f (newState.getStrokeFill (getParent(), imageProvider)); if (strokeFill != f) { @@ -85932,71 +86081,49 @@ void DrawableShape::writeTo (FillAndStrokeState& state, ImageProvider* imageProv state.setStrokeType (strokeType, undoManager); } -void DrawableShape::render (const Drawable::RenderingContext& context) const +void DrawableShape::paint (Graphics& g) { - setBrush (context, mainFill); - context.g.fillPath (getCachedPath(), context.transform); + transformContextToCorrectOrigin (g); + + g.setFillType (mainFill); + g.fillPath (path); if (isStrokeVisible()) { - setBrush (context, strokeFill); - context.g.fillPath (getCachedStrokePath(), context.transform); + g.setFillType (strokeFill); + g.fillPath (strokePath); } } void DrawableShape::pathChanged() { - pathNeedsUpdating = true; + strokeChanged(); } void DrawableShape::strokeChanged() { - strokeNeedsUpdating = true; + strokePath.clear(); + strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f); + + setBoundsToEnclose (getDrawableBounds()); + repaint(); } -void DrawableShape::invalidatePoints() -{ - pathNeedsUpdating = true; - strokeNeedsUpdating = true; -} - -const Path& DrawableShape::getCachedPath() const -{ - if (pathNeedsUpdating) - { - pathNeedsUpdating = false; - - if (rebuildPath (cachedPath)) - strokeNeedsUpdating = true; - } - - return cachedPath; -} - -const Path& DrawableShape::getCachedStrokePath() const -{ - if (strokeNeedsUpdating) - { - cachedStroke.clear(); - strokeType.createStrokedPath (cachedStroke, getCachedPath(), AffineTransform::identity, 4.0f); - strokeNeedsUpdating = false; // (must be called after getCachedPath) - } - - return cachedStroke; -} - -const Rectangle DrawableShape::getBounds() const +const Rectangle DrawableShape::getDrawableBounds() const { if (isStrokeVisible()) - return getCachedStrokePath().getBounds(); + return strokePath.getBounds(); else - return getCachedPath().getBounds(); + return path.getBounds(); } -bool DrawableShape::hitTest (float x, float y) const +bool DrawableShape::hitTest (int x, int y) const { - return getCachedPath().contains (x, y) - || (isStrokeVisible() && getCachedStrokePath().contains (x, y)); + const float globalX = (float) (x - originRelativeToComponent.getX()); + const float globalY = (float) (y - originRelativeToComponent.getY()); + + return path.contains (globalX, globalY) + || (isStrokeVisible() && strokePath.contains (globalX, globalY)); } const Identifier DrawableShape::FillAndStrokeState::type ("type"); @@ -86214,7 +86341,8 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE DrawableComposite::DrawableComposite() - : bounds (Point(), Point (100.0f, 0.0f), Point (0.0f, 100.0f)) + : bounds (Point(), Point (100.0f, 0.0f), Point (0.0f, 100.0f)), + updateBoundsReentrant (false) { setContentArea (RelativeRectangle (RelativeCoordinate (0.0), RelativeCoordinate (100.0), @@ -86226,8 +86354,8 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) { bounds = other.bounds; - for (int i = 0; i < other.drawables.size(); ++i) - drawables.add (other.drawables.getUnchecked(i)->createCopy()); + for (int i = 0; i < other.getNumDrawables(); ++i) + insertDrawable (other.getDrawable(i)->createCopy()); markersX.addCopiesOf (other.markersX); markersY.addCopiesOf (other.markersY); @@ -86235,17 +86363,23 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) DrawableComposite::~DrawableComposite() { + deleteAllChildren(); +} + +int DrawableComposite::getNumDrawables() const throw() +{ + return getNumChildComponents(); +} + +Drawable* DrawableComposite::getDrawable (int index) const +{ + return dynamic_cast (getChildComponent (index)); } void DrawableComposite::insertDrawable (Drawable* drawable, const int index) { if (drawable != 0) - { - jassert (! drawables.contains (drawable)); // trying to add a drawable that's already in here! - jassert (drawable->parent == 0); // A drawable can only live inside one parent at a time! - drawables.insert (index, drawable); - drawable->parent = this; - } + addAndMakeVisible (drawable, index); } void DrawableComposite::insertDrawable (const Drawable& drawable, const int index) @@ -86255,49 +86389,61 @@ void DrawableComposite::insertDrawable (const Drawable& drawable, const int inde void DrawableComposite::removeDrawable (const int index, const bool deleteDrawable) { - drawables.remove (index, deleteDrawable); + Drawable* const d = getDrawable (index); + + if (deleteDrawable) + delete d; + else + removeChildComponent (d); } Drawable* DrawableComposite::getDrawableWithName (const String& name) const throw() { - for (int i = drawables.size(); --i >= 0;) - if (drawables.getUnchecked(i)->getName() == name) - return drawables.getUnchecked(i); + for (int i = getNumChildComponents(); --i >= 0;) + if (getChildComponent(i)->getName() == name) + return getDrawable (i); return 0; } void DrawableComposite::bringToFront (const int index) { - if (index >= 0 && index < drawables.size() - 1) - drawables.move (index, -1); + Drawable* d = getDrawable (index); + if (d != 0) + d->toFront (false); } -void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBoundingBox) +const Rectangle DrawableComposite::getDrawableBounds() const { - bounds = newBoundingBox; + Rectangle r; + + for (int i = getNumDrawables(); --i >= 0;) + { + Drawable* const d = getDrawable(i); + + if (d != 0) + { + if (d->isTransformed()) + r = r.getUnion (d->getDrawableBounds().transformed (d->getTransform())); + else + r = r.getUnion (d->getDrawableBounds()); + } + } + + return r; } -DrawableComposite::Marker::Marker (const DrawableComposite::Marker& other) - : name (other.name), position (other.position) +void DrawableComposite::markerHasMoved() { -} + for (int i = getNumDrawables(); --i >= 0;) + { + Drawable* const d = getDrawable(i); -DrawableComposite::Marker::Marker (const String& name_, const RelativeCoordinate& position_) - : name (name_), position (position_) -{ + if (d != 0) + d->markerHasMoved(); + } } -bool DrawableComposite::Marker::operator!= (const DrawableComposite::Marker& other) const throw() -{ - return name != other.name || position != other.position; -} - -const char* const DrawableComposite::contentLeftMarkerName = "left"; -const char* const DrawableComposite::contentRightMarkerName = "right"; -const char* const DrawableComposite::contentTopMarkerName = "top"; -const char* const DrawableComposite::contentBottomMarkerName = "bottom"; - const RelativeRectangle DrawableComposite::getContentArea() const { jassert (markersX.size() >= 2 && getMarker (true, 0)->name == contentLeftMarkerName && getMarker (true, 1)->name == contentRightMarkerName); @@ -86313,6 +86459,13 @@ void DrawableComposite::setContentArea (const RelativeRectangle& newArea) setMarker (contentRightMarkerName, true, newArea.right); setMarker (contentTopMarkerName, false, newArea.top); setMarker (contentBottomMarkerName, false, newArea.bottom); + refreshTransformFromBounds(); +} + +void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBoundingBox) +{ + bounds = newBoundingBox; + refreshTransformFromBounds(); } void DrawableComposite::resetBoundingBoxToContentArea() @@ -86326,15 +86479,113 @@ void DrawableComposite::resetBoundingBoxToContentArea() void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren() { - const Rectangle bounds (getUntransformedBounds (false)); + const Rectangle activeArea (getDrawableBounds()); - setContentArea (RelativeRectangle (RelativeCoordinate (bounds.getX()), - RelativeCoordinate (bounds.getRight()), - RelativeCoordinate (bounds.getY()), - RelativeCoordinate (bounds.getBottom()))); + setContentArea (RelativeRectangle (RelativeCoordinate (activeArea.getX()), + RelativeCoordinate (activeArea.getRight()), + RelativeCoordinate (activeArea.getY()), + RelativeCoordinate (activeArea.getBottom()))); resetBoundingBoxToContentArea(); } +void DrawableComposite::refreshTransformFromBounds() +{ + Point resolved[3]; + bounds.resolveThreePoints (resolved, getParent()); + + const Rectangle content (getContentArea().resolve (getParent())); + + AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(), + content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(), + content.getX(), content.getBottom(), resolved[2].getX(), resolved[2].getY())); + + if (! t.isSingularity()) + setTransform (t); +} + +void DrawableComposite::parentHierarchyChanged() +{ + DrawableComposite* parent = getParent(); + if (parent != 0) + originRelativeToComponent = parent->originRelativeToComponent - getPosition(); +} + +void DrawableComposite::childBoundsChanged (Component*) +{ + updateBoundsToFitChildren(); +} + +void DrawableComposite::childrenChanged() +{ + updateBoundsToFitChildren(); +} + +struct RentrancyCheckSetter +{ + RentrancyCheckSetter (bool& b_) : b (b_) { b_ = true; } + ~RentrancyCheckSetter() { b = false; } + +private: + bool& b; + + RentrancyCheckSetter (const RentrancyCheckSetter&); + RentrancyCheckSetter& operator= (const RentrancyCheckSetter&); +}; + +void DrawableComposite::updateBoundsToFitChildren() +{ + if (! updateBoundsReentrant) + { + const RentrancyCheckSetter checkSetter (updateBoundsReentrant); + + Rectangle childArea; + + for (int i = getNumChildComponents(); --i >= 0;) + childArea = childArea.getUnion (getChildComponent(i)->getBoundsInParent()); + + const Point delta (childArea.getPosition()); + childArea += getPosition(); + + if (childArea != getBounds()) + { + if (! delta.isOrigin()) + { + originRelativeToComponent -= delta; + + for (int i = getNumChildComponents(); --i >= 0;) + { + Component* const c = getChildComponent(i); + + if (c != 0) + c->setBounds (c->getBounds() - delta); + } + } + + setBounds (childArea); + } + } +} + +const char* const DrawableComposite::contentLeftMarkerName = "left"; +const char* const DrawableComposite::contentRightMarkerName = "right"; +const char* const DrawableComposite::contentTopMarkerName = "top"; +const char* const DrawableComposite::contentBottomMarkerName = "bottom"; + +DrawableComposite::Marker::Marker (const DrawableComposite::Marker& other) + : name (other.name), position (other.position) +{ +} + +DrawableComposite::Marker::Marker (const String& name_, const RelativeCoordinate& position_) + : name (name_), position (position_) +{ +} + +bool DrawableComposite::Marker::operator!= (const DrawableComposite::Marker& other) const throw() +{ + return name != other.name || position != other.position; +} + int DrawableComposite::getNumMarkers (const bool xAxis) const throw() { return (xAxis ? markersX : markersY).size(); @@ -86357,7 +86608,7 @@ void DrawableComposite::setMarker (const String& name, const bool xAxis, const R if (m->position != position) { m->position = position; - invalidatePoints(); + markerHasMoved(); } return; @@ -86365,7 +86616,7 @@ void DrawableComposite::setMarker (const String& name, const bool xAxis, const R } (xAxis ? markersX : markersY).add (new Marker (name, position)); - invalidatePoints(); + markerHasMoved(); } void DrawableComposite::removeMarker (const bool xAxis, const int index) @@ -86376,55 +86627,6 @@ void DrawableComposite::removeMarker (const bool xAxis, const int index) (xAxis ? markersX : markersY).remove (index); } -const AffineTransform DrawableComposite::calculateTransform() const -{ - Point resolved[3]; - bounds.resolveThreePoints (resolved, parent); - - const Rectangle content (getContentArea().resolve (parent)); - - return AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(), - content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(), - content.getX(), content.getBottom(), resolved[2].getX(), resolved[2].getY()); -} - -void DrawableComposite::render (const Drawable::RenderingContext& context) const -{ - if (drawables.size() > 0 && context.opacity > 0) - { - if (context.opacity >= 1.0f || drawables.size() == 1) - { - Drawable::RenderingContext contextCopy (context); - contextCopy.transform = calculateTransform().followedBy (context.transform); - - for (int i = 0; i < drawables.size(); ++i) - drawables.getUnchecked(i)->render (contextCopy); - } - else - { - // To correctly render a whole composite layer with an overall transparency, - // we need to render everything opaquely into a temp buffer, then blend that - // with the target opacity... - const Rectangle clipBounds (context.g.getClipBounds()); - - if (! clipBounds.isEmpty()) - { - Image tempImage (Image::ARGB, clipBounds.getWidth(), clipBounds.getHeight(), true); - - { - Graphics tempG (tempImage); - tempG.setOrigin (-clipBounds.getX(), -clipBounds.getY()); - Drawable::RenderingContext tempContext (tempG, context.transform, 1.0f); - render (tempContext); - } - - context.g.setOpacity (context.opacity); - context.g.drawImageAt (tempImage, clipBounds.getX(), clipBounds.getY()); - } - } - } -} - const Expression DrawableComposite::getSymbolValue (const String& symbol, const String& member) const { jassert (member.isEmpty()) // the only symbols available in a Drawable are markers. @@ -86447,99 +86649,11 @@ const Expression DrawableComposite::getSymbolValue (const String& symbol, const throw Expression::EvaluationError (symbol, member); } -const Rectangle DrawableComposite::getUntransformedBounds (const bool includeMarkers) const -{ - Rectangle bounds; - - int i; - for (i = 0; i < drawables.size(); ++i) - bounds = bounds.getUnion (drawables.getUnchecked(i)->getBounds()); - - if (includeMarkers) - { - if (markersX.size() > 0) - { - float minX = std::numeric_limits::max(); - float maxX = std::numeric_limits::min(); - - for (i = markersX.size(); --i >= 0;) - { - const Marker* m = markersX.getUnchecked(i); - const float pos = (float) m->position.resolve (this); - minX = jmin (minX, pos); - maxX = jmax (maxX, pos); - } - - if (minX <= maxX) - { - if (bounds.getHeight() > 0) - { - minX = jmin (minX, bounds.getX()); - maxX = jmax (maxX, bounds.getRight()); - } - - bounds.setLeft (minX); - bounds.setWidth (maxX - minX); - } - } - - if (markersY.size() > 0) - { - float minY = std::numeric_limits::max(); - float maxY = std::numeric_limits::min(); - - for (i = markersY.size(); --i >= 0;) - { - const Marker* m = markersY.getUnchecked(i); - const float pos = (float) m->position.resolve (this); - minY = jmin (minY, pos); - maxY = jmax (maxY, pos); - } - - if (minY <= maxY) - { - if (bounds.getHeight() > 0) - { - minY = jmin (minY, bounds.getY()); - maxY = jmax (maxY, bounds.getBottom()); - } - - bounds.setTop (minY); - bounds.setHeight (maxY - minY); - } - } - } - - return bounds; -} - -const Rectangle DrawableComposite::getBounds() const -{ - return getUntransformedBounds (true).transformed (calculateTransform()); -} - -bool DrawableComposite::hitTest (float x, float y) const -{ - calculateTransform().inverted().transformPoint (x, y); - - for (int i = 0; i < drawables.size(); ++i) - if (drawables.getUnchecked(i)->hitTest (x, y)) - return true; - - return false; -} - Drawable* DrawableComposite::createCopy() const { return new DrawableComposite (*this); } -void DrawableComposite::invalidatePoints() -{ - for (int i = 0; i < drawables.size(); ++i) - drawables.getUnchecked(i)->invalidatePoints(); -} - const Identifier DrawableComposite::valueTreeType ("Group"); const Identifier DrawableComposite::ValueTreeWrapper::topLeft ("topLeft"); @@ -86735,21 +86849,14 @@ void DrawableComposite::ValueTreeWrapper::removeMarker (bool xAxis, const ValueT getMarkerList (xAxis).removeChild (state, undoManager); } -const Rectangle DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) +void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { const ValueTreeWrapper wrapper (tree); setName (wrapper.getID()); - Rectangle damage; - bool redrawAll = false; - const RelativeParallelogram newBounds (wrapper.getBoundingBox()); if (bounds != newBounds) - { - redrawAll = true; - damage = getBounds(); bounds = newBounds; - } const int numMarkersX = wrapper.getNumMarkers (true); const int numMarkersY = wrapper.getNumMarkers (false); @@ -86757,12 +86864,6 @@ const Rectangle DrawableComposite::refreshFromValueTree (const ValueTree& // Remove deleted markers... if (markersX.size() > numMarkersX || markersY.size() > numMarkersY) { - if (! redrawAll) - { - redrawAll = true; - damage = getBounds(); - } - markersX.removeRange (jmax (2, numMarkersX), markersX.size()); markersY.removeRange (jmax (2, numMarkersY), markersY.size()); } @@ -86774,19 +86875,10 @@ const Rectangle DrawableComposite::refreshFromValueTree (const ValueTree& const Marker newMarker (wrapper.getMarker (true, wrapper.getMarkerState (true, i))); Marker* m = markersX[i]; - if (m == 0 || newMarker != *m) - { - if (! redrawAll) - { - redrawAll = true; - damage = getBounds(); - } - - if (m == 0) - markersX.add (new Marker (newMarker)); - else - *m = newMarker; - } + if (m == 0) + markersX.add (new Marker (newMarker)); + else if (newMarker != *m) + *m = newMarker; } for (i = 0; i < numMarkersY; ++i) @@ -86794,78 +86886,43 @@ const Rectangle DrawableComposite::refreshFromValueTree (const ValueTree& const Marker newMarker (wrapper.getMarker (false, wrapper.getMarkerState (false, i))); Marker* m = markersY[i]; - if (m == 0 || newMarker != *m) - { - if (! redrawAll) - { - redrawAll = true; - damage = getBounds(); - } - - if (m == 0) - markersY.add (new Marker (newMarker)); - else - *m = newMarker; - } + if (m == 0) + markersY.add (new Marker (newMarker)); + else if (newMarker != *m) + *m = newMarker; } // Remove deleted drawables.. - for (i = drawables.size(); --i >= wrapper.getNumDrawables();) - { - Drawable* const d = drawables.getUnchecked(i); - - if (! redrawAll) - damage = damage.getUnion (d->getBounds()); - - d->parent = 0; - drawables.remove (i); - } + for (i = getNumDrawables(); --i >= wrapper.getNumDrawables();) + delete getDrawable(i); // Update drawables and add new ones.. for (i = 0; i < wrapper.getNumDrawables(); ++i) { const ValueTree newDrawable (wrapper.getDrawableState (i)); - Drawable* d = drawables[i]; + Drawable* d = getDrawable(i); if (d != 0) { if (newDrawable.hasType (d->getValueTreeType())) { - const Rectangle area (d->refreshFromValueTree (newDrawable, imageProvider)); - - if (! redrawAll) - damage = damage.getUnion (area); + d->refreshFromValueTree (newDrawable, imageProvider); } else { - if (! redrawAll) - damage = damage.getUnion (d->getBounds()); - - d = createChildFromValueTree (this, newDrawable, imageProvider); - drawables.set (i, d); - - if (! redrawAll) - damage = damage.getUnion (d->getBounds()); + delete d; + d = 0; } } - else + + if (d == 0) { d = createChildFromValueTree (this, newDrawable, imageProvider); - drawables.set (i, d); - - if (! redrawAll) - damage = damage.getUnion (d->getBounds()); + addAndMakeVisible (d, i); } } - invalidatePoints(); - - if (redrawAll) - damage = damage.getUnion (getBounds()); - else if (! damage.isEmpty()) - damage = damage.transformed (calculateTransform()); - - return damage; + refreshTransformFromBounds(); } const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider) const @@ -86877,8 +86934,8 @@ const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider v.setBoundingBox (bounds, 0); int i; - for (i = 0; i < drawables.size(); ++i) - v.addDrawable (drawables.getUnchecked(i)->createValueTree (imageProvider), -1, 0); + for (i = 0; i < getNumDrawables(); ++i) + v.addDrawable (getDrawable(i)->createValueTree (imageProvider), -1, 0); for (i = 0; i < markersX.size(); ++i) v.setMarker (true, *markersX.getUnchecked(i), 0); @@ -86921,12 +86978,16 @@ void DrawableImage::setImage (const Image& imageToUse) { image = imageToUse; + setBounds (imageToUse.getBounds()); + if (image.isValid()) { bounds.topLeft = RelativePoint (Point (0.0f, 0.0f)); bounds.topRight = RelativePoint (Point ((float) image.getWidth(), 0.0f)); bounds.bottomLeft = RelativePoint (Point (0.0f, (float) image.getHeight())); } + + refreshTransformFromBounds(); } void DrawableImage::setOpacity (const float newOpacity) @@ -86942,67 +87003,55 @@ void DrawableImage::setOverlayColour (const Colour& newOverlayColour) void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds) { bounds = newBounds; + refreshTransformFromBounds(); } -const AffineTransform DrawableImage::calculateTransform() const +void DrawableImage::refreshTransformFromBounds() { - if (image.isNull()) - return AffineTransform::identity; + if (! image.isNull()) + { + Point resolved[3]; + bounds.resolveThreePoints (resolved, getParent()); - Point resolved[3]; - bounds.resolveThreePoints (resolved, parent); + const Point tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth()); + const Point bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight()); - const Point tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth()); - const Point bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight()); + AffineTransform t (AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), + tr.getX(), tr.getY(), + bl.getX(), bl.getY())); - return AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), - tr.getX(), tr.getY(), - bl.getX(), bl.getY()); + if (! t.isSingularity()) + setTransform (t); + } } -void DrawableImage::render (const Drawable::RenderingContext& context) const +void DrawableImage::paint (Graphics& g) { if (image.isValid()) { - const AffineTransform t (calculateTransform().followedBy (context.transform)); - if (opacity > 0.0f && ! overlayColour.isOpaque()) { - context.g.setOpacity (context.opacity * opacity); - context.g.drawImageTransformed (image, t, false); + g.setOpacity (opacity); + g.drawImageAt (image, 0, 0, false); } if (! overlayColour.isTransparent()) { - context.g.setColour (overlayColour.withMultipliedAlpha (context.opacity)); - context.g.drawImageTransformed (image, t, true); + g.setColour (overlayColour.withMultipliedAlpha (opacity)); + g.drawImageAt (image, 0, 0, true); } } } -const Rectangle DrawableImage::getBounds() const +const Rectangle DrawableImage::getDrawableBounds() const { - if (image.isNull()) - return Rectangle(); - - return bounds.getBounds (parent); + return image.getBounds().toFloat(); } -bool DrawableImage::hitTest (float x, float y) const +bool DrawableImage::hitTest (int x, int y) const { - if (image.isNull()) - return false; - - calculateTransform().inverted().transformPoint (x, y); - - const int ix = roundToInt (x); - const int iy = roundToInt (y); - - return ix >= 0 - && iy >= 0 - && ix < image.getWidth() - && iy < image.getHeight() - && image.getPixelAt (ix, iy).getAlpha() >= 127; + return (! image.isNull()) + && image.getPixelAt (x, y).getAlpha() >= 127; } Drawable* DrawableImage::createCopy() const @@ -87010,10 +87059,6 @@ Drawable* DrawableImage::createCopy() const return new DrawableImage (*this); } -void DrawableImage::invalidatePoints() -{ -} - const Identifier DrawableImage::valueTreeType ("Image"); const Identifier DrawableImage::ValueTreeWrapper::opacity ("opacity"); @@ -87094,7 +87139,7 @@ void DrawableImage::ValueTreeWrapper::setBoundingBox (const RelativeParallelogra state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager); } -const Rectangle DrawableImage::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) +void DrawableImage::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { const ValueTreeWrapper controller (tree); setName (controller.getID()); @@ -87112,19 +87157,14 @@ const Rectangle DrawableImage::refreshFromValueTree (const ValueTree& tre const RelativeParallelogram newBounds (controller.getBoundingBox()); - if (newOpacity != opacity || overlayColour != newOverlayColour || image != newImage || bounds != newBounds) + if (newOpacity != opacity || overlayColour != newOverlayColour || image != newImage) { - const Rectangle damage (getBounds()); - + repaint(); opacity = newOpacity; overlayColour = newOverlayColour; bounds = newBounds; - image = newImage; - - return damage.getUnion (getBounds()); + setImage (newImage); } - - return Rectangle(); } const ValueTree DrawableImage::createValueTree (ImageProvider* imageProvider) const @@ -87165,7 +87205,7 @@ DrawablePath::DrawablePath (const DrawablePath& other) if (other.relativePath != 0) relativePath = new RelativePointPath (*other.relativePath); else - cachedPath = other.cachedPath; + setPath (other.path); } DrawablePath::~DrawablePath() @@ -87179,18 +87219,18 @@ Drawable* DrawablePath::createCopy() const void DrawablePath::setPath (const Path& newPath) { - cachedPath = newPath; - strokeChanged(); + path = newPath; + pathChanged(); } const Path& DrawablePath::getPath() const { - return getCachedPath(); + return path; } const Path& DrawablePath::getStrokePath() const { - return getCachedStrokePath(); + return strokePath; } bool DrawablePath::rebuildPath (Path& path) const @@ -87198,7 +87238,7 @@ bool DrawablePath::rebuildPath (Path& path) const if (relativePath != 0) { path.clear(); - relativePath->createPath (path, parent); + relativePath->createPath (path, getParent()); return true; } @@ -87558,38 +87598,31 @@ void DrawablePath::ValueTreeWrapper::Element::removePoint (UndoManager* undoMana state.getParent().removeChild (state, undoManager); } -const Rectangle DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) +void DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - Rectangle damageRect; ValueTreeWrapper v (tree); setName (v.getID()); - bool needsRedraw = refreshFillTypes (v, parent, imageProvider); + if (refreshFillTypes (v, getParent(), imageProvider)) + repaint(); ScopedPointer newRelativePath (new RelativePointPath (tree)); Path newPath; - newRelativePath->createPath (newPath, parent); + newRelativePath->createPath (newPath, getParent()); if (! newRelativePath->containsAnyDynamicPoints()) newRelativePath = 0; const PathStrokeType newStroke (v.getStrokeType()); - if (strokeType != newStroke || cachedPath != newPath) + if (strokeType != newStroke || path != newPath) { - damageRect = getBounds(); - cachedPath.swapWithPath (newPath); - strokeChanged(); + path.swapWithPath (newPath); strokeType = newStroke; - needsRedraw = true; + pathChanged(); } relativePath = newRelativePath; - - if (needsRedraw) - damageRect = damageRect.getUnion (getBounds()); - - return damageRect; } const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) const @@ -87606,7 +87639,7 @@ const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) con } else { - RelativePointPath rp (getCachedPath()); + RelativePointPath rp (path); rp.writeTo (tree, 0); } @@ -87655,13 +87688,13 @@ void DrawableRectangle::setCornerSize (const RelativePoint& newSize) bool DrawableRectangle::rebuildPath (Path& path) const { Point points[3]; - bounds.resolveThreePoints (points, parent); + bounds.resolveThreePoints (points, getParent()); const float w = Line (points[0], points[1]).getLength(); const float h = Line (points[0], points[2]).getLength(); - const float cornerSizeX = (float) cornerSize.x.resolve (parent); - const float cornerSizeY = (float) cornerSize.y.resolve (parent); + const float cornerSizeX = (float) cornerSize.x.resolve (getParent()); + const float cornerSizeY = (float) cornerSize.y.resolve (getParent()); path.clear(); @@ -87679,7 +87712,7 @@ bool DrawableRectangle::rebuildPath (Path& path) const const AffineTransform DrawableRectangle::calculateTransform() const { Point resolved[3]; - bounds.resolveThreePoints (resolved, parent); + bounds.resolveThreePoints (resolved, getParent()); return AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), resolved[1].getX(), resolved[1].getY(), @@ -87727,13 +87760,13 @@ Value DrawableRectangle::ValueTreeWrapper::getCornerSizeValue (UndoManager* undo return state.getPropertyAsValue (cornerSize, undoManager); } -const Rectangle DrawableRectangle::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) +void DrawableRectangle::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - Rectangle damageRect; ValueTreeWrapper v (tree); setName (v.getID()); - bool needsRedraw = refreshFillTypes (v, parent, imageProvider); + if (refreshFillTypes (v, getParent(), imageProvider)) + repaint(); RelativeParallelogram newBounds (v.getRectangle()); @@ -87742,19 +87775,12 @@ const Rectangle DrawableRectangle::refreshFromValueTree (const ValueTree& if (strokeType != newStroke || newBounds != bounds || newCornerSize != cornerSize) { - damageRect = getBounds(); + repaint(); bounds = newBounds; strokeType = newStroke; cornerSize = newCornerSize; pathChanged(); - strokeChanged(); - needsRedraw = true; } - - if (needsRedraw) - damageRect = damageRect.getUnion (getBounds()); - - return damageRect; } const ValueTree DrawableRectangle::createValueTree (ImageProvider* imageProvider) const @@ -87801,14 +87827,22 @@ DrawableText::~DrawableText() { } +void DrawableText::refreshBounds() +{ + setBoundsToEnclose (getDrawableBounds()); + repaint(); +} + void DrawableText::setText (const String& newText) { text = newText; + refreshBounds(); } void DrawableText::setColour (const Colour& newColour) { colour = newColour; + repaint(); } void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) @@ -87818,37 +87852,44 @@ void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) if (applySizeAndScale) { Point corners[3]; - bounds.resolveThreePoints (corners, parent); + bounds.resolveThreePoints (corners, getParent()); setFontSizeControlPoint (RelativePoint (RelativeParallelogram::getPointForInternalCoord (corners, Point (font.getHorizontalScale() * font.getHeight(), font.getHeight())))); } + + refreshBounds(); } void DrawableText::setJustification (const Justification& newJustification) { justification = newJustification; + repaint(); } void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds) { bounds = newBounds; + refreshBounds(); } void DrawableText::setFontSizeControlPoint (const RelativePoint& newPoint) { fontSizeControlPoint = newPoint; + refreshBounds(); } -void DrawableText::render (const Drawable::RenderingContext& context) const +void DrawableText::paint (Graphics& g) { + transformContextToCorrectOrigin (g); + Point points[3]; - bounds.resolveThreePoints (points, parent); + bounds.resolveThreePoints (points, getParent()); const float w = Line (points[0], points[1]).getLength(); const float h = Line (points[0], points[2]).getLength(); - const Point fontCoords (bounds.getInternalCoordForPoint (points, fontSizeControlPoint.resolve (parent))); + const Point fontCoords (bounds.getInternalCoordForPoint (points, fontSizeControlPoint.resolve (getParent()))); const float fontHeight = jlimit (0.01f, jmax (0.01f, h), fontCoords.getY()); const float fontWidth = jlimit (0.01f, jmax (0.01f, w), fontCoords.getX()); @@ -87856,27 +87897,18 @@ void DrawableText::render (const Drawable::RenderingContext& context) const f.setHeight (fontHeight); f.setHorizontalScale (fontWidth / fontHeight); - context.g.setColour (colour.withMultipliedAlpha (context.opacity)); + g.setColour (colour); GlyphArrangement ga; ga.addFittedText (f, text, 0, 0, w, h, justification, 0x100000); - ga.draw (context.g, - AffineTransform::fromTargetPoints (0, 0, points[0].getX(), points[0].getY(), - w, 0, points[1].getX(), points[1].getY(), - 0, h, points[2].getX(), points[2].getY()) - .followedBy (context.transform)); + ga.draw (g, AffineTransform::fromTargetPoints (0, 0, points[0].getX(), points[0].getY(), + w, 0, points[1].getX(), points[1].getY(), + 0, h, points[2].getX(), points[2].getY())); } -const Rectangle DrawableText::getBounds() const +const Rectangle DrawableText::getDrawableBounds() const { - return bounds.getBounds (parent); -} - -bool DrawableText::hitTest (float x, float y) const -{ - Path p; - bounds.getPath (p, parent); - return p.contains (x, y); + return bounds.getBounds (getParent()); } Drawable* DrawableText::createCopy() const @@ -87884,10 +87916,6 @@ Drawable* DrawableText::createCopy() const return new DrawableText (*this); } -void DrawableText::invalidatePoints() -{ -} - const Identifier DrawableText::valueTreeType ("Text"); const Identifier DrawableText::ValueTreeWrapper::text ("text"); @@ -87977,7 +88005,7 @@ void DrawableText::ValueTreeWrapper::setFontSizeControlPoint (const RelativePoin state.setProperty (fontSizeAnchor, p.toString(), undoManager); } -const Rectangle DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) +void DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) { ValueTreeWrapper v (tree); setName (v.getID()); @@ -87992,20 +88020,14 @@ const Rectangle DrawableText::refreshFromValueTree (const ValueTree& tree if (text != newText || font != newFont || justification != newJustification || colour != newColour || bounds != newBounds || newFontPoint != fontSizeControlPoint) { - const Rectangle damage (getBounds()); - + repaint(); setBoundingBox (newBounds); setFontSizeControlPoint (newFontPoint); setColour (newColour); setFont (newFont, false); setJustification (newJustification); setText (newText); - - return damage.getUnion (getBounds()); - } - - return Rectangle(); } const ValueTree DrawableText::createValueTree (ImageProvider*) const @@ -91377,37 +91399,21 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE AffineTransform::AffineTransform() throw() - : mat00 (1.0f), - mat01 (0), - mat02 (0), - mat10 (0), - mat11 (1.0f), - mat12 (0) + : mat00 (1.0f), mat01 (0), mat02 (0), + mat10 (0), mat11 (1.0f), mat12 (0) { } AffineTransform::AffineTransform (const AffineTransform& other) throw() - : mat00 (other.mat00), - mat01 (other.mat01), - mat02 (other.mat02), - mat10 (other.mat10), - mat11 (other.mat11), - mat12 (other.mat12) + : mat00 (other.mat00), mat01 (other.mat01), mat02 (other.mat02), + mat10 (other.mat10), mat11 (other.mat11), mat12 (other.mat12) { } -AffineTransform::AffineTransform (const float mat00_, - const float mat01_, - const float mat02_, - const float mat10_, - const float mat11_, - const float mat12_) throw() - : mat00 (mat00_), - mat01 (mat01_), - mat02 (mat02_), - mat10 (mat10_), - mat11 (mat11_), - mat12 (mat12_) +AffineTransform::AffineTransform (const float mat00_, const float mat01_, const float mat02_, + const float mat10_, const float mat11_, const float mat12_) throw() + : mat00 (mat00_), mat01 (mat01_), mat02 (mat02_), + mat10 (mat10_), mat11 (mat11_), mat12 (mat12_) { } @@ -91460,30 +91466,13 @@ const AffineTransform AffineTransform::followedBy (const AffineTransform& other) other.mat10 * mat02 + other.mat11 * mat12 + other.mat12); } -const AffineTransform AffineTransform::followedBy (const float omat00, - const float omat01, - const float omat02, - const float omat10, - const float omat11, - const float omat12) const throw() -{ - return AffineTransform (omat00 * mat00 + omat01 * mat10, - omat00 * mat01 + omat01 * mat11, - omat00 * mat02 + omat01 * mat12 + omat02, - omat10 * mat00 + omat11 * mat10, - omat10 * mat01 + omat11 * mat11, - omat10 * mat02 + omat11 * mat12 + omat12); -} - -const AffineTransform AffineTransform::translated (const float dx, - const float dy) const throw() +const AffineTransform AffineTransform::translated (const float dx, const float dy) const throw() { return AffineTransform (mat00, mat01, mat02 + dx, mat10, mat11, mat12 + dy); } -const AffineTransform AffineTransform::translation (const float dx, - const float dy) throw() +const AffineTransform AffineTransform::translation (const float dx, const float dy) throw() { return AffineTransform (1.0f, 0, dx, 0, 1.0f, dy); @@ -91494,8 +91483,12 @@ const AffineTransform AffineTransform::rotated (const float rad) const throw() const float cosRad = std::cos (rad); const float sinRad = std::sin (rad); - return followedBy (cosRad, -sinRad, 0, - sinRad, cosRad, 0); + return AffineTransform (cosRad * mat00 + -sinRad * mat10, + cosRad * mat01 + -sinRad * mat11, + cosRad * mat02 + -sinRad * mat12, + sinRad * mat00 + cosRad * mat10, + sinRad * mat01 + cosRad * mat11, + sinRad * mat02 + cosRad * mat12); } const AffineTransform AffineTransform::rotation (const float rad) throw() @@ -91507,33 +91500,27 @@ const AffineTransform AffineTransform::rotation (const float rad) throw() sinRad, cosRad, 0); } -const AffineTransform AffineTransform::rotated (const float angle, - const float pivotX, - const float pivotY) const throw() +const AffineTransform AffineTransform::rotation (const float rad, const float pivotX, const float pivotY) throw() { - return translated (-pivotX, -pivotY) - .rotated (angle) - .translated (pivotX, pivotY); + const float cosRad = std::cos (rad); + const float sinRad = std::sin (rad); + + return AffineTransform (cosRad, -sinRad, -cosRad * pivotX + sinRad * pivotY + pivotX, + sinRad, cosRad, -sinRad * pivotX + -cosRad * pivotY + pivotY); } -const AffineTransform AffineTransform::rotation (const float angle, - const float pivotX, - const float pivotY) throw() +const AffineTransform AffineTransform::rotated (const float angle, const float pivotX, const float pivotY) const throw() { - return translation (-pivotX, -pivotY) - .rotated (angle) - .translated (pivotX, pivotY); + return followedBy (rotation (angle, pivotX, pivotY)); } -const AffineTransform AffineTransform::scaled (const float factorX, - const float factorY) const throw() +const AffineTransform AffineTransform::scaled (const float factorX, const float factorY) const throw() { return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02, factorY * mat10, factorY * mat11, factorY * mat12); } -const AffineTransform AffineTransform::scale (const float factorX, - const float factorY) throw() +const AffineTransform AffineTransform::scale (const float factorX, const float factorY) throw() { return AffineTransform (factorX, 0, 0, 0, factorY, 0); @@ -91542,9 +91529,8 @@ const AffineTransform AffineTransform::scale (const float factorX, const AffineTransform AffineTransform::scaled (const float factorX, const float factorY, const float pivotX, const float pivotY) const throw() { - return translated (-pivotX, -pivotY) - .scaled (factorX, factorY) - .translated (pivotX, pivotY); + return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX), + factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY)); } const AffineTransform AffineTransform::scale (const float factorX, const float factorY, @@ -91554,11 +91540,20 @@ const AffineTransform AffineTransform::scale (const float factorX, const float f 0, factorY, pivotY * (1.0f - factorY)); } -const AffineTransform AffineTransform::sheared (const float shearX, - const float shearY) const throw() +const AffineTransform AffineTransform::shear (float shearX, float shearY) throw() { - return followedBy (1.0f, shearX, 0, - shearY, 1.0f, 0); + return AffineTransform (1.0f, shearX, 0, + shearY, 1.0f, 0); +} + +const AffineTransform AffineTransform::sheared (const float shearX, const float shearY) const throw() +{ + return AffineTransform (mat00 + shearX * mat10, + mat01 + shearX * mat11, + mat02 + shearX * mat12, + shearY * mat00 + mat10, + shearY * mat01 + mat11, + shearY * mat02 + mat12); } const AffineTransform AffineTransform::inverted() const throw() @@ -240759,6 +240754,16 @@ public: currentState = states.getLast(); } + void beginTransparencyLayer (float opacity) + { + jassertfalse; //xxx todo + } + + void endTransparencyLayer() + { + jassertfalse; //xxx todo + } + void setFill (const FillType& fillType) { currentState->setFill (fillType); @@ -244034,6 +244039,11 @@ void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool isStan DestroyCursor ((HCURSOR) cursorHandle); } +enum +{ + hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor) +}; + void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type) { LPCTSTR cursorName = IDC_ARROW; @@ -244041,7 +244051,7 @@ void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorT switch (type) { case NormalCursor: break; - case NoCursor: return 0; + case NoCursor: return (void*) hiddenMouseCursorHandle; case WaitCursor: cursorName = IDC_WAIT; break; case IBeamCursor: cursorName = IDC_IBEAM; break; case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break; @@ -244099,6 +244109,8 @@ void MouseCursor::showInWindow (ComponentPeer*) const if (c == 0) c = LoadCursor (0, IDC_ARROW); + else if (c == (HCURSOR) hiddenMouseCursorHandle) + c = 0; SetCursor (c); } @@ -266891,6 +266903,19 @@ public: } } + void beginTransparencyLayer (float opacity) + { + saveState(); + CGContextSetAlpha (context, opacity); + CGContextBeginTransparencyLayer (context, 0); + } + + void endTransparencyLayer() + { + CGContextEndTransparencyLayer (context); + restoreState(); + } + void setFill (const FillType& fillType) { state->fillType = fillType; @@ -271635,6 +271660,19 @@ public: } } + void beginTransparencyLayer (float opacity) + { + saveState(); + CGContextSetAlpha (context, opacity); + CGContextBeginTransparencyLayer (context, 0); + } + + void endTransparencyLayer() + { + CGContextEndTransparencyLayer (context); + restoreState(); + } + void setFill (const FillType& fillType) { state->fillType = fillType; diff --git a/juce_amalgamated.h b/juce_amalgamated.h index a0c0cccd7b..74670db392 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 95 +#define JUCE_BUILDNUMBER 97 /** Current Juce version number. @@ -4764,7 +4764,7 @@ public: if (howManyToRemove > numUsed) howManyToRemove = numUsed; - for (int i = 0; i < howManyToRemove; ++i) + for (int i = 1; i <= howManyToRemove; ++i) data.elements [numUsed - i].~ElementType(); numUsed -= howManyToRemove; @@ -19586,11 +19586,12 @@ public: float pivotX, float pivotY) throw(); /** Returns a transform which is the same as this one followed by a shear. - The shear is centred around the origin (0, 0). */ - const AffineTransform sheared (float shearX, - float shearY) const throw(); + const AffineTransform sheared (float shearX, float shearY) const throw(); + + /** Returns a shear transform, centred around the origin (0, 0). */ + static const AffineTransform shear (float shearX, float shearY) throw(); /** Returns a matrix which is the inverse operation of this one. @@ -19648,11 +19649,6 @@ public: float mat10, mat11, mat12; juce_UseDebuggingNewOperator - -private: - - const AffineTransform followedBy (float mat00, float mat01, float mat02, - float mat10, float mat11, float mat12) const throw(); }; #endif // __JUCE_AFFINETRANSFORM_JUCEHEADER__ @@ -24949,6 +24945,23 @@ public: */ void restoreState(); + /** Begins rendering to an off-screen bitmap which will later be flattened onto the current + context with the given opacity. + + The context uses an internal stack of temporary image layers to do this. When you've + finished drawing to the layer, call endTransparencyLayer() to complete the operation and + composite the finished layer. Every call to beginTransparencyLayer() MUST be matched + by a corresponding call to endTransparencyLayer()! + + This call also saves the current state, and endTransparencyLayer() restores it. + */ + void beginTransparencyLayer (float layerOpacity); + + /** Completes a drawing operation to a temporary semi-transparent buffer. + See beginTransparencyLayer() for more details. + */ + void endTransparencyLayer(); + /** Moves the position of the context's origin. This changes the position that the context considers to be (0, 0) to @@ -26182,6 +26195,15 @@ public: */ const Rectangle getLocalBounds() const throw(); + /** Returns the area of this component's parent which this component covers. + + The returned area is relative to the parent's coordinate space. + If the component has an affine transform specified, then the resulting area will be + the smallest rectangle that fully covers the component's transformed bounding box. + If this component has no parent, the return value will simply be the same as getBounds(). + */ + const Rectangle getBoundsInParent() const throw(); + /** Returns the region of this component that's not obscured by other, opaque components. The RectangleList that is returned represents the area of this component @@ -27136,7 +27158,18 @@ public: */ virtual void enablementChanged(); + /** Changes the transparency of this component. + When painted, the entire component and all its children will be rendered + with this as the overall opacity level, where 0 is completely invisible, and + 1.0 is fully opaque (i.e. normal). + + @see getAlpha + */ void setAlpha (float newAlpha); + + /** Returns the component's current transparancy level. + See setAlpha() for more details. + */ float getAlpha() const; /** Changes the mouse cursor shape to use when the mouse is over this component. @@ -37094,14 +37127,15 @@ public: This is the same as show(), but uses a specific location (in global screen co-ordinates) rather than the current mouse position. - Note that the co-ordinates don't specify the top-left of the menu - they - indicate a point of interest, and the menu will position itself nearby to - this point, trying to keep it fully on-screen. + The screenAreaToAttachTo parameter indicates a screen area to which the menu + will be adjacent. Depending on where this is, the menu will decide which edge to + attach itself to, in order to fit itself fully on-screen. If you just want to + trigger a menu at a specific point, you can pass in a rectangle of size (0, 0) + with the position that you want. @see show() */ - int showAt (int screenX, - int screenY, + int showAt (const Rectangle& screenAreaToAttachTo, int itemIdThatMustBeVisible = 0, int minimumWidth = 0, int maximumNumColumns = 0, @@ -37229,14 +37263,9 @@ private: void addSeparatorIfPending(); - int showMenu (const Rectangle& target, - int itemIdThatMustBeVisible, - int minimumWidth, - int maximumNumColumns, - int standardItemHeight, - bool alignToRectangle, - Component* componentAttachedTo, - ModalComponentManager::Callback* callback); + int showMenu (const Rectangle& target, int itemIdThatMustBeVisible, + int minimumWidth, int maximumNumColumns, int standardItemHeight, + Component* componentAttachedTo, ModalComponentManager::Callback* callback); }; #endif // __JUCE_POPUPMENU_JUCEHEADER__ @@ -40605,6 +40634,12 @@ public: /** The name of the plugin. */ String name; + /** A more descriptive name for the plugin. + This may be the same as the 'name' field, but some plugins may provide an + alternative name. + */ + String descriptiveName; + /** The plugin format, e.g. "VST", "AudioUnit", etc. */ String pluginFormatName; @@ -44814,7 +44849,7 @@ class DrawableComposite; @see DrawableComposite, DrawableImage, DrawablePath, DrawableText */ -class JUCE_API Drawable +class JUCE_API Drawable : public Component { protected: @@ -44835,6 +44870,11 @@ public: virtual Drawable* createCopy() const = 0; /** Renders this Drawable object. + + Note that the preferred way to render a drawable in future is by using it + as a component and adding it to a parent, so you might want to consider that + before using this method. + @see drawWithin */ void draw (Graphics& g, float opacity, @@ -44848,10 +44888,12 @@ public: @code draw (g, AffineTransform::translation (x, y)). @endcode + + Note that the preferred way to render a drawable in future is by using it + as a component and adding it to a parent, so you might want to consider that + before using this method. */ - void drawAt (Graphics& g, - float x, float y, - float opacity) const; + void drawAt (Graphics& g, float x, float y, float opacity) const; /** Renders the Drawable within a rectangle, scaling it to fit neatly inside without changing its aspect-ratio. @@ -44859,6 +44901,10 @@ public: The object can placed arbitrarily within the rectangle based on a Justification type, and can either be made as big as possible, or just reduced to fit. + Note that the preferred way to render a drawable in future is by using it + as a component and adding it to a parent, so you might want to consider that + before using this method. + @param g the graphics context to render onto @param destArea the target rectangle to fit the drawable into @param placement defines the alignment and rescaling to use to fit @@ -44870,49 +44916,18 @@ public: const RectanglePlacement& placement, float opacity) const; - /** Holds the information needed when telling a drawable to render itself. - @see Drawable::draw + /** Resets any transformations on this drawable, and positions its origin within + its parent component. */ - class RenderingContext - { - public: - RenderingContext (Graphics& g, const AffineTransform& transform, float opacity) throw(); + void setOriginWithOriginalSize (const Point& originWithinParent); - Graphics& g; - AffineTransform transform; - float opacity; - - private: - RenderingContext& operator= (const RenderingContext&); - }; - - /** Renders this Drawable object. - @see draw + /** Sets a transform for this drawable that will position it within the specified + area of its parent component. */ - virtual void render (const RenderingContext& context) const = 0; - - /** Returns the smallest rectangle that can contain this Drawable object. - - Co-ordinates are relative to the object's own origin. - */ - virtual const Rectangle getBounds() const = 0; - - /** Returns true if the given point is somewhere inside this Drawable. - - Co-ordinates are relative to the object's own origin. - */ - virtual bool hitTest (float x, float y) const = 0; - - /** Returns the name given to this drawable. - @see setName - */ - const String& getName() const throw() { return name; } - - /** Assigns a name to this drawable. */ - void setName (const String& newName) throw() { name = newName; } + void setTransformToFit (const Rectangle& areaInParent, const RectanglePlacement& placement); /** Returns the DrawableComposite that contains this object, if there is one. */ - DrawableComposite* getParent() const throw() { return parent; } + DrawableComposite* getParent() const; /** Tries to turn some kind of image file into a drawable. @@ -44982,7 +44997,7 @@ public: /** Tries to refresh a Drawable from the same ValueTree that was used to create it. @returns the damage rectangle that will need repainting due to any changes that were made. */ - virtual const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) = 0; + virtual void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) = 0; /** Creates a ValueTree to represent this Drawable. The VarTree that is returned can be turned back into a Drawable with @@ -44995,6 +45010,12 @@ public: /** Returns the tag ID that is used for a ValueTree that stores this type of drawable. */ virtual const Identifier getValueTreeType() const = 0; + /** Returns the area that this drawble covers. + The result is expressed in this drawable's own coordinate space, and does not take + into account any transforms that may be applied to the component. + */ + virtual const Rectangle getDrawableBounds() const = 0; + /** Internal class used to manage ValueTrees that represent Drawables. */ class ValueTreeWrapperBase { @@ -45015,15 +45036,23 @@ public: protected: friend class DrawableComposite; - /** @internal */ - DrawableComposite* parent; - /** @internal */ - virtual void invalidatePoints() = 0; + friend class DrawableShape; + /** @internal */ static Drawable* createChildFromValueTree (DrawableComposite* parent, const ValueTree& tree, ImageProvider* imageProvider); + /** @internal */ + void transformContextToCorrectOrigin (Graphics& g); + /** @internal */ + void markerHasMoved(); + /** @internal */ + void parentHierarchyChanged(); + /** @internal */ + void setBoundsToEnclose (const Rectangle& area); + + Point originRelativeToComponent; private: - String name; + void nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform); Drawable (const Drawable&); Drawable& operator= (const Drawable&); @@ -45143,10 +45172,10 @@ public: void setEdgeIndent (int numPixelsIndent); /** Returns the image that the button is currently displaying. */ - const Drawable* getCurrentImage() const throw(); - const Drawable* getNormalImage() const throw(); - const Drawable* getOverImage() const throw(); - const Drawable* getDownImage() const throw(); + Drawable* getCurrentImage() const throw(); + Drawable* getNormalImage() const throw(); + Drawable* getOverImage() const throw(); + Drawable* getDownImage() const throw(); /** A set of colour IDs to use to change the colour of various aspects of the link. @@ -45167,16 +45196,20 @@ protected: void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); + /** @internal */ + void buttonStateChanged(); + /** @internal */ + void resized(); private: ButtonStyle style; ScopedPointer normalImage, overImage, downImage, disabledImage; ScopedPointer normalImageOn, overImageOn, downImageOn, disabledImageOn; + Drawable* currentImage; Colour backgroundOff, backgroundOn; int edgeIndent; - void deleteImages(); DrawableButton (const DrawableButton&); DrawableButton& operator= (const DrawableButton&); }; @@ -46347,11 +46380,20 @@ public: void paintButtonArea (Graphics& g, int width, int height, bool isMouseOver, bool isMouseDown); /** @internal */ void contentAreaChanged (const Rectangle& newBounds); + /** @internal */ + void buttonStateChanged(); + /** @internal */ + void resized(); + /** @internal */ + void enablementChanged(); juce_UseDebuggingNewOperator private: - ScopedPointer normalImage, toggledOnImage; + ScopedPointer normalImage, toggledOnImage; + Drawable* currentImage; + + void updateDrawable(); ToolbarButton (const ToolbarButton&); ToolbarButton& operator= (const ToolbarButton&); @@ -59940,10 +59982,10 @@ public: /** Destructor. */ ~EdgeTable(); - void clipToRectangle (const Rectangle& r) throw(); - void excludeRectangle (const Rectangle& r) throw(); + void clipToRectangle (const Rectangle& r); + void excludeRectangle (const Rectangle& r); void clipToEdgeTable (const EdgeTable& other); - void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) throw(); + void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels); bool isEmpty() throw(); const Rectangle& getMaximumBounds() const throw() { return bounds; } void translate (float dx, int dy) throw(); @@ -59953,7 +59995,7 @@ public: This will shrink the table down to use as little memory as possible - useful for read-only tables that get stored and re-used for rendering. */ - void optimiseTable() throw(); + void optimiseTable(); /** Iterates the lines in the table, for rendering. @@ -60061,9 +60103,9 @@ private: int maxEdgesPerLine, lineStrideElements; bool needToCheckEmptinesss; - void addEdgePoint (int x, int y, int winding) throw(); - void remapTableForNumEdges (int newNumEdgesPerLine) throw(); - void intersectWithEdgeTableLine (int y, const int* otherLine) throw(); + void addEdgePoint (int x, int y, int winding); + void remapTableForNumEdges (int newNumEdgesPerLine); + void intersectWithEdgeTableLine (int y, const int* otherLine); void clipEdgeTableLineToRange (int* line, int x1, int x2) throw(); void sanitiseLevels (bool useNonZeroWinding) throw(); static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) throw(); @@ -60247,6 +60289,9 @@ public: virtual void saveState() = 0; virtual void restoreState() = 0; + virtual void beginTransparencyLayer (float opacity) = 0; + virtual void endTransparencyLayer() = 0; + virtual void setFill (const FillType& fillType) = 0; virtual void setOpacity (float newOpacity) = 0; virtual void setInterpolationQuality (Graphics::ResamplingQuality quality) = 0; @@ -60305,6 +60350,9 @@ public: void saveState(); void restoreState(); + void beginTransparencyLayer (float opacity); + void endTransparencyLayer(); + bool clipRegionIntersects (const Rectangle& r); const Rectangle getClipBounds() const; bool isClipEmpty() const; @@ -60407,6 +60455,9 @@ public: void saveState(); void restoreState(); + void beginTransparencyLayer (float opacity); + void endTransparencyLayer(); + void setFill (const FillType& fillType); void setOpacity (float opacity); void setInterpolationQuality (Graphics::ResamplingQuality quality); @@ -60524,7 +60575,7 @@ public: @see getDrawable */ - int getNumDrawables() const throw() { return drawables.size(); } + int getNumDrawables() const throw(); /** Returns one of the drawables that are contained in this one. @@ -60536,7 +60587,7 @@ public: @see getNumDrawables */ - Drawable* getDrawable (int index) const throw() { return drawables [index]; } + Drawable* getDrawable (int index) const; /** Looks for a child drawable with the specified name. */ Drawable* getDrawableWithName (const String& name) const throw(); @@ -60549,20 +60600,6 @@ public: */ void bringToFront (int index); - /** Returns the main content rectangle. - The content area is actually defined by the markers named "left", "right", "top" and - "bottom", but this method is a shortcut that returns them all at once. - @see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName - */ - const RelativeRectangle getContentArea() const; - - /** Changes the main content area. - The content area is actually defined by the markers named "left", "right", "top" and - "bottom", but this method is a shortcut that sets them all at once. - @see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName - */ - void setContentArea (const RelativeRectangle& newArea); - /** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered. @see setContentArea */ @@ -60578,6 +60615,20 @@ public: */ void resetBoundingBoxToContentArea(); + /** Returns the main content rectangle. + The content area is actually defined by the markers named "left", "right", "top" and + "bottom", but this method is a shortcut that returns them all at once. + @see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName + */ + const RelativeRectangle getContentArea() const; + + /** Changes the main content area. + The content area is actually defined by the markers named "left", "right", "top" and + "bottom", but this method is a shortcut that sets them all at once. + @see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName + */ + void setContentArea (const RelativeRectangle& newArea); + /** Resets the content area and the bounding transform to fit around the area occupied by the child components (ignoring any markers). */ @@ -60610,18 +60661,10 @@ public: /** The name of the marker that defines the bottom edge of the content area. */ static const char* const contentBottomMarkerName; - /** @internal */ - void render (const Drawable::RenderingContext& context) const; - /** @internal */ - const Rectangle getBounds() const; - /** @internal */ - bool hitTest (float x, float y) const; /** @internal */ Drawable* createCopy() const; /** @internal */ - void invalidatePoints(); - /** @internal */ - const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); + void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ const ValueTree createValueTree (ImageProvider* imageProvider) const; /** @internal */ @@ -60630,6 +60673,16 @@ public: const Identifier getValueTreeType() const { return valueTreeType; } /** @internal */ const Expression getSymbolValue (const String& symbol, const String& member) const; + /** @internal */ + const Rectangle getDrawableBounds() const; + /** @internal */ + void markerHasMoved(); + /** @internal */ + void childBoundsChanged (Component*); + /** @internal */ + void childrenChanged(); + /** @internal */ + void parentHierarchyChanged(); /** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */ class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase @@ -60674,12 +60727,12 @@ public: juce_UseDebuggingNewOperator private: - OwnedArray drawables; RelativeParallelogram bounds; OwnedArray markersX, markersY; + bool updateBoundsReentrant; - const Rectangle getUntransformedBounds (bool includeMarkers) const; - const AffineTransform calculateTransform() const; + void refreshTransformFromBounds(); + void updateBoundsToFitChildren(); DrawableComposite& operator= (const DrawableComposite&); }; @@ -60746,17 +60799,15 @@ public: const RelativeParallelogram& getBoundingBox() const throw() { return bounds; } /** @internal */ - void render (const Drawable::RenderingContext& context) const; + void paint (Graphics& g); /** @internal */ - const Rectangle getBounds() const; - /** @internal */ - bool hitTest (float x, float y) const; + bool hitTest (int x, int y) const; /** @internal */ Drawable* createCopy() const; /** @internal */ - void invalidatePoints(); + const Rectangle getDrawableBounds() const; /** @internal */ - const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); + void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ const ValueTree createValueTree (ImageProvider* imageProvider) const; /** @internal */ @@ -60796,7 +60847,7 @@ private: Colour overlayColour; RelativeParallelogram bounds; - const AffineTransform calculateTransform() const; + void refreshTransformFromBounds(); DrawableImage& operator= (const DrawableImage&); }; @@ -60910,13 +60961,11 @@ public: }; /** @internal */ - void invalidatePoints(); + const Rectangle getDrawableBounds() const; /** @internal */ - void render (const Drawable::RenderingContext& context) const; + void paint (Graphics& g); /** @internal */ - const Rectangle getBounds() const; - /** @internal */ - bool hitTest (float x, float y) const; + bool hitTest (int x, int y) const; protected: @@ -60939,19 +60988,11 @@ protected: /** Writes the stroke and fill details to a FillAndStrokeState object. */ void writeTo (FillAndStrokeState& state, ImageProvider* imageProvider, UndoManager* undoManager) const; - /** Returns the current cached path outline. */ - const Path& getCachedPath() const; - /** Returns the current cached stroke outline. */ - const Path& getCachedStrokePath() const; - PathStrokeType strokeType; - mutable Path cachedPath, cachedStroke; + Path path, strokePath; private: FillType mainFill, strokeFill; - mutable bool pathNeedsUpdating, strokeNeedsUpdating; - - static void setBrush (const Drawable::RenderingContext& context, const FillType& type); DrawableShape& operator= (const DrawableShape&); }; @@ -60991,7 +61032,7 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); + void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ const ValueTree createValueTree (ImageProvider* imageProvider) const; /** @internal */ @@ -61105,7 +61146,7 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); + void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ const ValueTree createValueTree (ImageProvider* imageProvider) const; /** @internal */ @@ -61211,23 +61252,19 @@ public: void setFontSizeControlPoint (const RelativePoint& newPoint); /** @internal */ - void render (const Drawable::RenderingContext& context) const; - /** @internal */ - const Rectangle getBounds() const; - /** @internal */ - bool hitTest (float x, float y) const; + void paint (Graphics& g); /** @internal */ Drawable* createCopy() const; /** @internal */ - void invalidatePoints(); - /** @internal */ - const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); + void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ const ValueTree createValueTree (ImageProvider* imageProvider) const; /** @internal */ static const Identifier valueTreeType; /** @internal */ const Identifier getValueTreeType() const { return valueTreeType; } + /** @internal */ + const Rectangle getDrawableBounds() const; /** Internally-used class for wrapping a DrawableText's state into a ValueTree. */ class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase @@ -61268,6 +61305,8 @@ private: Colour colour; Justification justification; + void refreshBounds(); + DrawableText& operator= (const DrawableText&); }; diff --git a/src/containers/juce_Array.h b/src/containers/juce_Array.h index 06dc00405e..b3cafb2b18 100644 --- a/src/containers/juce_Array.h +++ b/src/containers/juce_Array.h @@ -779,7 +779,7 @@ public: if (howManyToRemove > numUsed) howManyToRemove = numUsed; - for (int i = 0; i < howManyToRemove; ++i) + for (int i = 1; i <= howManyToRemove; ++i) data.elements [numUsed - i].~ElementType(); numUsed -= howManyToRemove; diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index f845075805..8a69e9b4d6 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 96 +#define JUCE_BUILDNUMBER 97 /** Current Juce version number. diff --git a/src/gui/components/buttons/juce_Button.cpp b/src/gui/components/buttons/juce_Button.cpp index abaed71e7f..905bd70b77 100644 --- a/src/gui/components/buttons/juce_Button.cpp +++ b/src/gui/components/buttons/juce_Button.cpp @@ -431,7 +431,7 @@ void Button::mouseUp (const MouseEvent& e) internalClickCallback (e.mods); } -void Button::mouseDrag (const MouseEvent& e) +void Button::mouseDrag (const MouseEvent&) { const ButtonState oldState = buttonState; updateState (isMouseOver(), true); diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp index 9fe144a0b6..2b13beed85 100644 --- a/src/gui/components/juce_Component.cpp +++ b/src/gui/components/juce_Component.cpp @@ -1836,7 +1836,7 @@ void Component::paintComponentAndChildren (Graphics& g) g.saveState(); g.addTransform (*child.affineTransform_); - if (child.flags.dontClipGraphicsFlag || g.reduceClipRegion (child.getBounds())) + if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds())) child.paintWithinParentContext (g); g.restoreState(); @@ -1904,17 +1904,9 @@ void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel) { if (componentTransparency < 255) { - Image temp (flags.opaqueFlag ? Image::RGB : Image::ARGB, - getWidth(), getHeight(), ! flags.opaqueFlag, Image::NativeImage); - - { - Graphics tempG (temp); - tempG.reduceClipRegion (g.getClipBounds()); - paintEntireComponent (tempG, true); - } - - g.setColour (Colours::black.withAlpha (getAlpha())); - g.drawImageAt (temp, 0, 0); + g.beginTransparencyLayer (getAlpha()); + paintComponentAndChildren (g); + g.endTransparencyLayer(); } } else diff --git a/src/gui/graphics/contexts/juce_EdgeTable.cpp b/src/gui/graphics/contexts/juce_EdgeTable.cpp index 70f636daae..aac399eb1a 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.cpp +++ b/src/gui/graphics/contexts/juce_EdgeTable.cpp @@ -329,7 +329,7 @@ void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) throw() } } -void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw() +void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) { if (newNumEdgesPerLine != maxEdgesPerLine) { @@ -347,7 +347,7 @@ void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw() } } -void EdgeTable::optimiseTable() throw() +void EdgeTable::optimiseTable() { int maxLineElements = 0; @@ -357,7 +357,7 @@ void EdgeTable::optimiseTable() throw() remapTableForNumEdges (maxLineElements); } -void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw() +void EdgeTable::addEdgePoint (const int x, const int y, const int winding) { jassert (y >= 0 && y < bounds.getHeight()); @@ -421,7 +421,7 @@ void EdgeTable::translate (float dx, const int dy) throw() } } -void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) throw() +void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) { jassert (y >= 0 && y < bounds.getHeight()); @@ -588,7 +588,7 @@ void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) //============================================================================== -void EdgeTable::clipToRectangle (const Rectangle& r) throw() +void EdgeTable::clipToRectangle (const Rectangle& r) { const Rectangle clipped (r.getIntersection (bounds)); @@ -630,7 +630,7 @@ void EdgeTable::clipToRectangle (const Rectangle& r) throw() } } -void EdgeTable::excludeRectangle (const Rectangle& r) throw() +void EdgeTable::excludeRectangle (const Rectangle& r) { const Rectangle clipped (r.getIntersection (bounds)); @@ -689,7 +689,7 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) } } -void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) throw() +void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) { y -= bounds.getY(); diff --git a/src/gui/graphics/contexts/juce_EdgeTable.h b/src/gui/graphics/contexts/juce_EdgeTable.h index 86b6884e10..5477a68984 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.h +++ b/src/gui/graphics/contexts/juce_EdgeTable.h @@ -79,10 +79,10 @@ public: ~EdgeTable(); //============================================================================== - void clipToRectangle (const Rectangle& r) throw(); - void excludeRectangle (const Rectangle& r) throw(); + void clipToRectangle (const Rectangle& r); + void excludeRectangle (const Rectangle& r); void clipToEdgeTable (const EdgeTable& other); - void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) throw(); + void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels); bool isEmpty() throw(); const Rectangle& getMaximumBounds() const throw() { return bounds; } void translate (float dx, int dy) throw(); @@ -92,7 +92,7 @@ public: This will shrink the table down to use as little memory as possible - useful for read-only tables that get stored and re-used for rendering. */ - void optimiseTable() throw(); + void optimiseTable(); //============================================================================== @@ -203,9 +203,9 @@ private: int maxEdgesPerLine, lineStrideElements; bool needToCheckEmptinesss; - void addEdgePoint (int x, int y, int winding) throw(); - void remapTableForNumEdges (int newNumEdgesPerLine) throw(); - void intersectWithEdgeTableLine (int y, const int* otherLine) throw(); + void addEdgePoint (int x, int y, int winding); + void remapTableForNumEdges (int newNumEdgesPerLine); + void intersectWithEdgeTableLine (int y, const int* otherLine); void clipEdgeTableLineToRange (int* line, int x1, int x2) throw(); void sanitiseLevels (bool useNonZeroWinding) throw(); static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) throw(); diff --git a/src/gui/graphics/contexts/juce_Graphics.cpp b/src/gui/graphics/contexts/juce_Graphics.cpp index 9dd267037a..23391747b7 100644 --- a/src/gui/graphics/contexts/juce_Graphics.cpp +++ b/src/gui/graphics/contexts/juce_Graphics.cpp @@ -176,6 +176,17 @@ bool Graphics::clipRegionIntersects (const Rectangle& area) const return context->clipRegionIntersects (area); } +void Graphics::beginTransparencyLayer (float layerOpacity) +{ + saveStateIfPending(); + context->beginTransparencyLayer (layerOpacity); +} + +void Graphics::endTransparencyLayer() +{ + context->endTransparencyLayer(); +} + //============================================================================== void Graphics::setColour (const Colour& newColour) { diff --git a/src/gui/graphics/contexts/juce_Graphics.h b/src/gui/graphics/contexts/juce_Graphics.h index d71fa6ad8a..84cbc963aa 100644 --- a/src/gui/graphics/contexts/juce_Graphics.h +++ b/src/gui/graphics/contexts/juce_Graphics.h @@ -613,6 +613,23 @@ public: */ void restoreState(); + /** Begins rendering to an off-screen bitmap which will later be flattened onto the current + context with the given opacity. + + The context uses an internal stack of temporary image layers to do this. When you've + finished drawing to the layer, call endTransparencyLayer() to complete the operation and + composite the finished layer. Every call to beginTransparencyLayer() MUST be matched + by a corresponding call to endTransparencyLayer()! + + This call also saves the current state, and endTransparencyLayer() restores it. + */ + void beginTransparencyLayer (float layerOpacity); + + /** Completes a drawing operation to a temporary semi-transparent buffer. + See beginTransparencyLayer() for more details. + */ + void endTransparencyLayer(); + /** Moves the position of the context's origin. This changes the position that the context considers to be (0, 0) to diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h index d543457212..d0a675943e 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h @@ -81,6 +81,9 @@ public: virtual void saveState() = 0; virtual void restoreState() = 0; + virtual void beginTransparencyLayer (float opacity) = 0; + virtual void endTransparencyLayer() = 0; + //============================================================================== virtual void setFill (const FillType& fillType) = 0; virtual void setOpacity (float newOpacity) = 0; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index 034cb5c6fe..10f5543347 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -192,6 +192,14 @@ void LowLevelGraphicsPostScriptRenderer::restoreState() stateStack.removeLast(); } +void LowLevelGraphicsPostScriptRenderer::beginTransparencyLayer (float) +{ +} + +void LowLevelGraphicsPostScriptRenderer::endTransparencyLayer() +{ +} + //============================================================================== void LowLevelGraphicsPostScriptRenderer::writeClip() { diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h index d8f0e1990c..0306e4ee3e 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h @@ -60,6 +60,9 @@ public: void saveState(); void restoreState(); + void beginTransparencyLayer (float opacity); + void endTransparencyLayer(); + bool clipRegionIntersects (const Rectangle& r); const Rectangle getClipBounds() const; bool isClipEmpty() const; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index caf8e934be..82799f3d7c 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -1045,6 +1045,7 @@ public: virtual const Ptr clipToPath (const Path& p, const AffineTransform& transform) = 0; virtual const Ptr clipToEdgeTable (const EdgeTable& et) = 0; virtual const Ptr clipToImageAlpha (const Image& image, const AffineTransform& t, const bool betterQuality) = 0; + virtual const Ptr translated (const Point& delta) = 0; virtual bool clipRegionIntersects (const Rectangle& r) const = 0; virtual const Rectangle getClipBounds() const = 0; @@ -1327,6 +1328,12 @@ public: return edgeTable.isEmpty() ? 0 : this; } + const Ptr translated (const Point& delta) + { + edgeTable.translate ((float) delta.getX(), delta.getY()); + return edgeTable.isEmpty() ? 0 : this; + } + bool clipRegionIntersects (const Rectangle& r) const { return edgeTable.getMaximumBounds().intersects (r); @@ -1479,6 +1486,12 @@ public: return Ptr (new ClipRegion_EdgeTable (clip))->clipToImageAlpha (image, transform, betterQuality); } + const Ptr translated (const Point& delta) + { + clip.offsetAll (delta.getX(), delta.getY()); + return clip.isEmpty() ? 0 : this; + } + bool clipRegionIntersects (const Rectangle& r) const { return clip.intersects (r); @@ -1788,21 +1801,25 @@ private: class LowLevelGraphicsSoftwareRenderer::SavedState { public: - SavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_) - : clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), - xOffset (xOffset_), yOffset (yOffset_), isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) + SavedState (const Image& image_, const Rectangle& clip_, const int xOffset_, const int yOffset_) + : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), + xOffset (xOffset_), yOffset (yOffset_), compositionAlpha (1.0f), + isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) { } - SavedState (const RectangleList& clip_, const int xOffset_, const int yOffset_) - : clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), - xOffset (xOffset_), yOffset (yOffset_), isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) + SavedState (const Image& image_, const RectangleList& clip_, const int xOffset_, const int yOffset_) + : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), + xOffset (xOffset_), yOffset (yOffset_), compositionAlpha (1.0f), + isOnlyTranslated (true), interpolationQuality (Graphics::mediumResamplingQuality) { } SavedState (const SavedState& other) - : clip (other.clip), complexTransform (other.complexTransform), xOffset (other.xOffset), yOffset (other.yOffset), - isOnlyTranslated (other.isOnlyTranslated), font (other.font), fillType (other.fillType), interpolationQuality (other.interpolationQuality) + : image (other.image), clip (other.clip), complexTransform (other.complexTransform), + xOffset (other.xOffset), yOffset (other.yOffset), compositionAlpha (other.compositionAlpha), + isOnlyTranslated (other.isOnlyTranslated), font (other.font), fillType (other.fillType), + interpolationQuality (other.interpolationQuality) { } @@ -1937,6 +1954,11 @@ public: return false; } + const Rectangle getUntransformedClipBounds() const + { + return clip != 0 ? clip->getClipBounds() : Rectangle(); + } + const Rectangle getClipBounds() const { if (clip != 0) @@ -1950,8 +1972,41 @@ public: return Rectangle(); } + SavedState* beginTransparencyLayer (float opacity) + { + const Rectangle clip (getUntransformedClipBounds()); + + SavedState* s = new SavedState (*this); + s->image = Image (Image::ARGB, clip.getWidth(), clip.getHeight(), true); + s->compositionAlpha = opacity; + + if (s->isOnlyTranslated) + { + s->xOffset -= clip.getX(); + s->yOffset -= clip.getY(); + } + else + { + s->complexTransform = s->complexTransform.followedBy (AffineTransform::translation ((float) -clip.getX(), (float) -clip.getY())); + } + + s->cloneClipIfMultiplyReferenced(); + s->clip = s->clip->translated (-clip.getPosition()); + return s; + } + + void endTransparencyLayer (SavedState& layerState) + { + const Rectangle clip (getUntransformedClipBounds()); + + const ScopedPointer g (image.createLowLevelContext()); + g->setOpacity (layerState.compositionAlpha); + g->drawImage (layerState.image, AffineTransform::translation ((float) clip.getX(), + (float) clip.getY()), false); + } + //============================================================================== - void fillRect (Image& image, const Rectangle& r, const bool replaceContents) + void fillRect (const Rectangle& r, const bool replaceContents) { if (clip != 0) { @@ -1968,19 +2023,19 @@ public: const Rectangle clipped (totalClip.getIntersection (r.translated (xOffset, yOffset))); if (! clipped.isEmpty()) - fillShape (image, new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false); + fillShape (new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false); } } else { Path p; p.addRectangle (r); - fillPath (image, p, AffineTransform::identity); + fillPath (p, AffineTransform::identity); } } } - void fillRect (Image& image, const Rectangle& r) + void fillRect (const Rectangle& r) { if (clip != 0) { @@ -1997,25 +2052,25 @@ public: const Rectangle clipped (totalClip.getIntersection (r.translated ((float) xOffset, (float) yOffset))); if (! clipped.isEmpty()) - fillShape (image, new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false); + fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false); } } else { Path p; p.addRectangle (r); - fillPath (image, p, AffineTransform::identity); + fillPath (p, AffineTransform::identity); } } } - void fillPath (Image& image, const Path& path, const AffineTransform& transform) + void fillPath (const Path& path, const AffineTransform& transform) { if (clip != 0) - fillShape (image, new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, getTransformWith (transform)), false); + fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, getTransformWith (transform)), false); } - void fillEdgeTable (Image& image, const EdgeTable& edgeTable, const float x, const int y) + void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y) { jassert (isOnlyTranslated); @@ -2024,11 +2079,11 @@ public: SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (edgeTable); SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill (edgeTableClip); edgeTableClip->edgeTable.translate (x + xOffset, y + yOffset); - fillShape (image, shapeToFill, false); + fillShape (shapeToFill, false); } } - void fillShape (Image& image, SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents) + void fillShape (SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents) { jassert (clip != 0); @@ -2060,7 +2115,7 @@ public: } else if (fillType.isTiledImage()) { - renderImage (image, fillType.image, fillType.transform, shapeToFill); + renderImage (fillType.image, fillType.transform, shapeToFill); } else { @@ -2070,11 +2125,11 @@ public: } //============================================================================== - void renderImage (Image& destImage, const Image& sourceImage, const AffineTransform& t, const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion) + void renderImage (const Image& sourceImage, const AffineTransform& t, const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion) { const AffineTransform transform (getTransformWith (t)); - const Image::BitmapData destData (destImage, true); + const Image::BitmapData destData (image, true); const Image::BitmapData srcData (sourceImage, false); const int alpha = fillType.colour.getAlpha(); const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); @@ -2096,7 +2151,7 @@ public: } else { - SoftwareRendererClasses::ClipRegionBase::Ptr c (new SoftwareRendererClasses::ClipRegion_EdgeTable (Rectangle (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()).getIntersection (destImage.getBounds()))); + SoftwareRendererClasses::ClipRegionBase::Ptr c (new SoftwareRendererClasses::ClipRegion_EdgeTable (Rectangle (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()).getIntersection (image.getBounds()))); c = clip->applyClipTo (c); if (c != 0) @@ -2128,11 +2183,13 @@ public: } //============================================================================== + Image image; SoftwareRendererClasses::ClipRegionBase::Ptr clip; private: AffineTransform complexTransform; int xOffset, yOffset; + float compositionAlpha; public: bool isOnlyTranslated; @@ -2170,16 +2227,16 @@ private: //============================================================================== LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image_) - : image (image_) + : image (image_), + currentState (new SavedState (image_, image_.getBounds(), 0, 0)) { - currentState = new SavedState (image_.getBounds(), 0, 0); } LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image_, const int xOffset, const int yOffset, const RectangleList& initialClip) - : image (image_) + : image (image_), + currentState (new SavedState (image_, initialClip, xOffset, yOffset)) { - currentState = new SavedState (initialClip, xOffset, yOffset); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -2263,6 +2320,19 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() } } +void LowLevelGraphicsSoftwareRenderer::beginTransparencyLayer (float opacity) +{ + saveState(); + currentState = currentState->beginTransparencyLayer (opacity); +} + +void LowLevelGraphicsSoftwareRenderer::endTransparencyLayer() +{ + const ScopedPointer layer (currentState); + restoreState(); + currentState->endTransparencyLayer (*layer); +} + //============================================================================== void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType) { @@ -2282,18 +2352,17 @@ void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::Resamp //============================================================================== void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents) { - currentState->fillRect (image, r, replaceExistingContents); + currentState->fillRect (r, replaceExistingContents); } void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform) { - currentState->fillPath (image, path, transform); + currentState->fillPath (path, transform); } void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform, const bool fillEntireClipAsTiles) { - currentState->renderImage (image, sourceImage, transform, - fillEntireClipAsTiles ? currentState->clip : 0); + currentState->renderImage (sourceImage, transform, fillEntireClipAsTiles ? currentState->clip : 0); } void LowLevelGraphicsSoftwareRenderer::drawLine (const Line & line) @@ -2306,13 +2375,13 @@ void LowLevelGraphicsSoftwareRenderer::drawLine (const Line & line) void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, float top, float bottom) { if (bottom > top) - currentState->fillRect (image, Rectangle ((float) x, top, 1.0f, bottom - top)); + currentState->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, float left, float right) { if (right > left) - currentState->fillRect (image, Rectangle (left, (float) y, right - left, 1.0f)); + currentState->fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } //============================================================================== @@ -2322,10 +2391,10 @@ public: CachedGlyph() : glyph (0), lastAccessCount (0) {} ~CachedGlyph() {} - void draw (SavedState& state, Image& image, const float x, const float y) const + void draw (SavedState& state, const float x, const float y) const { if (edgeTable != 0) - state.fillEdgeTable (image, *edgeTable, x, roundToInt (y)); + state.fillEdgeTable (*edgeTable, x, roundToInt (y)); } void generate (const Font& newFont, const int glyphNumber) @@ -2383,7 +2452,7 @@ public: juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); //============================================================================== - void drawGlyph (SavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) + void drawGlyph (SavedState& state, const Font& font, const int glyphNumber, float x, float y) { ++accessCounter; int oldestCounter = std::numeric_limits::max(); @@ -2397,7 +2466,7 @@ public: { ++hits; glyph->lastAccessCount = accessCounter; - glyph->draw (state, image, x, y); + glyph->draw (state, x, y); return; } @@ -2423,7 +2492,7 @@ public: jassert (oldest != 0); oldest->lastAccessCount = accessCounter; oldest->generate (font, glyphNumber); - oldest->draw (state, image, x, y); + oldest->draw (state, x, y); } //============================================================================== @@ -2457,7 +2526,7 @@ void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineT if (transform.isOnlyTranslation() && currentState->isOnlyTranslated) { - GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber, + GlyphCache::getInstance()->drawGlyph (*currentState, f, glyphNumber, transform.getTranslationX(), transform.getTranslationY()); } diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h index 60a8cd33a1..454557bf22 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h @@ -64,6 +64,9 @@ public: void saveState(); void restoreState(); + void beginTransparencyLayer (float opacity); + void endTransparencyLayer(); + //============================================================================== void setFill (const FillType& fillType); void setOpacity (float opacity); diff --git a/src/gui/graphics/drawables/juce_Drawable.cpp b/src/gui/graphics/drawables/juce_Drawable.cpp index 107cf0a624..4f0443974d 100644 --- a/src/gui/graphics/drawables/juce_Drawable.cpp +++ b/src/gui/graphics/drawables/juce_Drawable.cpp @@ -61,8 +61,8 @@ void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& g.saveState(); const float oldOpacity = getAlpha(); setAlpha (opacity); - g.addTransform (AffineTransform::translation (-originRelativeToComponent.getX(), - -originRelativeToComponent.getY()) + g.addTransform (AffineTransform::translation ((float) -originRelativeToComponent.getX(), + (float) -originRelativeToComponent.getY()) .followedBy (getTransform()) .followedBy (transform)); diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.cpp b/src/gui/graphics/drawables/juce_DrawableComposite.cpp index 642656f0fd..cb0f2f20b0 100644 --- a/src/gui/graphics/drawables/juce_DrawableComposite.cpp +++ b/src/gui/graphics/drawables/juce_DrawableComposite.cpp @@ -216,19 +216,22 @@ void DrawableComposite::childrenChanged() updateBoundsToFitChildren(); } +struct RentrancyCheckSetter +{ + RentrancyCheckSetter (bool& b_) : b (b_) { b_ = true; } + ~RentrancyCheckSetter() { b = false; } + +private: + bool& b; + + RentrancyCheckSetter (const RentrancyCheckSetter&); + RentrancyCheckSetter& operator= (const RentrancyCheckSetter&); +}; + void DrawableComposite::updateBoundsToFitChildren() { if (! updateBoundsReentrant) { - struct RentrancyCheckSetter - { - RentrancyCheckSetter (bool& b_) : b (b_) { b_ = true; } - ~RentrancyCheckSetter() { b = false; } - - private: - bool& b; - }; - const RentrancyCheckSetter checkSetter (updateBoundsReentrant); Rectangle childArea; diff --git a/src/gui/graphics/drawables/juce_DrawableShape.cpp b/src/gui/graphics/drawables/juce_DrawableShape.cpp index a6545271d4..b67ee6050f 100644 --- a/src/gui/graphics/drawables/juce_DrawableShape.cpp +++ b/src/gui/graphics/drawables/juce_DrawableShape.cpp @@ -151,8 +151,8 @@ const Rectangle DrawableShape::getDrawableBounds() const bool DrawableShape::hitTest (int x, int y) const { - const float globalX = x - originRelativeToComponent.getX(); - const float globalY = y - originRelativeToComponent.getY(); + const float globalX = (float) (x - originRelativeToComponent.getX()); + const float globalY = (float) (y - originRelativeToComponent.getY()); return path.contains (globalX, globalY) || (isStrokeVisible() && strokePath.contains (globalX, globalY)); diff --git a/src/native/mac/juce_mac_CoreGraphicsContext.mm b/src/native/mac/juce_mac_CoreGraphicsContext.mm index de04b3d6c1..dd5255d110 100644 --- a/src/native/mac/juce_mac_CoreGraphicsContext.mm +++ b/src/native/mac/juce_mac_CoreGraphicsContext.mm @@ -325,6 +325,19 @@ public: } } + void beginTransparencyLayer (float opacity) + { + saveState(); + CGContextSetAlpha (context, opacity); + CGContextBeginTransparencyLayer (context, 0); + } + + void endTransparencyLayer() + { + CGContextEndTransparencyLayer (context); + restoreState(); + } + //============================================================================== void setFill (const FillType& fillType) { diff --git a/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp b/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp index f97f7bc587..f9b561a231 100644 --- a/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp +++ b/src/native/windows/juce_win32_Direct2DGraphicsContext.cpp @@ -182,6 +182,16 @@ public: currentState = states.getLast(); } + void beginTransparencyLayer (float opacity) + { + jassertfalse; //xxx todo + } + + void endTransparencyLayer() + { + jassertfalse; //xxx todo + } + void setFill (const FillType& fillType) { currentState->setFill (fillType);