Browse Source

Added method Drawable::getOutlineAsPath(), and used this for parsing SVG clip regions

tags/2021-05-28
jules 8 years ago
parent
commit
cb7ecfd77b
11 changed files with 418 additions and 45 deletions
  1. +22
    -0
      modules/juce_gui_basics/drawables/juce_Drawable.cpp
  2. +27
    -1
      modules/juce_gui_basics/drawables/juce_Drawable.h
  3. +13
    -0
      modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp
  4. +2
    -0
      modules/juce_gui_basics/drawables/juce_DrawableComposite.h
  5. +5
    -0
      modules/juce_gui_basics/drawables/juce_DrawableImage.cpp
  6. +2
    -0
      modules/juce_gui_basics/drawables/juce_DrawableImage.h
  7. +8
    -0
      modules/juce_gui_basics/drawables/juce_DrawableShape.cpp
  8. +2
    -0
      modules/juce_gui_basics/drawables/juce_DrawableShape.h
  9. +42
    -4
      modules/juce_gui_basics/drawables/juce_DrawableText.cpp
  10. +4
    -0
      modules/juce_gui_basics/drawables/juce_DrawableText.h
  11. +291
    -40
      modules/juce_gui_basics/drawables/juce_SVGParser.cpp

+ 22
- 0
modules/juce_gui_basics/drawables/juce_Drawable.cpp View File

@@ -44,6 +44,17 @@ Drawable::~Drawable()
{
}
void Drawable::applyDrawableClipPath (Graphics& g)
{
if (drawableClipPath != nullptr)
{
auto clipPath = drawableClipPath->getOutlineAsPath();
if (! clipPath.isEmpty())
g.getInternalContext().clipToPath (clipPath, {});
}
}
//==============================================================================
void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const
{
@@ -59,6 +70,8 @@ void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform&
.followedBy (getTransform())
.followedBy (transform));
applyDrawableClipPath (g);
if (! g.isClipEmpty())
{
if (opacity < 1.0f)
@@ -91,6 +104,15 @@ DrawableComposite* Drawable::getParent() const
return dynamic_cast<DrawableComposite*> (getParentComponent());
}
void Drawable::setClipPath (Drawable* clipPath)
{
if (drawableClipPath != clipPath)
{
drawableClipPath = clipPath;
repaint();
}
}
void Drawable::transformContextToCorrectOrigin (Graphics& g)
{
g.setOrigin (originRelativeToComponent);


+ 27
- 1
modules/juce_gui_basics/drawables/juce_Drawable.h View File

@@ -54,6 +54,9 @@ public:
*/
virtual Drawable* createCopy() const = 0;
/** Creates a path that describes the outline of this drawable. */
virtual Path getOutlineAsPath() const = 0;
//==============================================================================
/** Renders this Drawable object.
@@ -63,7 +66,8 @@ public:
@see drawWithin
*/
void draw (Graphics& g, float opacity, const AffineTransform& transform = {}) const;
void draw (Graphics& g, float opacity,
const AffineTransform& transform = AffineTransform()) const;
/** Renders the Drawable at a given offset within the Graphics context.
@@ -116,6 +120,11 @@ public:
/** Returns the DrawableComposite that contains this object, if there is one. */
DrawableComposite* getParent() const;
/** Sets a the clipping region of this drawable using another drawable.
The drawbale passed in ill be deleted when no longer needed.
*/
void setClipPath (Drawable* drawableClipPath);
//==============================================================================
/** Tries to turn some kind of image file into a drawable.
@@ -149,6 +158,20 @@ public:
*/
static Drawable* createFromSVG (const XmlElement& svgDocument);
/** Attempts to parse an SVG (Scalable Vector Graphics) document from a file,
and to turn this into a Drawable tree.
The object returned must be deleted by the caller. If something goes wrong
while parsing, it may return nullptr.
SVG is a pretty large and complex spec, and this doesn't aim to be a full
implementation, but it can return the basic vector objects.
Any references to references to external image files will be relative to
the parent directory of the file passed.
*/
static Drawable* createFromSVGFile (const File& svgFile);
/** Parses an SVG path string and returns it. */
static Path parseSVGPath (const String& svgPath);
@@ -213,8 +236,11 @@ protected:
void parentHierarchyChanged() override;
/** @internal */
void setBoundsToEnclose (Rectangle<float>);
/** @internal */
void applyDrawableClipPath (Graphics&);
Point<int> originRelativeToComponent;
ScopedPointer<Drawable> drawableClipPath;
#ifndef DOXYGEN
/** Internal utility class used by Drawables. */


+ 13
- 0
modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp View File

@@ -319,3 +319,16 @@ ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* i
return tree;
}
Path DrawableComposite::getOutlineAsPath() const
{
Path p;
for (int i = 0; i < getNumChildComponents(); ++i)
if (auto* childDrawable = dynamic_cast<Drawable*> (getChildComponent (i)))
p.addPath (childDrawable->getOutlineAsPath());
p.applyTransform (getTransform());
return p;
}

+ 2
- 0
modules/juce_gui_basics/drawables/juce_DrawableComposite.h View File

@@ -115,6 +115,8 @@ public:
void parentHierarchyChanged() override;
/** @internal */
MarkerList* getMarkers (bool xAxis) override;
/** @internal */
Path getOutlineAsPath() const override;
//==============================================================================
/** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */


+ 5
- 0
modules/juce_gui_basics/drawables/juce_DrawableImage.cpp View File

@@ -290,3 +290,8 @@ ValueTree DrawableImage::createValueTree (ComponentBuilder::ImageProvider* image
return tree;
}
Path DrawableImage::getOutlineAsPath() const
{
return {}; // not applicable for images
}

+ 2
- 0
modules/juce_gui_basics/drawables/juce_DrawableImage.h View File

@@ -94,6 +94,8 @@ public:
ValueTree createValueTree (ComponentBuilder::ImageProvider*) const override;
/** @internal */
static const Identifier valueTreeType;
/** @internal */
Path getOutlineAsPath() const override;
//==============================================================================
/** Internally-used class for wrapping a DrawableImage's state into a ValueTree. */


+ 8
- 0
modules/juce_gui_basics/drawables/juce_DrawableShape.cpp View File

@@ -171,6 +171,7 @@ void DrawableShape::writeTo (FillAndStrokeState& state, ComponentBuilder::ImageP
void DrawableShape::paint (Graphics& g)
{
transformContextToCorrectOrigin (g);
applyDrawableClipPath (g);
g.setFillType (mainFill.fill);
g.fillPath (path);
@@ -488,3 +489,10 @@ bool DrawableShape::replaceColour (Colour original, Colour replacement)
bool changed2 = replaceColourInFill (strokeFill, original, replacement);
return changed1 || changed2;
}
Path DrawableShape::getOutlineAsPath() const
{
Path outline (isStrokeVisible() ? strokePath : path);
outline.applyTransform (getTransform());
return outline;
}

+ 2
- 0
modules/juce_gui_basics/drawables/juce_DrawableShape.h View File

@@ -156,6 +156,8 @@ public:
bool hitTest (int x, int y) override;
/** @internal */
bool replaceColour (Colour originalColour, Colour replacementColour) override;
/** @internal */
Path getOutlineAsPath() const override;
protected:
//==============================================================================


+ 42
- 4
modules/juce_gui_basics/drawables/juce_DrawableText.cpp View File

@@ -162,6 +162,18 @@ void DrawableText::recalculateCoordinates (Expression::Scope* scope)
}
//==============================================================================
Rectangle<int> DrawableText::getTextArea (float w, float h) const
{
return Rectangle<float> (w, h).getSmallestIntegerContainer();
}
AffineTransform DrawableText::getTextTransform (float w, float h) const
{
return AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y,
w, 0, resolvedPoints[1].x, resolvedPoints[1].y,
0, h, resolvedPoints[2].x, resolvedPoints[2].y);
}
void DrawableText::paint (Graphics& g)
{
transformContextToCorrectOrigin (g);
@@ -169,13 +181,11 @@ void DrawableText::paint (Graphics& g)
const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
g.addTransform (AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y,
w, 0, resolvedPoints[1].x, resolvedPoints[1].y,
0, h, resolvedPoints[2].x, resolvedPoints[2].y));
g.addTransform (getTextTransform (w, h));
g.setFont (scaledFont);
g.setColour (colour);
g.drawFittedText (text, Rectangle<float> (w, h).getSmallestIntegerContainer(), justification, 0x100000);
g.drawFittedText (text, getTextArea (w, h), justification, 0x100000);
}
Rectangle<float> DrawableText::getDrawableBounds() const
@@ -334,3 +344,31 @@ ValueTree DrawableText::createValueTree (ComponentBuilder::ImageProvider*) const
return tree;
}
Path DrawableText::getOutlineAsPath() const
{
auto w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
auto h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
const auto area = getTextArea (w, h).toFloat();
GlyphArrangement arr;
arr.addFittedText (scaledFont, text,
area.getX(), area.getY(),
area.getWidth(), area.getHeight(),
justification,
0x100000);
Path pathOfAllGlyphs;
for (int i = 0; i < arr.getNumGlyphs(); ++i)
{
Path gylphPath;
arr.getGlyph (i).createPath (gylphPath);
pathOfAllGlyphs.addPath (gylphPath);
}
pathOfAllGlyphs.applyTransform (getTextTransform (w, h)
.followedBy (getTransform()));
return pathOfAllGlyphs;
}

+ 4
- 0
modules/juce_gui_basics/drawables/juce_DrawableText.h View File

@@ -99,6 +99,8 @@ public:
static const Identifier valueTreeType;
/** @internal */
Rectangle<float> getDrawableBounds() const override;
/** @internal */
Path getOutlineAsPath() const override;
//==============================================================================
/** Internally-used class for wrapping a DrawableText's state into a ValueTree. */
@@ -147,6 +149,8 @@ private:
bool registerCoordinates (RelativeCoordinatePositionerBase&);
void recalculateCoordinates (Expression::Scope*);
void refreshBounds();
Rectangle<int> getTextArea (float width, float height) const;
AffineTransform getTextTransform (float width, float height) const;
DrawableText& operator= (const DrawableText&);
JUCE_LEAK_DETECTOR (DrawableText)


+ 291
- 40
modules/juce_gui_basics/drawables/juce_SVGParser.cpp View File

@@ -28,7 +28,8 @@ class SVGState
{
public:
//==============================================================================
explicit SVGState (const XmlElement* topLevel) : topLevelXml (topLevel, nullptr)
explicit SVGState (const XmlElement* topLevel, const File& svgFile = {})
: originalFile (svgFile), topLevelXml (topLevel, nullptr)
{
}
@@ -47,11 +48,9 @@ public:
{
XmlPath child (e, this);
if (e->compareAttribute ("id", id))
{
op (child);
return true;
}
if (e->compareAttribute ("id", id)
&& ! child->hasTagName ("defs"))
return op (child);
if (child.applyOperationToChildWithID (id, op))
return true;
@@ -70,20 +69,60 @@ public:
const SVGState* state;
Path* targetPath;
void operator() (const XmlPath& xmlPath) const
bool operator() (const XmlPath& xmlPath) const
{
state->parsePathElement (xmlPath, *targetPath);
return state->parsePathElement (xmlPath, *targetPath);
}
};
struct GetClipPathOp
struct UseShapeOp
{
const SVGState* state;
Path* sourcePath;
AffineTransform* transform;
Drawable* target;
bool operator() (const XmlPath& xmlPath)
{
target = state->parseShape (xmlPath, *sourcePath, true, transform);
return target != nullptr;
}
};
struct UseTextOp
{
const SVGState* state;
AffineTransform* transform;
Drawable* target;
bool operator() (const XmlPath& xmlPath)
{
target = state->parseText (xmlPath, true, transform);
return target != nullptr;
}
};
struct UseImageOp
{
const SVGState* state;
AffineTransform* transform;
Drawable* target;
void operator() (const XmlPath& xmlPath) const
bool operator() (const XmlPath& xmlPath)
{
state->applyClipPath (*target, xmlPath);
target = state->parseImage (xmlPath, true, transform);
return target != nullptr;
}
};
struct GetClipPathOp
{
SVGState* state;
Drawable* target;
bool operator() (const XmlPath& xmlPath)
{
return state->applyClipPath (*target, xmlPath);
}
};
@@ -92,9 +131,9 @@ public:
const SVGState* state;
ColourGradient* gradient;
void operator() (const XmlPath& xml) const
bool operator() (const XmlPath& xml) const
{
state->addGradientStopsIn (*gradient, xml);
return state->addGradientStopsIn (*gradient, xml);
}
};
@@ -105,11 +144,16 @@ public:
float opacity;
FillType fillType;
void operator() (const XmlPath& xml)
bool operator() (const XmlPath& xml)
{
if (xml->hasTagNameIgnoringNamespace ("linearGradient")
|| xml->hasTagNameIgnoringNamespace ("radialGradient"))
{
fillType = state->getGradientFillType (xml, *path, opacity);
return true;
}
return false;
}
};
@@ -402,6 +446,7 @@ public:
private:
//==============================================================================
const File originalFile;
const XmlPath topLevelXml;
float width = 512, height = 512, viewBoxW = 0, viewBoxH = 0;
AffineTransform transform;
@@ -423,7 +468,7 @@ private:
}
//==============================================================================
void parseSubElements (const XmlPath& xml, DrawableComposite& parentDrawable)
void parseSubElements (const XmlPath& xml, DrawableComposite& parentDrawable, const bool shouldParseClip = true)
{
forEachXmlChildElement (*xml, e)
{
@@ -435,6 +480,9 @@ private:
if (! isNone (getStyleAttribute (child, "display")))
drawable->setVisible (true);
if (shouldParseClip)
parseClipPath (child, *drawable);
}
}
}
@@ -449,11 +497,13 @@ private:
auto tag = xml->getTagNameWithoutNamespace();
if (tag == "g") return parseGroupElement (xml);
if (tag == "g") return parseGroupElement (xml, true);
if (tag == "svg") return parseSVGElement (xml);
if (tag == "text") return parseText (xml, true);
if (tag == "image") return parseImage (xml, true);
if (tag == "switch") return parseSwitch (xml);
if (tag == "a") return parseLinkElement (xml);
if (tag == "use") return parseUseOther (xml);
if (tag == "style") parseCSSStyle (xml);
if (tag == "defs") parseDefs (xml);
@@ -462,7 +512,7 @@ private:
bool parsePathElement (const XmlPath& xml, Path& path) const
{
const String tag (xml->getTagNameWithoutNamespace());
auto tag = xml->getTagNameWithoutNamespace();
if (tag == "path") { parsePath (xml, path); return true; }
if (tag == "rect") { parseRect (xml, path); return true; }
@@ -471,7 +521,7 @@ private:
if (tag == "line") { parseLine (xml, path); return true; }
if (tag == "polyline") { parsePolygon (xml, true, path); return true; }
if (tag == "polygon") { parsePolygon (xml, false, path); return true; }
if (tag == "use") { parseUse (xml, path); return true; }
if (tag == "use") { return parseUsePath (xml, path); }
return false;
}
@@ -484,9 +534,17 @@ private:
return nullptr;
}
DrawableComposite* parseGroupElement (const XmlPath& xml)
DrawableComposite* parseGroupElement (const XmlPath& xml, bool shouldParseTransform = true)
{
auto drawable = new DrawableComposite();
if (shouldParseTransform && xml->hasAttribute ("transform"))
{
SVGState newState (*this);
newState.addTransform (xml);
return newState.parseGroupElement (xml, false);
}
auto* drawable = new DrawableComposite();
setCommonAttributes (*drawable, xml);
@@ -602,17 +660,35 @@ private:
}
}
void parseUse (const XmlPath& xml, Path& path) const
static String getLinkedID (const XmlPath& xml)
{
auto link = xml->getStringAttribute ("xlink:href");
if (link.startsWithChar ('#'))
{
auto linkedID = link.substring (1);
return link.substring (1);
return {};
}
bool parseUsePath (const XmlPath& xml, Path& path) const
{
auto linkedID = getLinkedID (xml);
if (linkedID.isNotEmpty())
{
UsePathOp op = { this, &path };
topLevelXml.applyOperationToChildWithID (linkedID, op);
return topLevelXml.applyOperationToChildWithID (linkedID, op);
}
return false;
}
Drawable* parseUseOther (const XmlPath& xml) const
{
if (auto* drawableText = parseText (xml, false)) return drawableText;
if (auto* drawableImage = parseImage (xml, false)) return drawableImage;
return nullptr;
}
static String parseURL (const String& str)
@@ -625,22 +701,46 @@ private:
}
//==============================================================================
Drawable* useShape (const XmlPath& xml, Path& path) const
{
auto translation = AffineTransform::translation ((float) xml->getDoubleAttribute ("x", 0.0),
(float) xml->getDoubleAttribute ("y", 0.0));
UseShapeOp op = { this, &path, &translation, nullptr };
auto linkedID = getLinkedID (xml);
if (linkedID.isNotEmpty())
topLevelXml.applyOperationToChildWithID (linkedID, op);
return op.target;
}
Drawable* parseShape (const XmlPath& xml, Path& path,
const bool shouldParseTransform = true) const
const bool shouldParseTransform = true,
AffineTransform* additonalTransform = nullptr) const
{
if (shouldParseTransform && xml->hasAttribute ("transform"))
{
SVGState newState (*this);
newState.addTransform (xml);
return newState.parseShape (xml, path, false);
return newState.parseShape (xml, path, false, additonalTransform);
}
if (xml->hasTagName ("use"))
return useShape (xml, path);
auto dp = new DrawablePath();
setCommonAttributes (*dp, xml);
dp->setFill (Colours::transparentBlack);
path.applyTransform (transform);
if (additonalTransform != nullptr)
path.applyTransform (*additonalTransform);
dp->setPath (path);
dp->setFill (getPathFillType (path, xml, "fill",
@@ -666,7 +766,6 @@ private:
if (strokeDashArray.isNotEmpty())
parseDashArray (strokeDashArray, *dp);
parseClipPath (xml, *dp);
return dp;
}
@@ -726,7 +825,7 @@ private:
}
}
void parseClipPath (const XmlPath& xml, Drawable& d) const
bool parseClipPath (const XmlPath& xml, Drawable& d)
{
const String clipPath (getStyleAttribute (xml, "clip-path"));
@@ -737,22 +836,36 @@ private:
if (urlID.isNotEmpty())
{
GetClipPathOp op = { this, &d };
topLevelXml.applyOperationToChildWithID (urlID, op);
return topLevelXml.applyOperationToChildWithID (urlID, op);
}
}
return false;
}
void applyClipPath (Drawable& target, const XmlPath& xmlPath) const
bool applyClipPath (Drawable& target, const XmlPath& xmlPath)
{
if (xmlPath->hasTagNameIgnoringNamespace ("clipPath"))
{
// TODO: implement clipping..
ignoreUnused (target);
ScopedPointer<DrawableComposite> drawableClipPath (new DrawableComposite());
parseSubElements (xmlPath, *drawableClipPath, false);
if (drawableClipPath->getNumChildComponents() > 0)
{
setCommonAttributes (*drawableClipPath, xmlPath);
target.setClipPath (drawableClipPath.release());
return true;
}
}
return false;
}
void addGradientStopsIn (ColourGradient& cg, const XmlPath& fillXml) const
bool addGradientStopsIn (ColourGradient& cg, const XmlPath& fillXml) const
{
bool result = false;
if (fillXml.xml != nullptr)
{
forEachXmlChildElementWithTagName (*fillXml, e, "stop")
@@ -768,8 +881,11 @@ private:
offset *= 0.01;
cg.addColour (jlimit (0.0, 1.0, offset), col);
result = true;
}
}
return result;
}
FillType getGradientFillType (const XmlPath& fillXml,
@@ -779,12 +895,12 @@ private:
ColourGradient gradient;
{
auto link = fillXml->getStringAttribute ("xlink:href");
auto linkedID = getLinkedID (fillXml);
if (link.startsWithChar ('#'))
if (linkedID.isNotEmpty())
{
SetGradientStopsOp op = { this, &gradient, };
topLevelXml.applyOperationToChildWithID (link.substring (1), op);
topLevelXml.applyOperationToChildWithID (linkedID, op);
}
}
@@ -957,16 +1073,39 @@ private:
}
//==============================================================================
Drawable* parseText (const XmlPath& xml, bool shouldParseTransform)
Drawable* useText (const XmlPath& xml) const
{
auto translation = AffineTransform::translation ((float) xml->getDoubleAttribute ("x", 0.0),
(float) xml->getDoubleAttribute ("y", 0.0));
UseTextOp op = { this, &translation, nullptr };
auto linkedID = getLinkedID (xml);
if (linkedID.isNotEmpty())
topLevelXml.applyOperationToChildWithID (linkedID, op);
return op.target;
}
Drawable* parseText (const XmlPath& xml, bool shouldParseTransform,
AffineTransform* additonalTransform = nullptr) const
{
if (shouldParseTransform && xml->hasAttribute ("transform"))
{
SVGState newState (*this);
newState.addTransform (xml);
return newState.parseText (xml, false);
return newState.parseText (xml, false, additonalTransform);
}
if (xml->hasTagName ("use"))
return useText (xml);
if (! xml->hasTagName ("text"))
return nullptr;
Array<float> xCoords, yCoords, dxCoords, dyCoords;
getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true);
@@ -975,7 +1114,7 @@ private:
getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false);
auto font = getFont (xml);
auto anchorStr = getStyleAttribute(xml, "text-anchor");
auto anchorStr = getStyleAttribute (xml, "text-anchor");
auto dc = new DrawableComposite();
setCommonAttributes (*dc, xml);
@@ -991,7 +1130,11 @@ private:
dt->setText (text);
dt->setFont (font, true);
dt->setTransform (transform);
if (additonalTransform != nullptr)
dt->setTransform (transform.followedBy (*additonalTransform));
else
dt->setTransform (transform);
dt->setColour (parseColour (xml, "fill", Colours::black)
.withMultipliedAlpha (getStyleAttribute (xml, "fill-opacity", "1").getFloatValue()));
@@ -1030,6 +1173,95 @@ private:
return f.withPointHeight (getCoordLength (getStyleAttribute (xml, "font-size"), 1.0f));
}
//==============================================================================
Drawable* useImage (const XmlPath& xml) const
{
auto translation = AffineTransform::translation ((float) xml->getDoubleAttribute ("x", 0.0),
(float) xml->getDoubleAttribute ("y", 0.0));
UseImageOp op = { this, &translation, nullptr };
auto linkedID = getLinkedID (xml);
if (linkedID.isNotEmpty())
topLevelXml.applyOperationToChildWithID (linkedID, op);
return op.target;
}
Drawable* parseImage (const XmlPath& xml, bool shouldParseTransform,
AffineTransform* additionalTransform = nullptr) const
{
if (shouldParseTransform && xml->hasAttribute ("transform"))
{
SVGState newState (*this);
newState.addTransform (xml);
return newState.parseImage (xml, false, additionalTransform);
}
if (xml->hasTagName ("use"))
return useImage (xml);
if (! xml->hasTagName ("image"))
return nullptr;
auto link = xml->getStringAttribute ("xlink:href");
ScopedPointer<InputStream> inputStream;
MemoryOutputStream imageStream;
if (link.startsWith ("data:"))
{
const auto indexOfComma = link.indexOf (",");
auto format = link.substring (5, indexOfComma).trim();
const auto indexOfSemi = format.indexOf (";");
if (format.substring (indexOfSemi + 1).trim().equalsIgnoreCase ("base64"))
{
auto mime = format.substring (0, indexOfSemi).trim();
if (mime.equalsIgnoreCase ("image/png") || mime.equalsIgnoreCase ("image/jpeg"))
{
const String base64text = link.substring (indexOfComma + 1).removeCharacters ("\t\n\r ");
if (Base64::convertFromBase64 (imageStream, base64text))
inputStream = new MemoryInputStream (imageStream.getData(), imageStream.getDataSize(), false);
}
}
}
else
{
auto linkedFile = originalFile.getParentDirectory().getChildFile (link);
if (linkedFile.existsAsFile())
inputStream = linkedFile.createInputStream();
}
if (inputStream != nullptr)
{
auto image = ImageFileFormat::loadFrom (*inputStream);
if (image.isValid())
{
auto* di = new DrawableImage();
setCommonAttributes (*di, xml);
di->setImage (image);
if (additionalTransform != nullptr)
di->setTransform (transform.followedBy (*additionalTransform));
else
di->setTransform (transform);
return di;
}
}
return nullptr;
}
//==============================================================================
void addTransform (const XmlPath& xml)
{
@@ -1514,6 +1746,25 @@ Drawable* Drawable::createFromSVG (const XmlElement& svgDocument)
return state.parseSVGElement (SVGState::XmlPath (&svgDocument, nullptr));
}
Drawable* Drawable::createFromSVGFile (const File& svgFile)
{
XmlDocument doc (svgFile);
ScopedPointer<XmlElement> outer (doc.getDocumentElement (true));
if (outer != nullptr && outer->hasTagName ("svg"))
{
ScopedPointer<XmlElement> svgDocument (doc.getDocumentElement());
if (svgDocument != nullptr)
{
SVGState state (svgDocument, svgFile);
return state.parseSVGElement (SVGState::XmlPath (svgDocument, nullptr));
}
}
return nullptr;
}
Path Drawable::parseSVGPath (const String& svgPath)
{
SVGState state (nullptr);


Loading…
Cancel
Save