mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-25 02:04:23 +00:00
Changes to DrawableText. Minor new methods for Rectangle, Line and AffineTransform. Optimisation for CoreGraphics clip bounds.
This commit is contained in:
parent
fffa698104
commit
d2492f5f3b
15 changed files with 1026 additions and 124 deletions
|
|
@ -185,6 +185,10 @@ public:
|
|||
DrawablePath::ValueTreeWrapper wrapper (item.getState());
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +331,7 @@ public:
|
|||
if (stroke.isGradient())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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()
|
||||
|
|
@ -720,6 +944,7 @@ DrawableTypeManager::DrawableTypeManager()
|
|||
handlers.add (new DrawablePathHandler());
|
||||
handlers.add (new DrawableImageHandler());
|
||||
handlers.add (new DrawableCompositeHandler());
|
||||
handlers.add (new DrawableTextHandler());
|
||||
}
|
||||
|
||||
DrawableTypeManager::~DrawableTypeManager()
|
||||
|
|
@ -738,7 +963,7 @@ DrawableTypeHandler* DrawableTypeManager::getHandlerFor (const Identifier& type)
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -750,6 +975,7 @@ const ValueTree DrawableTypeManager::createNewItem (const int index, DrawableDoc
|
|||
case 1: return DrawablePathHandler::createNewRectangle (document, approxPosition);
|
||||
case 2: return DrawablePathHandler::createNewEllipse (document, approxPosition);
|
||||
case 3: return DrawableImageHandler::createNewInstance (document, approxPosition);
|
||||
case 4: return DrawableTextHandler::createNewInstance (document, approxPosition);
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ MainWindow::MainWindow()
|
|||
// don't want the window to take focus when the title-bar is clicked..
|
||||
setWantsKeyboardFocus (false);
|
||||
|
||||
getPeer()->setCurrentRenderingEngine (0);
|
||||
//getPeer()->setCurrentRenderingEngine (0);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
|
|
|||
|
|
@ -84706,25 +84706,12 @@ const Rectangle<float> DrawableImage::getBounds() const
|
|||
if (image.isNull())
|
||||
return Rectangle<float>();
|
||||
|
||||
Point<float> resolved[3];
|
||||
Point<float> corners[4];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
resolved[i] = controlPoints[i].resolve (parent);
|
||||
corners[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());
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -85264,6 +85251,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree
|
|||
{
|
||||
damageRect = getBounds();
|
||||
path.swapWithPath (newPath);
|
||||
strokeNeedsUpdating = true;
|
||||
strokeType = newStroke;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
|
@ -85307,50 +85295,136 @@ END_JUCE_NAMESPACE
|
|||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
DrawableText::DrawableText()
|
||||
: colour (Colours::white)
|
||||
: colour (Colours::black),
|
||||
justification (Justification::centredLeft)
|
||||
{
|
||||
setFont (Font (15.0f), true);
|
||||
}
|
||||
|
||||
DrawableText::DrawableText (const DrawableText& other)
|
||||
: 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()
|
||||
{
|
||||
}
|
||||
|
||||
void DrawableText::setText (const GlyphArrangement& newText)
|
||||
void DrawableText::setText (const String& newText)
|
||||
{
|
||||
text = newText;
|
||||
}
|
||||
|
||||
void DrawableText::setText (const String& newText, const Font& fontToUse)
|
||||
{
|
||||
text.clear();
|
||||
text.addLineOfText (fontToUse, newText, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void DrawableText::setColour (const Colour& newColour)
|
||||
{
|
||||
colour = newColour;
|
||||
}
|
||||
|
||||
void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
|
||||
{
|
||||
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
|
||||
{
|
||||
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));
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
|
|
@ -85365,6 +85439,13 @@ void DrawableText::invalidatePoints()
|
|||
const Identifier DrawableText::valueTreeType ("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_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
|
|
@ -85372,12 +85453,113 @@ DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
|||
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*)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
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>();
|
||||
}
|
||||
|
|
@ -85388,8 +85570,14 @@ const ValueTree DrawableText::createValueTree (ImageProvider*) const
|
|||
ValueTreeWrapper v (tree);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -88911,6 +89099,15 @@ const AffineTransform AffineTransform::fromTargetPoints (const float x00, const
|
|||
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()
|
||||
{
|
||||
return (mat01 == 0)
|
||||
|
|
@ -263809,7 +264006,8 @@ public:
|
|||
: context (context_),
|
||||
flipHeight (flipHeight_),
|
||||
state (new SavedState()),
|
||||
numGradientLookupEntries (0)
|
||||
numGradientLookupEntries (0),
|
||||
lastClipRectIsValid (false)
|
||||
{
|
||||
CGContextRetain (context);
|
||||
CGContextSaveGState(context);
|
||||
|
|
@ -263837,11 +264035,21 @@ public:
|
|||
void setOrigin (int x, int y)
|
||||
{
|
||||
CGContextTranslateCTM (context, x, -y);
|
||||
|
||||
if (lastClipRectIsValid)
|
||||
lastClipRect.translate (-x, -y);
|
||||
}
|
||||
|
||||
bool clipToRectangle (const Rectangle<int>& r)
|
||||
{
|
||||
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));
|
||||
|
||||
if (lastClipRectIsValid)
|
||||
{
|
||||
lastClipRect = lastClipRect.getIntersection (r);
|
||||
return ! r.isEmpty();
|
||||
}
|
||||
|
||||
return ! isClipEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -263850,6 +264058,8 @@ public:
|
|||
if (clipRegion.isEmpty())
|
||||
{
|
||||
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
|
||||
lastClipRectIsValid = true;
|
||||
lastClipRect = Rectangle<int>();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
|
@ -263864,6 +264074,7 @@ public:
|
|||
}
|
||||
|
||||
CGContextClipToRects (context, rects, numRects);
|
||||
lastClipRectIsValid = false;
|
||||
return ! isClipEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -263873,12 +264084,14 @@ public:
|
|||
RectangleList remaining (getClipBounds());
|
||||
remaining.subtract (r);
|
||||
clipToRectangleList (remaining);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
|
||||
void clipToPath (const Path& path, const AffineTransform& transform)
|
||||
{
|
||||
createPath (path, transform);
|
||||
CGContextClip (context);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
|
||||
void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform)
|
||||
|
|
@ -263903,6 +264116,7 @@ public:
|
|||
flip();
|
||||
|
||||
CGImageRelease (image);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -263913,17 +264127,23 @@ public:
|
|||
|
||||
const Rectangle<int> getClipBounds() const
|
||||
{
|
||||
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
if (! lastClipRectIsValid)
|
||||
{
|
||||
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
|
||||
return Rectangle<int> (roundToInt (bounds.origin.x),
|
||||
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
|
||||
roundToInt (bounds.size.width),
|
||||
roundToInt (bounds.size.height));
|
||||
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 lastClipRect;
|
||||
}
|
||||
|
||||
bool isClipEmpty() const
|
||||
{
|
||||
return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
|
||||
return getClipBounds().isEmpty();
|
||||
}
|
||||
|
||||
void saveState()
|
||||
|
|
@ -263942,6 +264162,7 @@ public:
|
|||
{
|
||||
state = top;
|
||||
stateStack.removeLast (1, false);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -264223,6 +264444,8 @@ private:
|
|||
const CGFloat flipHeight;
|
||||
CGColorSpaceRef rgbColourSpace, greyColourSpace;
|
||||
CGFunctionCallbacks gradientCallbacks;
|
||||
mutable Rectangle<int> lastClipRect;
|
||||
mutable bool lastClipRectIsValid;
|
||||
|
||||
struct SavedState
|
||||
{
|
||||
|
|
@ -268422,7 +268645,8 @@ public:
|
|||
: context (context_),
|
||||
flipHeight (flipHeight_),
|
||||
state (new SavedState()),
|
||||
numGradientLookupEntries (0)
|
||||
numGradientLookupEntries (0),
|
||||
lastClipRectIsValid (false)
|
||||
{
|
||||
CGContextRetain (context);
|
||||
CGContextSaveGState(context);
|
||||
|
|
@ -268450,11 +268674,21 @@ public:
|
|||
void setOrigin (int x, int y)
|
||||
{
|
||||
CGContextTranslateCTM (context, x, -y);
|
||||
|
||||
if (lastClipRectIsValid)
|
||||
lastClipRect.translate (-x, -y);
|
||||
}
|
||||
|
||||
bool clipToRectangle (const Rectangle<int>& r)
|
||||
{
|
||||
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));
|
||||
|
||||
if (lastClipRectIsValid)
|
||||
{
|
||||
lastClipRect = lastClipRect.getIntersection (r);
|
||||
return ! r.isEmpty();
|
||||
}
|
||||
|
||||
return ! isClipEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -268463,6 +268697,8 @@ public:
|
|||
if (clipRegion.isEmpty())
|
||||
{
|
||||
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
|
||||
lastClipRectIsValid = true;
|
||||
lastClipRect = Rectangle<int>();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
|
@ -268477,6 +268713,7 @@ public:
|
|||
}
|
||||
|
||||
CGContextClipToRects (context, rects, numRects);
|
||||
lastClipRectIsValid = false;
|
||||
return ! isClipEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -268486,12 +268723,14 @@ public:
|
|||
RectangleList remaining (getClipBounds());
|
||||
remaining.subtract (r);
|
||||
clipToRectangleList (remaining);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
|
||||
void clipToPath (const Path& path, const AffineTransform& transform)
|
||||
{
|
||||
createPath (path, transform);
|
||||
CGContextClip (context);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
|
||||
void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform)
|
||||
|
|
@ -268516,6 +268755,7 @@ public:
|
|||
flip();
|
||||
|
||||
CGImageRelease (image);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268526,17 +268766,23 @@ public:
|
|||
|
||||
const Rectangle<int> getClipBounds() const
|
||||
{
|
||||
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
if (! lastClipRectIsValid)
|
||||
{
|
||||
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
|
||||
return Rectangle<int> (roundToInt (bounds.origin.x),
|
||||
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
|
||||
roundToInt (bounds.size.width),
|
||||
roundToInt (bounds.size.height));
|
||||
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 lastClipRect;
|
||||
}
|
||||
|
||||
bool isClipEmpty() const
|
||||
{
|
||||
return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
|
||||
return getClipBounds().isEmpty();
|
||||
}
|
||||
|
||||
void saveState()
|
||||
|
|
@ -268555,6 +268801,7 @@ public:
|
|||
{
|
||||
state = top;
|
||||
stateStack.removeLast (1, false);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -268836,6 +269083,8 @@ private:
|
|||
const CGFloat flipHeight;
|
||||
CGColorSpaceRef rgbColourSpace, greyColourSpace;
|
||||
CGFunctionCallbacks gradientCallbacks;
|
||||
mutable Rectangle<int> lastClipRect;
|
||||
mutable bool lastClipRectIsValid;
|
||||
|
||||
struct SavedState
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18946,6 +18946,12 @@ public:
|
|||
float x10, float y10,
|
||||
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. */
|
||||
const AffineTransform followedBy (const AffineTransform& other) const throw();
|
||||
|
||||
|
|
@ -19981,6 +19987,18 @@ public:
|
|||
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.
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
/** 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>.
|
||||
Obviously this is mainly useful for rectangles that use integer types.
|
||||
@see getSmallestIntegerContainer
|
||||
|
|
@ -22147,12 +22187,21 @@ public:
|
|||
/** Returns the stroke thickness. */
|
||||
float getStrokeThickness() const throw() { return thickness; }
|
||||
|
||||
/** Sets the stroke thickness. */
|
||||
void setStrokeThickness (float newThickness) throw() { thickness = newThickness; }
|
||||
|
||||
/** Returns the joint style. */
|
||||
JointStyle getJointStyle() const throw() { return jointStyle; }
|
||||
|
||||
/** Sets the joint style. */
|
||||
void setJointStyle (JointStyle newStyle) throw() { jointStyle = newStyle; }
|
||||
|
||||
/** Returns the end-cap style. */
|
||||
EndCapStyle getEndStyle() const throw() { return endStyle; }
|
||||
|
||||
/** Sets the end-cap style. */
|
||||
void setEndStyle (EndCapStyle newStyle) throw() { endStyle = newStyle; }
|
||||
|
||||
juce_UseDebuggingNewOperator
|
||||
|
||||
/** Compares the stroke thickness, joint and end styles of two stroke types. */
|
||||
|
|
@ -31096,9 +31145,16 @@ public:
|
|||
*/
|
||||
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();
|
||||
|
||||
/** Returns the current gain.
|
||||
@see setGain
|
||||
*/
|
||||
float getGain() const throw() { return gain; }
|
||||
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int totalNumInputChannels,
|
||||
|
|
@ -58788,19 +58844,8 @@ public:
|
|||
/** Destructor. */
|
||||
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. */
|
||||
void setColour (const Colour& newColour);
|
||||
|
|
@ -58808,6 +58853,37 @@ public:
|
|||
/** Returns the current text 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 */
|
||||
void render (const Drawable::RenderingContext& context) const;
|
||||
/** @internal */
|
||||
|
|
@ -58833,17 +58909,43 @@ public:
|
|||
public:
|
||||
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
|
||||
|
||||
private:
|
||||
GlyphArrangement text;
|
||||
RelativePoint controlPoints[4];
|
||||
Font font;
|
||||
String text;
|
||||
Colour colour;
|
||||
Justification justification;
|
||||
|
||||
void resolveCorners (Point<float>* corners) const;
|
||||
|
||||
DrawableText& operator= (const DrawableText&);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -74,9 +74,16 @@ public:
|
|||
*/
|
||||
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();
|
||||
|
||||
/** Returns the current gain.
|
||||
@see setGain
|
||||
*/
|
||||
float getGain() const throw() { return gain; }
|
||||
|
||||
//==============================================================================
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
|
|
|
|||
|
|
@ -130,25 +130,12 @@ const Rectangle<float> DrawableImage::getBounds() const
|
|||
if (image.isNull())
|
||||
return Rectangle<float>();
|
||||
|
||||
Point<float> resolved[3];
|
||||
Point<float> corners[4];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
resolved[i] = controlPoints[i].resolve (parent);
|
||||
corners[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());
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -382,6 +382,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree
|
|||
{
|
||||
damageRect = getBounds();
|
||||
path.swapWithPath (newPath);
|
||||
strokeNeedsUpdating = true;
|
||||
strokeType = newStroke;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,18 +28,25 @@
|
|||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
#include "juce_DrawableText.h"
|
||||
#include "juce_DrawableComposite.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
DrawableText::DrawableText()
|
||||
: colour (Colours::white)
|
||||
: colour (Colours::black),
|
||||
justification (Justification::centredLeft)
|
||||
{
|
||||
setFont (Font (15.0f), true);
|
||||
}
|
||||
|
||||
DrawableText::DrawableText (const DrawableText& other)
|
||||
: 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()
|
||||
|
|
@ -47,37 +54,117 @@ DrawableText::~DrawableText()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::setText (const GlyphArrangement& newText)
|
||||
void DrawableText::setText (const String& newText)
|
||||
{
|
||||
text = newText;
|
||||
}
|
||||
|
||||
void DrawableText::setText (const String& newText, const Font& fontToUse)
|
||||
{
|
||||
text.clear();
|
||||
text.addLineOfText (fontToUse, newText, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void DrawableText::setColour (const Colour& newColour)
|
||||
{
|
||||
colour = newColour;
|
||||
}
|
||||
|
||||
void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
|
||||
{
|
||||
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
|
||||
{
|
||||
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));
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
|
|
@ -93,19 +180,128 @@ void DrawableText::invalidatePoints()
|
|||
const Identifier DrawableText::valueTreeType ("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_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
{
|
||||
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*)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
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>();
|
||||
}
|
||||
|
|
@ -116,8 +312,14 @@ const ValueTree DrawableText::createValueTree (ImageProvider*) const
|
|||
ValueTreeWrapper v (tree);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,19 +48,8 @@ public:
|
|||
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. */
|
||||
void setColour (const Colour& newColour);
|
||||
|
|
@ -68,6 +57,36 @@ public:
|
|||
/** Returns the current text 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 */
|
||||
|
|
@ -96,18 +115,44 @@ public:
|
|||
public:
|
||||
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
|
||||
|
||||
private:
|
||||
GlyphArrangement text;
|
||||
RelativePoint controlPoints[4];
|
||||
Font font;
|
||||
String text;
|
||||
Colour colour;
|
||||
Justification justification;
|
||||
|
||||
void resolveCorners (Point<float>* corners) const;
|
||||
|
||||
DrawableText& operator= (const DrawableText&);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -241,6 +241,15 @@ const AffineTransform AffineTransform::fromTargetPoints (const float x00, const
|
|||
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()
|
||||
{
|
||||
return (mat01 == 0)
|
||||
|
|
|
|||
|
|
@ -156,6 +156,12 @@ public:
|
|||
float x10, float y10,
|
||||
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. */
|
||||
const AffineTransform followedBy (const AffineTransform& other) const throw();
|
||||
|
|
|
|||
|
|
@ -164,6 +164,18 @@ public:
|
|||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -141,12 +141,20 @@ public:
|
|||
/** Returns the stroke thickness. */
|
||||
float getStrokeThickness() const throw() { return thickness; }
|
||||
|
||||
/** Sets the stroke thickness. */
|
||||
void setStrokeThickness (float newThickness) throw() { thickness = newThickness; }
|
||||
|
||||
/** Returns the joint style. */
|
||||
JointStyle getJointStyle() const throw() { return jointStyle; }
|
||||
|
||||
/** Sets the joint style. */
|
||||
void setJointStyle (JointStyle newStyle) throw() { jointStyle = newStyle; }
|
||||
|
||||
/** Returns the end-cap style. */
|
||||
EndCapStyle getEndStyle() const throw() { return endStyle; }
|
||||
|
||||
/** Sets the end-cap style. */
|
||||
void setEndStyle (EndCapStyle newStyle) throw() { endStyle = newStyle; }
|
||||
|
||||
//==============================================================================
|
||||
juce_UseDebuggingNewOperator
|
||||
|
|
|
|||
|
|
@ -509,6 +509,28 @@ public:
|
|||
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>.
|
||||
Obviously this is mainly useful for rectangles that use integer types.
|
||||
@see getSmallestIntegerContainer
|
||||
|
|
|
|||
|
|
@ -145,7 +145,8 @@ public:
|
|||
: context (context_),
|
||||
flipHeight (flipHeight_),
|
||||
state (new SavedState()),
|
||||
numGradientLookupEntries (0)
|
||||
numGradientLookupEntries (0),
|
||||
lastClipRectIsValid (false)
|
||||
{
|
||||
CGContextRetain (context);
|
||||
CGContextSaveGState(context);
|
||||
|
|
@ -174,11 +175,21 @@ public:
|
|||
void setOrigin (int x, int y)
|
||||
{
|
||||
CGContextTranslateCTM (context, x, -y);
|
||||
|
||||
if (lastClipRectIsValid)
|
||||
lastClipRect.translate (-x, -y);
|
||||
}
|
||||
|
||||
bool clipToRectangle (const Rectangle<int>& r)
|
||||
{
|
||||
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));
|
||||
|
||||
if (lastClipRectIsValid)
|
||||
{
|
||||
lastClipRect = lastClipRect.getIntersection (r);
|
||||
return ! r.isEmpty();
|
||||
}
|
||||
|
||||
return ! isClipEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -187,6 +198,8 @@ public:
|
|||
if (clipRegion.isEmpty())
|
||||
{
|
||||
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
|
||||
lastClipRectIsValid = true;
|
||||
lastClipRect = Rectangle<int>();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
|
@ -201,6 +214,7 @@ public:
|
|||
}
|
||||
|
||||
CGContextClipToRects (context, rects, numRects);
|
||||
lastClipRectIsValid = false;
|
||||
return ! isClipEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -210,12 +224,14 @@ public:
|
|||
RectangleList remaining (getClipBounds());
|
||||
remaining.subtract (r);
|
||||
clipToRectangleList (remaining);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
|
||||
void clipToPath (const Path& path, const AffineTransform& transform)
|
||||
{
|
||||
createPath (path, transform);
|
||||
CGContextClip (context);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
|
||||
void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform)
|
||||
|
|
@ -240,6 +256,7 @@ public:
|
|||
flip();
|
||||
|
||||
CGImageRelease (image);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,17 +267,23 @@ public:
|
|||
|
||||
const Rectangle<int> getClipBounds() const
|
||||
{
|
||||
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
if (! lastClipRectIsValid)
|
||||
{
|
||||
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
|
||||
return Rectangle<int> (roundToInt (bounds.origin.x),
|
||||
roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
|
||||
roundToInt (bounds.size.width),
|
||||
roundToInt (bounds.size.height));
|
||||
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 lastClipRect;
|
||||
}
|
||||
|
||||
bool isClipEmpty() const
|
||||
{
|
||||
return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
|
||||
return getClipBounds().isEmpty();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -280,6 +303,7 @@ public:
|
|||
{
|
||||
state = top;
|
||||
stateStack.removeLast (1, false);
|
||||
lastClipRectIsValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -564,6 +588,8 @@ private:
|
|||
const CGFloat flipHeight;
|
||||
CGColorSpaceRef rgbColourSpace, greyColourSpace;
|
||||
CGFunctionCallbacks gradientCallbacks;
|
||||
mutable Rectangle<int> lastClipRect;
|
||||
mutable bool lastClipRectIsValid;
|
||||
|
||||
struct SavedState
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue