Browse Source

Changes to DrawableText. Minor new methods for Rectangle, Line and AffineTransform. Optimisation for CoreGraphics clip bounds.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
d2492f5f3b
15 changed files with 1024 additions and 122 deletions
  1. +228
    -2
      extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp
  2. +1
    -1
      extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp
  3. +294
    -45
      juce_amalgamated.cpp
  4. +120
    -18
      juce_amalgamated.h
  5. +8
    -1
      src/audio/audio_sources/juce_AudioSourcePlayer.h
  6. +4
    -17
      src/gui/graphics/drawables/juce_DrawableImage.cpp
  7. +1
    -0
      src/gui/graphics/drawables/juce_DrawablePath.cpp
  8. +216
    -14
      src/gui/graphics/drawables/juce_DrawableText.cpp
  9. +62
    -17
      src/gui/graphics/drawables/juce_DrawableText.h
  10. +9
    -0
      src/gui/graphics/geometry/juce_AffineTransform.cpp
  11. +6
    -0
      src/gui/graphics/geometry/juce_AffineTransform.h
  12. +12
    -0
      src/gui/graphics/geometry/juce_Line.h
  13. +8
    -0
      src/gui/graphics/geometry/juce_PathStrokeType.h
  14. +22
    -0
      src/gui/graphics/geometry/juce_Rectangle.h
  15. +33
    -7
      src/native/mac/juce_mac_CoreGraphicsContext.mm

+ 228
- 2
extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp View File

@@ -185,6 +185,10 @@ public:
DrawablePath::ValueTreeWrapper wrapper (item.getState()); DrawablePath::ValueTreeWrapper wrapper (item.getState());
props.add (new DrawablePathFillPropComp (item, "Fill", wrapper.getMainFillState())); props.add (new DrawablePathFillPropComp (item, "Fill", wrapper.getMainFillState()));
props.add (StrokeThicknessValueSource::create (wrapper, item.getDocument().getUndoManager()));
props.add (StrokeJoinStyleValueSource::create (wrapper, item.getDocument().getUndoManager()));
props.add (StrokeCapStyleValueSource::create (wrapper, item.getDocument().getUndoManager()));
props.add (new DrawablePathFillPropComp (item, "Stroke", wrapper.getStrokeFillState())); props.add (new DrawablePathFillPropComp (item, "Stroke", wrapper.getStrokeFillState()));
} }
@@ -327,7 +331,7 @@ public:
if (stroke.isGradient()) if (stroke.isGradient())
{ {
points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), true, true)); points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), true, true));
points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), false, true));
points.add (new GradientControlPoint (itemId + "/gs2", item.getState(), false, true));
} }
} }
@@ -412,6 +416,113 @@ public:
getGradientControlPoints (wrapper, item, points, itemId); getGradientControlPoints (wrapper, item, points, itemId);
} }
//==============================================================================
class StrokeValueSourceBase : public Value::ValueSource,
public ValueTree::Listener
{
public:
StrokeValueSourceBase (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_)
: wrapper (wrapper_), undoManager (undoManager_)
{
wrapper.getState().addListener (this);
}
~StrokeValueSourceBase() {}
void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { sendChangeMessage (true); }
void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) {}
void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {}
protected:
DrawablePath::ValueTreeWrapper wrapper;
UndoManager* undoManager;
};
//==============================================================================
class StrokeThicknessValueSource : public StrokeValueSourceBase
{
public:
StrokeThicknessValueSource (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_)
: StrokeValueSourceBase (wrapper_, undoManager_)
{}
const var getValue() const
{
return wrapper.getStrokeType().getStrokeThickness();
}
void setValue (const var& newValue)
{
PathStrokeType s (wrapper.getStrokeType());
s.setStrokeThickness (newValue);
wrapper.setStrokeType (s, undoManager);
}
static PropertyComponent* create (const DrawablePath::ValueTreeWrapper& wrapper, UndoManager* undoManager)
{
return new SliderPropertyComponent (Value (new StrokeThicknessValueSource (wrapper, undoManager)),
"Stroke Thickness", 0, 50.0, 0.1);
}
};
//==============================================================================
class StrokeJoinStyleValueSource : public StrokeValueSourceBase
{
public:
StrokeJoinStyleValueSource (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_)
: StrokeValueSourceBase (wrapper_, undoManager_)
{}
const var getValue() const
{
return (int) wrapper.getStrokeType().getJointStyle();
}
void setValue (const var& newValue)
{
PathStrokeType s (wrapper.getStrokeType());
s.setJointStyle ((PathStrokeType::JointStyle) (int) newValue);
wrapper.setStrokeType (s, undoManager);
}
static PropertyComponent* create (const DrawablePath::ValueTreeWrapper& wrapper, UndoManager* undoManager)
{
const char* types[] = { "Miter", "Curved", "Bevel", 0 };
const int mappings[] = { PathStrokeType::mitered, PathStrokeType::curved, PathStrokeType::beveled };
return new ChoicePropertyComponent (Value (new StrokeJoinStyleValueSource (wrapper, undoManager)),
"Joint Style", StringArray (types), Array<var> (mappings, numElementsInArray (mappings)));
}
};
//==============================================================================
class StrokeCapStyleValueSource : public StrokeValueSourceBase
{
public:
StrokeCapStyleValueSource (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_)
: StrokeValueSourceBase (wrapper_, undoManager_)
{}
const var getValue() const
{
return (int) wrapper.getStrokeType().getEndStyle();
}
void setValue (const var& newValue)
{
PathStrokeType s (wrapper.getStrokeType());
s.setEndStyle ((PathStrokeType::EndCapStyle) (int) newValue);
wrapper.setStrokeType (s, undoManager);
}
static PropertyComponent* create (const DrawablePath::ValueTreeWrapper& wrapper, UndoManager* undoManager)
{
const char* types[] = { "Butt", "Square", "Round", 0 };
const int mappings[] = { PathStrokeType::butt, PathStrokeType::square, PathStrokeType::rounded };
return new ChoicePropertyComponent (Value (new StrokeCapStyleValueSource (wrapper, undoManager)),
"Cap Style", StringArray (types), Array<var> (mappings, numElementsInArray (mappings)));
}
};
}; };
//============================================================================== //==============================================================================
@@ -713,6 +824,119 @@ public:
}; };
}; };
//==============================================================================
class DrawableTextHandler : public DrawableTypeHandler
{
public:
DrawableTextHandler() : DrawableTypeHandler ("Text", DrawableText::valueTreeType) {}
~DrawableTextHandler() {}
static const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition)
{
DrawableText dt;
dt.setText ("Text");
dt.setBounds (RelativePoint (approxPosition),
RelativePoint (approxPosition + Point<float> (100.0f, 0.0f)),
RelativePoint (approxPosition + Point<float> (0.0f, 100.0f)),
RelativePoint (approxPosition + Point<float> (25.0f, 25.0f)));
dt.setFont (Font (25.0f), true);
return dt.createValueTree (&document);
}
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props)
{
DrawableText::ValueTreeWrapper wrapper (item.getState());
//props.add (new ResetButtonPropertyComponent (item, wrapper));
}
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item)
{
}
//==============================================================================
class TextControlPoint : public ControlPoint
{
public:
TextControlPoint (const String& id_, const ValueTree& item_, const int cpNum_)
: ControlPoint (id_), item (item_), cpNum (cpNum_)
{}
~TextControlPoint() {}
const RelativePoint getPosition()
{
DrawableText::ValueTreeWrapper wrapper (item);
switch (cpNum)
{
case 0: return wrapper.getBoundingBoxTopLeft();
case 1: return wrapper.getBoundingBoxTopRight();
case 2: return wrapper.getBoundingBoxBottomLeft();
case 3: return wrapper.getFontSizeAndScaleAnchor();
default: jassertfalse; break;
}
return RelativePoint();
}
void setPosition (const RelativePoint& newPoint, UndoManager* undoManager)
{
DrawableText::ValueTreeWrapper wrapper (item);
switch (cpNum)
{
case 0: wrapper.setBoundingBoxTopLeft (newPoint, undoManager); break;
case 1: wrapper.setBoundingBoxTopRight (newPoint, undoManager); break;
case 2: wrapper.setBoundingBoxBottomLeft (newPoint, undoManager); break;
case 3: wrapper.setFontSizeAndScaleAnchor (newPoint, undoManager); break;
default: jassertfalse; break;
}
}
const Value getPositionValue (UndoManager* undoManager)
{
DrawableText::ValueTreeWrapper wrapper (item);
switch (cpNum)
{
case 0: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::topLeft, undoManager);
case 1: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::topRight, undoManager);
case 2: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::bottomLeft, undoManager);
case 3: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::fontSizeAnchor, undoManager);
default: jassertfalse; break;
}
return Value();
}
bool hasLine() { return false; }
RelativePoint getEndOfLine() { return RelativePoint(); }
void createProperties (DrawableDocument& document, Array <PropertyComponent*>& props)
{
DrawableTypeInstance instance (document, item);
props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager()));
props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager()));
}
private:
ValueTree item;
int cpNum;
};
void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points)
{
const String itemIDRoot (item.getID() + "/");
for (int i = 0; i < 4; ++i)
points.add (new TextControlPoint (itemIDRoot + String(i), item.getState(), i));
}
void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems&)
{
return getAllControlPoints (item, points);
}
};
//============================================================================== //==============================================================================
DrawableTypeManager::DrawableTypeManager() DrawableTypeManager::DrawableTypeManager()
@@ -720,6 +944,7 @@ DrawableTypeManager::DrawableTypeManager()
handlers.add (new DrawablePathHandler()); handlers.add (new DrawablePathHandler());
handlers.add (new DrawableImageHandler()); handlers.add (new DrawableImageHandler());
handlers.add (new DrawableCompositeHandler()); handlers.add (new DrawableCompositeHandler());
handlers.add (new DrawableTextHandler());
} }
DrawableTypeManager::~DrawableTypeManager() DrawableTypeManager::~DrawableTypeManager()
@@ -738,7 +963,7 @@ DrawableTypeHandler* DrawableTypeManager::getHandlerFor (const Identifier& type)
const StringArray DrawableTypeManager::getNewItemList() const StringArray DrawableTypeManager::getNewItemList()
{ {
const char* types[] = { "New Triangle", "New Rectangle", "New Ellipse", "New Image", 0 };
const char* types[] = { "New Triangle", "New Rectangle", "New Ellipse", "New Image", "New Text Object", 0 };
return StringArray (types); return StringArray (types);
} }
@@ -750,6 +975,7 @@ const ValueTree DrawableTypeManager::createNewItem (const int index, DrawableDoc
case 1: return DrawablePathHandler::createNewRectangle (document, approxPosition); case 1: return DrawablePathHandler::createNewRectangle (document, approxPosition);
case 2: return DrawablePathHandler::createNewEllipse (document, approxPosition); case 2: return DrawablePathHandler::createNewEllipse (document, approxPosition);
case 3: return DrawableImageHandler::createNewInstance (document, approxPosition); case 3: return DrawableImageHandler::createNewInstance (document, approxPosition);
case 4: return DrawableTextHandler::createNewInstance (document, approxPosition);
default: jassertfalse; break; default: jassertfalse; break;
} }


+ 1
- 1
extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp View File

@@ -83,7 +83,7 @@ MainWindow::MainWindow()
// don't want the window to take focus when the title-bar is clicked.. // don't want the window to take focus when the title-bar is clicked..
setWantsKeyboardFocus (false); setWantsKeyboardFocus (false);
getPeer()->setCurrentRenderingEngine (0);
//getPeer()->setCurrentRenderingEngine (0);
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()


+ 294
- 45
juce_amalgamated.cpp View File

@@ -84706,25 +84706,12 @@ const Rectangle<float> DrawableImage::getBounds() const
if (image.isNull()) if (image.isNull())
return Rectangle<float>(); return Rectangle<float>();


Point<float> resolved[3];
for (int i = 0; i < 3; ++i)
resolved[i] = controlPoints[i].resolve (parent);

const Point<float> bottomRight (resolved[1] + (resolved[2] - resolved[0]));
float minX = bottomRight.getX();
float maxX = minX;
float minY = bottomRight.getY();
float maxY = minY;

Point<float> corners[4];
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{
minX = jmin (minX, resolved[i].getX());
maxX = jmax (maxX, resolved[i].getX());
minY = jmin (minY, resolved[i].getY());
maxY = jmax (maxY, resolved[i].getY());
}
corners[i] = controlPoints[i].resolve (parent);


return Rectangle<float> (minX, minY, maxX - minX, maxY - minY);
corners[3] = corners[1] + (corners[2] - corners[0]);
return Rectangle<float>::findAreaContainingPoints (corners, 4);
} }


bool DrawableImage::hitTest (float x, float y) const bool DrawableImage::hitTest (float x, float y) const
@@ -85264,6 +85251,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree
{ {
damageRect = getBounds(); damageRect = getBounds();
path.swapWithPath (newPath); path.swapWithPath (newPath);
strokeNeedsUpdating = true;
strokeType = newStroke; strokeType = newStroke;
needsRedraw = true; needsRedraw = true;
} }
@@ -85307,50 +85295,136 @@ END_JUCE_NAMESPACE
BEGIN_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE


DrawableText::DrawableText() DrawableText::DrawableText()
: colour (Colours::white)
: colour (Colours::black),
justification (Justification::centredLeft)
{ {
setFont (Font (15.0f), true);
} }


DrawableText::DrawableText (const DrawableText& other) DrawableText::DrawableText (const DrawableText& other)
: text (other.text), : text (other.text),
colour (other.colour)
font (other.font),
colour (other.colour),
justification (other.justification)
{ {
for (int i = 0; i < numElementsInArray (controlPoints); ++i)
controlPoints[i] = other.controlPoints[i];
} }


DrawableText::~DrawableText() DrawableText::~DrawableText()
{ {
} }


void DrawableText::setText (const GlyphArrangement& newText)
void DrawableText::setText (const String& newText)
{ {
text = newText; text = newText;
} }


void DrawableText::setText (const String& newText, const Font& fontToUse)
void DrawableText::setColour (const Colour& newColour)
{ {
text.clear();
text.addLineOfText (fontToUse, newText, 0.0f, 0.0f);
colour = newColour;
} }


void DrawableText::setColour (const Colour& newColour)
void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
{ {
colour = newColour;
font = newFont;

if (applySizeAndScale)
{
const Line<float> left (Point<float>(), controlPoints[2].resolve (getParent()));
const Line<float> top (Point<float>(), controlPoints[1].resolve (getParent()));

controlPoints[3] = RelativePoint (controlPoints[0].resolve (getParent())
+ left.getPointAlongLine (font.getHeight())
+ top.getPointAlongLine (font.getHorizontalScale() * font.getHeight()));
}
}

void DrawableText::setJustification (const Justification& newJustification)
{
justification = newJustification;
}

void DrawableText::setBounds (const RelativePoint& boundingBoxTopLeft,
const RelativePoint& boundingBoxTopRight,
const RelativePoint& boundingBoxBottomLeft,
const RelativePoint& fontSizeAndScaleAnchor)
{
controlPoints[0] = boundingBoxTopLeft;
controlPoints[1] = boundingBoxTopRight;
controlPoints[2] = boundingBoxBottomLeft;
controlPoints[3] = fontSizeAndScaleAnchor;
}

static const Point<float> findNormalisedCoordWithinParallelogram (const Point<float>& origin,
Point<float> topRight,
Point<float> bottomLeft,
Point<float> target)
{
topRight -= origin;
bottomLeft -= origin;
target -= origin;

return Point<float> (Line<float> (Point<float>(), topRight).getIntersection (Line<float> (target, target - bottomLeft)).getDistanceFromOrigin(),
Line<float> (Point<float>(), bottomLeft).getIntersection (Line<float> (target, target - topRight)).getDistanceFromOrigin());
} }


void DrawableText::render (const Drawable::RenderingContext& context) const void DrawableText::render (const Drawable::RenderingContext& context) const
{ {
Point<float> points[4];
for (int i = 0; i < 4; ++i)
points[i] = controlPoints[i].resolve (getParent());

const float w = Line<float> (points[0], points[1]).getLength();
const float h = Line<float> (points[0], points[2]).getLength();

const Point<float> fontCoords (findNormalisedCoordWithinParallelogram (points[0], points[1], points[2], points[3]));
const float fontHeight = jlimit (1.0f, h, fontCoords.getY());
const float fontWidth = jlimit (0.01f, w, fontCoords.getX());

Font f (font);
f.setHeight (fontHeight);
f.setHorizontalScale (fontWidth / fontHeight);

context.g.setColour (colour.withMultipliedAlpha (context.opacity)); context.g.setColour (colour.withMultipliedAlpha (context.opacity));
text.draw (context.g, context.transform);

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));
}

void DrawableText::resolveCorners (Point<float>* const corners) const
{
for (int i = 0; i < 3; ++i)
corners[i] = controlPoints[i].resolve (parent);

corners[3] = corners[1] + (corners[2] - corners[0]);
} }


const Rectangle<float> DrawableText::getBounds() const const Rectangle<float> DrawableText::getBounds() const
{ {
return text.getBoundingBox (0, -1, false);
Point<float> corners[4];
resolveCorners (corners);
return Rectangle<float>::findAreaContainingPoints (corners, 4);
} }


bool DrawableText::hitTest (float x, float y) const bool DrawableText::hitTest (float x, float y) const
{ {
return text.findGlyphIndexAt (x, y) >= 0;
Point<float> corners[4];
resolveCorners (corners);

Path p;
p.startNewSubPath (corners[0].getX(), corners[0].getY());
p.lineTo (corners[1].getX(), corners[1].getY());
p.lineTo (corners[3].getX(), corners[3].getY());
p.lineTo (corners[2].getX(), corners[2].getY());
p.closeSubPath();

return p.contains (x, y);
} }


Drawable* DrawableText::createCopy() const Drawable* DrawableText::createCopy() const
@@ -85365,6 +85439,13 @@ void DrawableText::invalidatePoints()
const Identifier DrawableText::valueTreeType ("Text"); const Identifier DrawableText::valueTreeType ("Text");


const Identifier DrawableText::ValueTreeWrapper::text ("text"); const Identifier DrawableText::ValueTreeWrapper::text ("text");
const Identifier DrawableText::ValueTreeWrapper::colour ("colour");
const Identifier DrawableText::ValueTreeWrapper::font ("font");
const Identifier DrawableText::ValueTreeWrapper::justification ("justification");
const Identifier DrawableText::ValueTreeWrapper::topLeft ("topLeft");
const Identifier DrawableText::ValueTreeWrapper::topRight ("topRight");
const Identifier DrawableText::ValueTreeWrapper::bottomLeft ("bottomLeft");
const Identifier DrawableText::ValueTreeWrapper::fontSizeAnchor ("fontSizeAnchor");


DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
: ValueTreeWrapperBase (state_) : ValueTreeWrapperBase (state_)
@@ -85372,12 +85453,113 @@ DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
jassert (state.hasType (valueTreeType)); jassert (state.hasType (valueTreeType));
} }


const String DrawableText::ValueTreeWrapper::getText() const
{
return state [text].toString();
}

void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager)
{
state.setProperty (text, newText, undoManager);
}

const Colour DrawableText::ValueTreeWrapper::getColour() const
{
return Colour::fromString (state [colour].toString());
}

void DrawableText::ValueTreeWrapper::setColour (const Colour& newColour, UndoManager* undoManager)
{
state.setProperty (colour, newColour.toString(), undoManager);
}

const Justification DrawableText::ValueTreeWrapper::getJustification() const
{
return Justification ((int) state [justification]);
}

void DrawableText::ValueTreeWrapper::setJustification (const Justification& newJustification, UndoManager* undoManager)
{
state.setProperty (justification, newJustification.getFlags(), undoManager);
}

const Font DrawableText::ValueTreeWrapper::getFont() const
{
return Font::fromString (state [font]);
}

void DrawableText::ValueTreeWrapper::setFont (const Font& newFont, UndoManager* undoManager)
{
state.setProperty (font, newFont.toString(), undoManager);
}

const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopLeft() const
{
return state [topLeft].toString();
}

void DrawableText::ValueTreeWrapper::setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (topLeft, p.toString(), undoManager);
}

const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopRight() const
{
return state [topRight].toString();
}

void DrawableText::ValueTreeWrapper::setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (topRight, p.toString(), undoManager);
}

const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxBottomLeft() const
{
return state [bottomLeft].toString();
}

void DrawableText::ValueTreeWrapper::setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (bottomLeft, p.toString(), undoManager);
}

const RelativePoint DrawableText::ValueTreeWrapper::getFontSizeAndScaleAnchor() const
{
return state [fontSizeAnchor].toString();
}

void DrawableText::ValueTreeWrapper::setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (fontSizeAnchor, p.toString(), undoManager);
}

const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*)
{ {
ValueTreeWrapper v (tree); ValueTreeWrapper v (tree);
setName (v.getID()); setName (v.getID());


jassertfalse; // xxx not finished!
const RelativePoint p1 (v.getBoundingBoxTopLeft()), p2 (v.getBoundingBoxTopRight()),
p3 (v.getBoundingBoxBottomLeft()), p4 (v.getFontSizeAndScaleAnchor());

const Colour newColour (v.getColour());
const Justification newJustification (v.getJustification());
const String newText (v.getText());
const Font newFont (v.getFont());

if (text != newText || font != newFont || justification != newJustification || colour != newColour
|| p1 != controlPoints[0] || p2 != controlPoints[1] || p3 != controlPoints[2] || p4 != controlPoints[3])
{
const Rectangle<float> damage (getBounds());

setBounds (p1, p2, p3, p4);
setColour (newColour);
setFont (newFont, false);
setJustification (newJustification);
setText (newText);

return damage.getUnion (getBounds());

}


return Rectangle<float>(); return Rectangle<float>();
} }
@@ -85388,8 +85570,14 @@ const ValueTree DrawableText::createValueTree (ImageProvider*) const
ValueTreeWrapper v (tree); ValueTreeWrapper v (tree);


v.setID (getName(), 0); v.setID (getName(), 0);

jassertfalse; // xxx not finished!
v.setText (text, 0);
v.setFont (font, 0);
v.setJustification (justification, 0);
v.setColour (colour, 0);
v.setBoundingBoxTopLeft (controlPoints[0], 0);
v.setBoundingBoxTopRight (controlPoints[1], 0);
v.setBoundingBoxBottomLeft (controlPoints[2], 0);
v.setFontSizeAndScaleAnchor (controlPoints[3], 0);


return tree; return tree;
} }
@@ -88911,6 +89099,15 @@ const AffineTransform AffineTransform::fromTargetPoints (const float x00, const
y10 - y00, y01 - y00, y00); y10 - y00, y01 - y00, y00);
} }


const AffineTransform AffineTransform::fromTargetPoints (const float sx1, const float sy1, const float tx1, const float ty1,
const float sx2, const float sy2, const float tx2, const float ty2,
const float sx3, const float sy3, const float tx3, const float ty3) throw()
{
return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3)
.inverted()
.followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3));
}

bool AffineTransform::isOnlyTranslation() const throw() bool AffineTransform::isOnlyTranslation() const throw()
{ {
return (mat01 == 0) return (mat01 == 0)
@@ -263809,7 +264006,8 @@ public:
: context (context_), : context (context_),
flipHeight (flipHeight_), flipHeight (flipHeight_),
state (new SavedState()), state (new SavedState()),
numGradientLookupEntries (0)
numGradientLookupEntries (0),
lastClipRectIsValid (false)
{ {
CGContextRetain (context); CGContextRetain (context);
CGContextSaveGState(context); CGContextSaveGState(context);
@@ -263837,11 +264035,21 @@ public:
void setOrigin (int x, int y) void setOrigin (int x, int y)
{ {
CGContextTranslateCTM (context, x, -y); CGContextTranslateCTM (context, x, -y);

if (lastClipRectIsValid)
lastClipRect.translate (-x, -y);
} }


bool clipToRectangle (const Rectangle<int>& r) bool clipToRectangle (const Rectangle<int>& r)
{ {
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));

if (lastClipRectIsValid)
{
lastClipRect = lastClipRect.getIntersection (r);
return ! r.isEmpty();
}

return ! isClipEmpty(); return ! isClipEmpty();
} }


@@ -263850,6 +264058,8 @@ public:
if (clipRegion.isEmpty()) if (clipRegion.isEmpty())
{ {
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
lastClipRectIsValid = true;
lastClipRect = Rectangle<int>();
return false; return false;
} }
else else
@@ -263864,6 +264074,7 @@ public:
} }


CGContextClipToRects (context, rects, numRects); CGContextClipToRects (context, rects, numRects);
lastClipRectIsValid = false;
return ! isClipEmpty(); return ! isClipEmpty();
} }
} }
@@ -263873,12 +264084,14 @@ public:
RectangleList remaining (getClipBounds()); RectangleList remaining (getClipBounds());
remaining.subtract (r); remaining.subtract (r);
clipToRectangleList (remaining); clipToRectangleList (remaining);
lastClipRectIsValid = false;
} }


void clipToPath (const Path& path, const AffineTransform& transform) void clipToPath (const Path& path, const AffineTransform& transform)
{ {
createPath (path, transform); createPath (path, transform);
CGContextClip (context); CGContextClip (context);
lastClipRectIsValid = false;
} }


void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform)
@@ -263903,6 +264116,7 @@ public:
flip(); flip();


CGImageRelease (image); CGImageRelease (image);
lastClipRectIsValid = false;
} }
} }


@@ -263913,17 +264127,23 @@ public:


const Rectangle<int> getClipBounds() const const Rectangle<int> getClipBounds() const
{ {
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
if (! lastClipRectIsValid)
{
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));

lastClipRectIsValid = true;
lastClipRect.setBounds (roundToInt (bounds.origin.x),
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
roundToInt (bounds.size.width),
roundToInt (bounds.size.height));
}


return Rectangle<int> (roundToInt (bounds.origin.x),
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
roundToInt (bounds.size.width),
roundToInt (bounds.size.height));
return lastClipRect;
} }


bool isClipEmpty() const bool isClipEmpty() const
{ {
return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
return getClipBounds().isEmpty();
} }


void saveState() void saveState()
@@ -263942,6 +264162,7 @@ public:
{ {
state = top; state = top;
stateStack.removeLast (1, false); stateStack.removeLast (1, false);
lastClipRectIsValid = false;
} }
else else
{ {
@@ -264223,6 +264444,8 @@ private:
const CGFloat flipHeight; const CGFloat flipHeight;
CGColorSpaceRef rgbColourSpace, greyColourSpace; CGColorSpaceRef rgbColourSpace, greyColourSpace;
CGFunctionCallbacks gradientCallbacks; CGFunctionCallbacks gradientCallbacks;
mutable Rectangle<int> lastClipRect;
mutable bool lastClipRectIsValid;


struct SavedState struct SavedState
{ {
@@ -268422,7 +268645,8 @@ public:
: context (context_), : context (context_),
flipHeight (flipHeight_), flipHeight (flipHeight_),
state (new SavedState()), state (new SavedState()),
numGradientLookupEntries (0)
numGradientLookupEntries (0),
lastClipRectIsValid (false)
{ {
CGContextRetain (context); CGContextRetain (context);
CGContextSaveGState(context); CGContextSaveGState(context);
@@ -268450,11 +268674,21 @@ public:
void setOrigin (int x, int y) void setOrigin (int x, int y)
{ {
CGContextTranslateCTM (context, x, -y); CGContextTranslateCTM (context, x, -y);

if (lastClipRectIsValid)
lastClipRect.translate (-x, -y);
} }


bool clipToRectangle (const Rectangle<int>& r) bool clipToRectangle (const Rectangle<int>& r)
{ {
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));

if (lastClipRectIsValid)
{
lastClipRect = lastClipRect.getIntersection (r);
return ! r.isEmpty();
}

return ! isClipEmpty(); return ! isClipEmpty();
} }


@@ -268463,6 +268697,8 @@ public:
if (clipRegion.isEmpty()) if (clipRegion.isEmpty())
{ {
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
lastClipRectIsValid = true;
lastClipRect = Rectangle<int>();
return false; return false;
} }
else else
@@ -268477,6 +268713,7 @@ public:
} }


CGContextClipToRects (context, rects, numRects); CGContextClipToRects (context, rects, numRects);
lastClipRectIsValid = false;
return ! isClipEmpty(); return ! isClipEmpty();
} }
} }
@@ -268486,12 +268723,14 @@ public:
RectangleList remaining (getClipBounds()); RectangleList remaining (getClipBounds());
remaining.subtract (r); remaining.subtract (r);
clipToRectangleList (remaining); clipToRectangleList (remaining);
lastClipRectIsValid = false;
} }


void clipToPath (const Path& path, const AffineTransform& transform) void clipToPath (const Path& path, const AffineTransform& transform)
{ {
createPath (path, transform); createPath (path, transform);
CGContextClip (context); CGContextClip (context);
lastClipRectIsValid = false;
} }


void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform)
@@ -268516,6 +268755,7 @@ public:
flip(); flip();


CGImageRelease (image); CGImageRelease (image);
lastClipRectIsValid = false;
} }
} }


@@ -268526,17 +268766,23 @@ public:


const Rectangle<int> getClipBounds() const const Rectangle<int> getClipBounds() const
{ {
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
if (! lastClipRectIsValid)
{
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));

lastClipRectIsValid = true;
lastClipRect.setBounds (roundToInt (bounds.origin.x),
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
roundToInt (bounds.size.width),
roundToInt (bounds.size.height));
}


return Rectangle<int> (roundToInt (bounds.origin.x),
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
roundToInt (bounds.size.width),
roundToInt (bounds.size.height));
return lastClipRect;
} }


bool isClipEmpty() const bool isClipEmpty() const
{ {
return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
return getClipBounds().isEmpty();
} }


void saveState() void saveState()
@@ -268555,6 +268801,7 @@ public:
{ {
state = top; state = top;
stateStack.removeLast (1, false); stateStack.removeLast (1, false);
lastClipRectIsValid = false;
} }
else else
{ {
@@ -268836,6 +269083,8 @@ private:
const CGFloat flipHeight; const CGFloat flipHeight;
CGColorSpaceRef rgbColourSpace, greyColourSpace; CGColorSpaceRef rgbColourSpace, greyColourSpace;
CGFunctionCallbacks gradientCallbacks; CGFunctionCallbacks gradientCallbacks;
mutable Rectangle<int> lastClipRect;
mutable bool lastClipRectIsValid;


struct SavedState struct SavedState
{ {


+ 120
- 18
juce_amalgamated.h View File

@@ -18946,6 +18946,12 @@ public:
float x10, float y10, float x10, float y10,
float x01, float y01) throw(); float x01, float y01) throw();


/** Returns the transform that will map three specified points onto three target points.
*/
static const AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1,
float sourceX2, float sourceY2, float targetX2, float targetY2,
float sourceX3, float sourceY3, float targetX3, float targetY3) throw();

/** Returns the result of concatenating another transformation after this one. */ /** Returns the result of concatenating another transformation after this one. */
const AffineTransform followedBy (const AffineTransform& other) const throw(); const AffineTransform followedBy (const AffineTransform& other) const throw();


@@ -19981,6 +19987,18 @@ public:
return findIntersection (start, end, line.start, line.end, intersection); return findIntersection (start, end, line.start, line.end, intersection);
} }


/** Finds the intersection between two lines.

@param line the line to intersect with
@returns the point at which the lines intersect, even if this lies beyond the end of the lines
*/
const Point<ValueType> getIntersection (const Line& line) const throw()
{
Point<ValueType> p;
findIntersection (start, end, line.start, line.end, p);
return p;
}

/** Returns the location of the point which is a given distance along this line. /** Returns the location of the point which is a given distance along this line.


@param distanceFromStart the distance to move along the line from its @param distanceFromStart the distance to move along the line from its
@@ -20660,6 +20678,28 @@ public:
return Rectangle<int> (x1, y1, x2 - x1, y2 - y1); return Rectangle<int> (x1, y1, x2 - x1, y2 - y1);
} }


/** Returns the smallest Rectangle that can contain a set of points. */
static const Rectangle findAreaContainingPoints (const Point<ValueType>* const points, const int numPoints) throw()
{
if (numPoints == 0)
return Rectangle();

ValueType minX (points[0].getX());
ValueType maxX (minX);
ValueType minY (points[0].getY());
ValueType maxY (minY);

for (int i = 1; i < numPoints; ++i)
{
minX = jmin (minX, points[i].getX());
maxX = jmax (maxX, points[i].getX());
minY = jmin (minY, points[i].getY());
maxY = jmax (maxY, points[i].getY());
}

return Rectangle (minX, minY, maxX - minX, maxY - minY);
}

/** Casts this rectangle to a Rectangle<float>. /** Casts this rectangle to a Rectangle<float>.
Obviously this is mainly useful for rectangles that use integer types. Obviously this is mainly useful for rectangles that use integer types.
@see getSmallestIntegerContainer @see getSmallestIntegerContainer
@@ -22147,12 +22187,21 @@ public:
/** Returns the stroke thickness. */ /** Returns the stroke thickness. */
float getStrokeThickness() const throw() { return thickness; } float getStrokeThickness() const throw() { return thickness; }


/** Sets the stroke thickness. */
void setStrokeThickness (float newThickness) throw() { thickness = newThickness; }

/** Returns the joint style. */ /** Returns the joint style. */
JointStyle getJointStyle() const throw() { return jointStyle; } JointStyle getJointStyle() const throw() { return jointStyle; }


/** Sets the joint style. */
void setJointStyle (JointStyle newStyle) throw() { jointStyle = newStyle; }

/** Returns the end-cap style. */ /** Returns the end-cap style. */
EndCapStyle getEndStyle() const throw() { return endStyle; } EndCapStyle getEndStyle() const throw() { return endStyle; }


/** Sets the end-cap style. */
void setEndStyle (EndCapStyle newStyle) throw() { endStyle = newStyle; }

juce_UseDebuggingNewOperator juce_UseDebuggingNewOperator


/** Compares the stroke thickness, joint and end styles of two stroke types. */ /** Compares the stroke thickness, joint and end styles of two stroke types. */
@@ -31096,9 +31145,16 @@ public:
*/ */
AudioSource* getCurrentSource() const throw() { return source; } AudioSource* getCurrentSource() const throw() { return source; }


/** Sets a gain to apply to the audio data. */
/** Sets a gain to apply to the audio data.
@see getGain
*/
void setGain (const float newGain) throw(); void setGain (const float newGain) throw();


/** Returns the current gain.
@see setGain
*/
float getGain() const throw() { return gain; }

/** Implementation of the AudioIODeviceCallback method. */ /** Implementation of the AudioIODeviceCallback method. */
void audioDeviceIOCallback (const float** inputChannelData, void audioDeviceIOCallback (const float** inputChannelData,
int totalNumInputChannels, int totalNumInputChannels,
@@ -58788,19 +58844,8 @@ public:
/** Destructor. */ /** Destructor. */
virtual ~DrawableText(); virtual ~DrawableText();


/** Sets the block of text to render */
void setText (const GlyphArrangement& newText);

/** Sets a single line of text to render.

This is a convenient method of adding a single line - for
more complex text, use the setText() that takes a
GlyphArrangement instead.
*/
void setText (const String& newText, const Font& fontToUse);

/** Returns the text arrangement that was set with setText(). */
const GlyphArrangement& getText() const throw() { return text; }
/** Sets the text to display.*/
void setText (const String& newText);


/** Sets the colour of the text. */ /** Sets the colour of the text. */
void setColour (const Colour& newColour); void setColour (const Colour& newColour);
@@ -58808,6 +58853,37 @@ public:
/** Returns the current text colour. */ /** Returns the current text colour. */
const Colour& getColour() const throw() { return colour; } const Colour& getColour() const throw() { return colour; }


/** Sets the font to use.
Note that the font height and horizontal scale are actually based upon the position
of the fontSizeAndScaleAnchor parameter to setBounds(). If applySizeAndScale is true, then
the height and scale control point will be moved to match the dimensions of the font supplied;
if it is false, then the new font's height and scale are ignored.
*/
void setFont (const Font& newFont, bool applySizeAndScale);

/** Changes the justification of the text within the bounding box. */
void setJustification (const Justification& newJustification);

/** Sets the bounding box and the control point that controls the font size.
The three bounding box points define the parallelogram within which the text will be
placed. The fontSizeAndScaleAnchor specifies a position within that parallelogram, whose
Y position (relative to the parallelogram's origin and possibly distorted shape) specifies
the font's height, and its X defines the font's horizontal scale.
*/
void setBounds (const RelativePoint& boundingBoxTopLeft,
const RelativePoint& boundingBoxTopRight,
const RelativePoint& boundingBoxBottomLeft,
const RelativePoint& fontSizeAndScaleAnchor);

/** Returns the origin of the text bounding box. */
const RelativePoint& getBoundingBoxTopLeft() const throw() { return controlPoints[0]; }
/** Returns the top-right of the text bounding box. */
const RelativePoint& getBoundingBoxTopRight() const throw() { return controlPoints[1]; }
/** Returns the bottom-left of the text bounding box. */
const RelativePoint& getBoundingBoxBottomLeft() const throw() { return controlPoints[2]; }
/** Returns the point within the text bounding box which defines the size and scale of the font. */
const RelativePoint& getFontSizeAndScaleAnchor() const throw() { return controlPoints[3]; }

/** @internal */ /** @internal */
void render (const Drawable::RenderingContext& context) const; void render (const Drawable::RenderingContext& context) const;
/** @internal */ /** @internal */
@@ -58833,17 +58909,43 @@ public:
public: public:
ValueTreeWrapper (const ValueTree& state); ValueTreeWrapper (const ValueTree& state);


//xxx todo
const String getText() const;
void setText (const String& newText, UndoManager* undoManager);


private:
static const Identifier text;
const Colour getColour() const;
void setColour (const Colour& newColour, UndoManager* undoManager);

const Justification getJustification() const;
void setJustification (const Justification& newJustification, UndoManager* undoManager);

const Font getFont() const;
void setFont (const Font& newFont, UndoManager* undoManager);

const RelativePoint getBoundingBoxTopLeft() const;
void setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager);

const RelativePoint getBoundingBoxTopRight() const;
void setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager);

const RelativePoint getBoundingBoxBottomLeft() const;
void setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager);

const RelativePoint getFontSizeAndScaleAnchor() const;
void setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager);

static const Identifier text, colour, font, justification, topLeft, topRight, bottomLeft, fontSizeAnchor;
}; };


juce_UseDebuggingNewOperator juce_UseDebuggingNewOperator


private: private:
GlyphArrangement text;
RelativePoint controlPoints[4];
Font font;
String text;
Colour colour; Colour colour;
Justification justification;

void resolveCorners (Point<float>* corners) const;


DrawableText& operator= (const DrawableText&); DrawableText& operator= (const DrawableText&);
}; };


+ 8
- 1
src/audio/audio_sources/juce_AudioSourcePlayer.h View File

@@ -74,9 +74,16 @@ public:
*/ */
AudioSource* getCurrentSource() const throw() { return source; } AudioSource* getCurrentSource() const throw() { return source; }
/** Sets a gain to apply to the audio data. */
/** Sets a gain to apply to the audio data.
@see getGain
*/
void setGain (const float newGain) throw(); void setGain (const float newGain) throw();
/** Returns the current gain.
@see setGain
*/
float getGain() const throw() { return gain; }
//============================================================================== //==============================================================================
/** Implementation of the AudioIODeviceCallback method. */ /** Implementation of the AudioIODeviceCallback method. */
void audioDeviceIOCallback (const float** inputChannelData, void audioDeviceIOCallback (const float** inputChannelData,


+ 4
- 17
src/gui/graphics/drawables/juce_DrawableImage.cpp View File

@@ -130,25 +130,12 @@ const Rectangle<float> DrawableImage::getBounds() const
if (image.isNull()) if (image.isNull())
return Rectangle<float>(); return Rectangle<float>();
Point<float> resolved[3];
Point<float> corners[4];
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
resolved[i] = controlPoints[i].resolve (parent);
const Point<float> bottomRight (resolved[1] + (resolved[2] - resolved[0]));
float minX = bottomRight.getX();
float maxX = minX;
float minY = bottomRight.getY();
float maxY = minY;
for (int i = 0; i < 3; ++i)
{
minX = jmin (minX, resolved[i].getX());
maxX = jmax (maxX, resolved[i].getX());
minY = jmin (minY, resolved[i].getY());
maxY = jmax (maxY, resolved[i].getY());
}
corners[i] = controlPoints[i].resolve (parent);
return Rectangle<float> (minX, minY, maxX - minX, maxY - minY);
corners[3] = corners[1] + (corners[2] - corners[0]);
return Rectangle<float>::findAreaContainingPoints (corners, 4);
} }
bool DrawableImage::hitTest (float x, float y) const bool DrawableImage::hitTest (float x, float y) const


+ 1
- 0
src/gui/graphics/drawables/juce_DrawablePath.cpp View File

@@ -382,6 +382,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree
{ {
damageRect = getBounds(); damageRect = getBounds();
path.swapWithPath (newPath); path.swapWithPath (newPath);
strokeNeedsUpdating = true;
strokeType = newStroke; strokeType = newStroke;
needsRedraw = true; needsRedraw = true;
} }


+ 216
- 14
src/gui/graphics/drawables/juce_DrawableText.cpp View File

@@ -28,18 +28,25 @@
BEGIN_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE
#include "juce_DrawableText.h" #include "juce_DrawableText.h"
#include "juce_DrawableComposite.h"
//============================================================================== //==============================================================================
DrawableText::DrawableText() DrawableText::DrawableText()
: colour (Colours::white)
: colour (Colours::black),
justification (Justification::centredLeft)
{ {
setFont (Font (15.0f), true);
} }
DrawableText::DrawableText (const DrawableText& other) DrawableText::DrawableText (const DrawableText& other)
: text (other.text), : text (other.text),
colour (other.colour)
font (other.font),
colour (other.colour),
justification (other.justification)
{ {
for (int i = 0; i < numElementsInArray (controlPoints); ++i)
controlPoints[i] = other.controlPoints[i];
} }
DrawableText::~DrawableText() DrawableText::~DrawableText()
@@ -47,37 +54,117 @@ DrawableText::~DrawableText()
} }
//============================================================================== //==============================================================================
void DrawableText::setText (const GlyphArrangement& newText)
void DrawableText::setText (const String& newText)
{ {
text = newText; text = newText;
} }
void DrawableText::setText (const String& newText, const Font& fontToUse)
void DrawableText::setColour (const Colour& newColour)
{ {
text.clear();
text.addLineOfText (fontToUse, newText, 0.0f, 0.0f);
colour = newColour;
} }
void DrawableText::setColour (const Colour& newColour)
void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
{ {
colour = newColour;
font = newFont;
if (applySizeAndScale)
{
const Line<float> left (Point<float>(), controlPoints[2].resolve (getParent()));
const Line<float> top (Point<float>(), controlPoints[1].resolve (getParent()));
controlPoints[3] = RelativePoint (controlPoints[0].resolve (getParent())
+ left.getPointAlongLine (font.getHeight())
+ top.getPointAlongLine (font.getHorizontalScale() * font.getHeight()));
}
}
void DrawableText::setJustification (const Justification& newJustification)
{
justification = newJustification;
}
void DrawableText::setBounds (const RelativePoint& boundingBoxTopLeft,
const RelativePoint& boundingBoxTopRight,
const RelativePoint& boundingBoxBottomLeft,
const RelativePoint& fontSizeAndScaleAnchor)
{
controlPoints[0] = boundingBoxTopLeft;
controlPoints[1] = boundingBoxTopRight;
controlPoints[2] = boundingBoxBottomLeft;
controlPoints[3] = fontSizeAndScaleAnchor;
} }
//============================================================================== //==============================================================================
static const Point<float> findNormalisedCoordWithinParallelogram (const Point<float>& origin,
Point<float> topRight,
Point<float> bottomLeft,
Point<float> target)
{
topRight -= origin;
bottomLeft -= origin;
target -= origin;
return Point<float> (Line<float> (Point<float>(), topRight).getIntersection (Line<float> (target, target - bottomLeft)).getDistanceFromOrigin(),
Line<float> (Point<float>(), bottomLeft).getIntersection (Line<float> (target, target - topRight)).getDistanceFromOrigin());
}
void DrawableText::render (const Drawable::RenderingContext& context) const void DrawableText::render (const Drawable::RenderingContext& context) const
{ {
Point<float> points[4];
for (int i = 0; i < 4; ++i)
points[i] = controlPoints[i].resolve (getParent());
const float w = Line<float> (points[0], points[1]).getLength();
const float h = Line<float> (points[0], points[2]).getLength();
const Point<float> fontCoords (findNormalisedCoordWithinParallelogram (points[0], points[1], points[2], points[3]));
const float fontHeight = jlimit (1.0f, h, fontCoords.getY());
const float fontWidth = jlimit (0.01f, w, fontCoords.getX());
Font f (font);
f.setHeight (fontHeight);
f.setHorizontalScale (fontWidth / fontHeight);
context.g.setColour (colour.withMultipliedAlpha (context.opacity)); context.g.setColour (colour.withMultipliedAlpha (context.opacity));
text.draw (context.g, context.transform);
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));
}
void DrawableText::resolveCorners (Point<float>* const corners) const
{
for (int i = 0; i < 3; ++i)
corners[i] = controlPoints[i].resolve (parent);
corners[3] = corners[1] + (corners[2] - corners[0]);
} }
const Rectangle<float> DrawableText::getBounds() const const Rectangle<float> DrawableText::getBounds() const
{ {
return text.getBoundingBox (0, -1, false);
Point<float> corners[4];
resolveCorners (corners);
return Rectangle<float>::findAreaContainingPoints (corners, 4);
} }
bool DrawableText::hitTest (float x, float y) const bool DrawableText::hitTest (float x, float y) const
{ {
return text.findGlyphIndexAt (x, y) >= 0;
Point<float> corners[4];
resolveCorners (corners);
Path p;
p.startNewSubPath (corners[0].getX(), corners[0].getY());
p.lineTo (corners[1].getX(), corners[1].getY());
p.lineTo (corners[3].getX(), corners[3].getY());
p.lineTo (corners[2].getX(), corners[2].getY());
p.closeSubPath();
return p.contains (x, y);
} }
Drawable* DrawableText::createCopy() const Drawable* DrawableText::createCopy() const
@@ -93,19 +180,128 @@ void DrawableText::invalidatePoints()
const Identifier DrawableText::valueTreeType ("Text"); const Identifier DrawableText::valueTreeType ("Text");
const Identifier DrawableText::ValueTreeWrapper::text ("text"); const Identifier DrawableText::ValueTreeWrapper::text ("text");
const Identifier DrawableText::ValueTreeWrapper::colour ("colour");
const Identifier DrawableText::ValueTreeWrapper::font ("font");
const Identifier DrawableText::ValueTreeWrapper::justification ("justification");
const Identifier DrawableText::ValueTreeWrapper::topLeft ("topLeft");
const Identifier DrawableText::ValueTreeWrapper::topRight ("topRight");
const Identifier DrawableText::ValueTreeWrapper::bottomLeft ("bottomLeft");
const Identifier DrawableText::ValueTreeWrapper::fontSizeAnchor ("fontSizeAnchor");
//==============================================================================
DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
: ValueTreeWrapperBase (state_) : ValueTreeWrapperBase (state_)
{ {
jassert (state.hasType (valueTreeType)); jassert (state.hasType (valueTreeType));
} }
const String DrawableText::ValueTreeWrapper::getText() const
{
return state [text].toString();
}
void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager)
{
state.setProperty (text, newText, undoManager);
}
const Colour DrawableText::ValueTreeWrapper::getColour() const
{
return Colour::fromString (state [colour].toString());
}
void DrawableText::ValueTreeWrapper::setColour (const Colour& newColour, UndoManager* undoManager)
{
state.setProperty (colour, newColour.toString(), undoManager);
}
const Justification DrawableText::ValueTreeWrapper::getJustification() const
{
return Justification ((int) state [justification]);
}
void DrawableText::ValueTreeWrapper::setJustification (const Justification& newJustification, UndoManager* undoManager)
{
state.setProperty (justification, newJustification.getFlags(), undoManager);
}
const Font DrawableText::ValueTreeWrapper::getFont() const
{
return Font::fromString (state [font]);
}
void DrawableText::ValueTreeWrapper::setFont (const Font& newFont, UndoManager* undoManager)
{
state.setProperty (font, newFont.toString(), undoManager);
}
const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopLeft() const
{
return state [topLeft].toString();
}
void DrawableText::ValueTreeWrapper::setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (topLeft, p.toString(), undoManager);
}
const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopRight() const
{
return state [topRight].toString();
}
void DrawableText::ValueTreeWrapper::setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (topRight, p.toString(), undoManager);
}
const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxBottomLeft() const
{
return state [bottomLeft].toString();
}
void DrawableText::ValueTreeWrapper::setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (bottomLeft, p.toString(), undoManager);
}
const RelativePoint DrawableText::ValueTreeWrapper::getFontSizeAndScaleAnchor() const
{
return state [fontSizeAnchor].toString();
}
void DrawableText::ValueTreeWrapper::setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager)
{
state.setProperty (fontSizeAnchor, p.toString(), undoManager);
}
const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*)
{ {
ValueTreeWrapper v (tree); ValueTreeWrapper v (tree);
setName (v.getID()); setName (v.getID());
jassertfalse; // xxx not finished!
const RelativePoint p1 (v.getBoundingBoxTopLeft()), p2 (v.getBoundingBoxTopRight()),
p3 (v.getBoundingBoxBottomLeft()), p4 (v.getFontSizeAndScaleAnchor());
const Colour newColour (v.getColour());
const Justification newJustification (v.getJustification());
const String newText (v.getText());
const Font newFont (v.getFont());
if (text != newText || font != newFont || justification != newJustification || colour != newColour
|| p1 != controlPoints[0] || p2 != controlPoints[1] || p3 != controlPoints[2] || p4 != controlPoints[3])
{
const Rectangle<float> damage (getBounds());
setBounds (p1, p2, p3, p4);
setColour (newColour);
setFont (newFont, false);
setJustification (newJustification);
setText (newText);
return damage.getUnion (getBounds());
}
return Rectangle<float>(); return Rectangle<float>();
} }
@@ -116,8 +312,14 @@ const ValueTree DrawableText::createValueTree (ImageProvider*) const
ValueTreeWrapper v (tree); ValueTreeWrapper v (tree);
v.setID (getName(), 0); v.setID (getName(), 0);
jassertfalse; // xxx not finished!
v.setText (text, 0);
v.setFont (font, 0);
v.setJustification (justification, 0);
v.setColour (colour, 0);
v.setBoundingBoxTopLeft (controlPoints[0], 0);
v.setBoundingBoxTopRight (controlPoints[1], 0);
v.setBoundingBoxBottomLeft (controlPoints[2], 0);
v.setFontSizeAndScaleAnchor (controlPoints[3], 0);
return tree; return tree;
} }


+ 62
- 17
src/gui/graphics/drawables/juce_DrawableText.h View File

@@ -48,19 +48,8 @@ public:
virtual ~DrawableText(); virtual ~DrawableText();
//============================================================================== //==============================================================================
/** Sets the block of text to render */
void setText (const GlyphArrangement& newText);
/** Sets a single line of text to render.
This is a convenient method of adding a single line - for
more complex text, use the setText() that takes a
GlyphArrangement instead.
*/
void setText (const String& newText, const Font& fontToUse);
/** Returns the text arrangement that was set with setText(). */
const GlyphArrangement& getText() const throw() { return text; }
/** Sets the text to display.*/
void setText (const String& newText);
/** Sets the colour of the text. */ /** Sets the colour of the text. */
void setColour (const Colour& newColour); void setColour (const Colour& newColour);
@@ -68,6 +57,36 @@ public:
/** Returns the current text colour. */ /** Returns the current text colour. */
const Colour& getColour() const throw() { return colour; } const Colour& getColour() const throw() { return colour; }
/** Sets the font to use.
Note that the font height and horizontal scale are actually based upon the position
of the fontSizeAndScaleAnchor parameter to setBounds(). If applySizeAndScale is true, then
the height and scale control point will be moved to match the dimensions of the font supplied;
if it is false, then the new font's height and scale are ignored.
*/
void setFont (const Font& newFont, bool applySizeAndScale);
/** Changes the justification of the text within the bounding box. */
void setJustification (const Justification& newJustification);
/** Sets the bounding box and the control point that controls the font size.
The three bounding box points define the parallelogram within which the text will be
placed. The fontSizeAndScaleAnchor specifies a position within that parallelogram, whose
Y position (relative to the parallelogram's origin and possibly distorted shape) specifies
the font's height, and its X defines the font's horizontal scale.
*/
void setBounds (const RelativePoint& boundingBoxTopLeft,
const RelativePoint& boundingBoxTopRight,
const RelativePoint& boundingBoxBottomLeft,
const RelativePoint& fontSizeAndScaleAnchor);
/** Returns the origin of the text bounding box. */
const RelativePoint& getBoundingBoxTopLeft() const throw() { return controlPoints[0]; }
/** Returns the top-right of the text bounding box. */
const RelativePoint& getBoundingBoxTopRight() const throw() { return controlPoints[1]; }
/** Returns the bottom-left of the text bounding box. */
const RelativePoint& getBoundingBoxBottomLeft() const throw() { return controlPoints[2]; }
/** Returns the point within the text bounding box which defines the size and scale of the font. */
const RelativePoint& getFontSizeAndScaleAnchor() const throw() { return controlPoints[3]; }
//============================================================================== //==============================================================================
/** @internal */ /** @internal */
@@ -96,18 +115,44 @@ public:
public: public:
ValueTreeWrapper (const ValueTree& state); ValueTreeWrapper (const ValueTree& state);
//xxx todo
const String getText() const;
void setText (const String& newText, UndoManager* undoManager);
private:
static const Identifier text;
const Colour getColour() const;
void setColour (const Colour& newColour, UndoManager* undoManager);
const Justification getJustification() const;
void setJustification (const Justification& newJustification, UndoManager* undoManager);
const Font getFont() const;
void setFont (const Font& newFont, UndoManager* undoManager);
const RelativePoint getBoundingBoxTopLeft() const;
void setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager);
const RelativePoint getBoundingBoxTopRight() const;
void setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager);
const RelativePoint getBoundingBoxBottomLeft() const;
void setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager);
const RelativePoint getFontSizeAndScaleAnchor() const;
void setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager);
static const Identifier text, colour, font, justification, topLeft, topRight, bottomLeft, fontSizeAnchor;
}; };
//============================================================================== //==============================================================================
juce_UseDebuggingNewOperator juce_UseDebuggingNewOperator
private: private:
GlyphArrangement text;
RelativePoint controlPoints[4];
Font font;
String text;
Colour colour; Colour colour;
Justification justification;
void resolveCorners (Point<float>* corners) const;
DrawableText& operator= (const DrawableText&); DrawableText& operator= (const DrawableText&);
}; };


+ 9
- 0
src/gui/graphics/geometry/juce_AffineTransform.cpp View File

@@ -241,6 +241,15 @@ const AffineTransform AffineTransform::fromTargetPoints (const float x00, const
y10 - y00, y01 - y00, y00); y10 - y00, y01 - y00, y00);
} }
const AffineTransform AffineTransform::fromTargetPoints (const float sx1, const float sy1, const float tx1, const float ty1,
const float sx2, const float sy2, const float tx2, const float ty2,
const float sx3, const float sy3, const float tx3, const float ty3) throw()
{
return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3)
.inverted()
.followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3));
}
bool AffineTransform::isOnlyTranslation() const throw() bool AffineTransform::isOnlyTranslation() const throw()
{ {
return (mat01 == 0) return (mat01 == 0)


+ 6
- 0
src/gui/graphics/geometry/juce_AffineTransform.h View File

@@ -156,6 +156,12 @@ public:
float x10, float y10, float x10, float y10,
float x01, float y01) throw(); float x01, float y01) throw();
/** Returns the transform that will map three specified points onto three target points.
*/
static const AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1,
float sourceX2, float sourceY2, float targetX2, float targetY2,
float sourceX3, float sourceY3, float targetX3, float targetY3) throw();
//============================================================================== //==============================================================================
/** Returns the result of concatenating another transformation after this one. */ /** Returns the result of concatenating another transformation after this one. */
const AffineTransform followedBy (const AffineTransform& other) const throw(); const AffineTransform followedBy (const AffineTransform& other) const throw();


+ 12
- 0
src/gui/graphics/geometry/juce_Line.h View File

@@ -164,6 +164,18 @@ public:
return findIntersection (start, end, line.start, line.end, intersection); return findIntersection (start, end, line.start, line.end, intersection);
} }
/** Finds the intersection between two lines.
@param line the line to intersect with
@returns the point at which the lines intersect, even if this lies beyond the end of the lines
*/
const Point<ValueType> getIntersection (const Line& line) const throw()
{
Point<ValueType> p;
findIntersection (start, end, line.start, line.end, p);
return p;
}
//============================================================================== //==============================================================================
/** Returns the location of the point which is a given distance along this line. /** Returns the location of the point which is a given distance along this line.


+ 8
- 0
src/gui/graphics/geometry/juce_PathStrokeType.h View File

@@ -141,12 +141,20 @@ public:
/** Returns the stroke thickness. */ /** Returns the stroke thickness. */
float getStrokeThickness() const throw() { return thickness; } float getStrokeThickness() const throw() { return thickness; }
/** Sets the stroke thickness. */
void setStrokeThickness (float newThickness) throw() { thickness = newThickness; }
/** Returns the joint style. */ /** Returns the joint style. */
JointStyle getJointStyle() const throw() { return jointStyle; } JointStyle getJointStyle() const throw() { return jointStyle; }
/** Sets the joint style. */
void setJointStyle (JointStyle newStyle) throw() { jointStyle = newStyle; }
/** Returns the end-cap style. */ /** Returns the end-cap style. */
EndCapStyle getEndStyle() const throw() { return endStyle; } EndCapStyle getEndStyle() const throw() { return endStyle; }
/** Sets the end-cap style. */
void setEndStyle (EndCapStyle newStyle) throw() { endStyle = newStyle; }
//============================================================================== //==============================================================================
juce_UseDebuggingNewOperator juce_UseDebuggingNewOperator


+ 22
- 0
src/gui/graphics/geometry/juce_Rectangle.h View File

@@ -509,6 +509,28 @@ public:
return Rectangle<int> (x1, y1, x2 - x1, y2 - y1); return Rectangle<int> (x1, y1, x2 - x1, y2 - y1);
} }
/** Returns the smallest Rectangle that can contain a set of points. */
static const Rectangle findAreaContainingPoints (const Point<ValueType>* const points, const int numPoints) throw()
{
if (numPoints == 0)
return Rectangle();
ValueType minX (points[0].getX());
ValueType maxX (minX);
ValueType minY (points[0].getY());
ValueType maxY (minY);
for (int i = 1; i < numPoints; ++i)
{
minX = jmin (minX, points[i].getX());
maxX = jmax (maxX, points[i].getX());
minY = jmin (minY, points[i].getY());
maxY = jmax (maxY, points[i].getY());
}
return Rectangle (minX, minY, maxX - minX, maxY - minY);
}
/** Casts this rectangle to a Rectangle<float>. /** Casts this rectangle to a Rectangle<float>.
Obviously this is mainly useful for rectangles that use integer types. Obviously this is mainly useful for rectangles that use integer types.
@see getSmallestIntegerContainer @see getSmallestIntegerContainer


+ 33
- 7
src/native/mac/juce_mac_CoreGraphicsContext.mm View File

@@ -145,7 +145,8 @@ public:
: context (context_), : context (context_),
flipHeight (flipHeight_), flipHeight (flipHeight_),
state (new SavedState()), state (new SavedState()),
numGradientLookupEntries (0)
numGradientLookupEntries (0),
lastClipRectIsValid (false)
{ {
CGContextRetain (context); CGContextRetain (context);
CGContextSaveGState(context); CGContextSaveGState(context);
@@ -174,11 +175,21 @@ public:
void setOrigin (int x, int y) void setOrigin (int x, int y)
{ {
CGContextTranslateCTM (context, x, -y); CGContextTranslateCTM (context, x, -y);
if (lastClipRectIsValid)
lastClipRect.translate (-x, -y);
} }
bool clipToRectangle (const Rectangle<int>& r) bool clipToRectangle (const Rectangle<int>& r)
{ {
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));
if (lastClipRectIsValid)
{
lastClipRect = lastClipRect.getIntersection (r);
return ! r.isEmpty();
}
return ! isClipEmpty(); return ! isClipEmpty();
} }
@@ -187,6 +198,8 @@ public:
if (clipRegion.isEmpty()) if (clipRegion.isEmpty())
{ {
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
lastClipRectIsValid = true;
lastClipRect = Rectangle<int>();
return false; return false;
} }
else else
@@ -201,6 +214,7 @@ public:
} }
CGContextClipToRects (context, rects, numRects); CGContextClipToRects (context, rects, numRects);
lastClipRectIsValid = false;
return ! isClipEmpty(); return ! isClipEmpty();
} }
} }
@@ -210,12 +224,14 @@ public:
RectangleList remaining (getClipBounds()); RectangleList remaining (getClipBounds());
remaining.subtract (r); remaining.subtract (r);
clipToRectangleList (remaining); clipToRectangleList (remaining);
lastClipRectIsValid = false;
} }
void clipToPath (const Path& path, const AffineTransform& transform) void clipToPath (const Path& path, const AffineTransform& transform)
{ {
createPath (path, transform); createPath (path, transform);
CGContextClip (context); CGContextClip (context);
lastClipRectIsValid = false;
} }
void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform)
@@ -240,6 +256,7 @@ public:
flip(); flip();
CGImageRelease (image); CGImageRelease (image);
lastClipRectIsValid = false;
} }
} }
@@ -250,17 +267,23 @@ public:
const Rectangle<int> getClipBounds() const const Rectangle<int> getClipBounds() const
{ {
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
if (! lastClipRectIsValid)
{
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
lastClipRectIsValid = true;
lastClipRect.setBounds (roundToInt (bounds.origin.x),
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
roundToInt (bounds.size.width),
roundToInt (bounds.size.height));
}
return Rectangle<int> (roundToInt (bounds.origin.x),
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
roundToInt (bounds.size.width),
roundToInt (bounds.size.height));
return lastClipRect;
} }
bool isClipEmpty() const bool isClipEmpty() const
{ {
return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
return getClipBounds().isEmpty();
} }
//============================================================================== //==============================================================================
@@ -280,6 +303,7 @@ public:
{ {
state = top; state = top;
stateStack.removeLast (1, false); stateStack.removeLast (1, false);
lastClipRectIsValid = false;
} }
else else
{ {
@@ -564,6 +588,8 @@ private:
const CGFloat flipHeight; const CGFloat flipHeight;
CGColorSpaceRef rgbColourSpace, greyColourSpace; CGColorSpaceRef rgbColourSpace, greyColourSpace;
CGFunctionCallbacks gradientCallbacks; CGFunctionCallbacks gradientCallbacks;
mutable Rectangle<int> lastClipRect;
mutable bool lastClipRectIsValid;
struct SavedState struct SavedState
{ {


Loading…
Cancel
Save