The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

406 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_DrawableShape.h"
  21. #include "juce_DrawableComposite.h"
  22. //==============================================================================
  23. DrawableShape::DrawableShape()
  24. : strokeType (0.0f),
  25. mainFill (Colours::black),
  26. strokeFill (Colours::black),
  27. pathNeedsUpdating (true),
  28. strokeNeedsUpdating (true)
  29. {
  30. }
  31. DrawableShape::DrawableShape (const DrawableShape& other)
  32. : strokeType (other.strokeType),
  33. mainFill (other.mainFill),
  34. strokeFill (other.strokeFill),
  35. pathNeedsUpdating (true),
  36. strokeNeedsUpdating (true)
  37. {
  38. }
  39. DrawableShape::~DrawableShape()
  40. {
  41. }
  42. void DrawableShape::setFill (const FillType& newFill)
  43. {
  44. mainFill = newFill;
  45. }
  46. void DrawableShape::setStrokeFill (const FillType& newFill)
  47. {
  48. strokeFill = newFill;
  49. }
  50. void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
  51. {
  52. strokeType = newStrokeType;
  53. strokeNeedsUpdating = true;
  54. }
  55. void DrawableShape::setStrokeThickness (const float newThickness)
  56. {
  57. setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle()));
  58. }
  59. bool DrawableShape::isStrokeVisible() const throw()
  60. {
  61. return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible();
  62. }
  63. void DrawableShape::setBrush (const Drawable::RenderingContext& context, const FillType& type)
  64. {
  65. FillType f (type);
  66. if (f.isGradient())
  67. f.gradient->multiplyOpacity (context.opacity);
  68. else
  69. f.setOpacity (f.getOpacity() * context.opacity);
  70. f.transform = f.transform.followedBy (context.transform);
  71. context.g.setFillType (f);
  72. }
  73. bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState,
  74. Expression::EvaluationContext* /*nameFinder*/,
  75. ImageProvider* imageProvider)
  76. {
  77. bool hasChanged = false;
  78. {
  79. const FillType f (newState.getMainFill (parent, imageProvider));
  80. if (mainFill != f)
  81. {
  82. hasChanged = true;
  83. mainFill = f;
  84. }
  85. }
  86. {
  87. const FillType f (newState.getStrokeFill (parent, imageProvider));
  88. if (strokeFill != f)
  89. {
  90. hasChanged = true;
  91. strokeFill = f;
  92. }
  93. }
  94. return hasChanged;
  95. }
  96. void DrawableShape::writeTo (FillAndStrokeState& state, ImageProvider* imageProvider, UndoManager* undoManager) const
  97. {
  98. state.setMainFill (mainFill, 0, 0, 0, imageProvider, undoManager);
  99. state.setStrokeFill (strokeFill, 0, 0, 0, imageProvider, undoManager);
  100. state.setStrokeType (strokeType, undoManager);
  101. }
  102. //==============================================================================
  103. void DrawableShape::render (const Drawable::RenderingContext& context) const
  104. {
  105. setBrush (context, mainFill);
  106. context.g.fillPath (getCachedPath(), context.transform);
  107. if (isStrokeVisible())
  108. {
  109. setBrush (context, strokeFill);
  110. context.g.fillPath (getCachedStrokePath(), context.transform);
  111. }
  112. }
  113. void DrawableShape::pathChanged()
  114. {
  115. pathNeedsUpdating = true;
  116. }
  117. void DrawableShape::strokeChanged()
  118. {
  119. strokeNeedsUpdating = true;
  120. }
  121. void DrawableShape::invalidatePoints()
  122. {
  123. pathNeedsUpdating = true;
  124. strokeNeedsUpdating = true;
  125. }
  126. const Path& DrawableShape::getCachedPath() const
  127. {
  128. if (pathNeedsUpdating)
  129. {
  130. pathNeedsUpdating = false;
  131. if (rebuildPath (cachedPath))
  132. strokeNeedsUpdating = true;
  133. }
  134. return cachedPath;
  135. }
  136. const Path& DrawableShape::getCachedStrokePath() const
  137. {
  138. if (strokeNeedsUpdating)
  139. {
  140. cachedStroke.clear();
  141. strokeType.createStrokedPath (cachedStroke, getCachedPath(), AffineTransform::identity, 4.0f);
  142. strokeNeedsUpdating = false; // (must be called after getCachedPath)
  143. }
  144. return cachedStroke;
  145. }
  146. const Rectangle<float> DrawableShape::getBounds() const
  147. {
  148. if (isStrokeVisible())
  149. return getCachedStrokePath().getBounds();
  150. else
  151. return getCachedPath().getBounds();
  152. }
  153. bool DrawableShape::hitTest (float x, float y) const
  154. {
  155. return getCachedPath().contains (x, y)
  156. || (isStrokeVisible() && getCachedStrokePath().contains (x, y));
  157. }
  158. //==============================================================================
  159. const Identifier DrawableShape::FillAndStrokeState::type ("type");
  160. const Identifier DrawableShape::FillAndStrokeState::colour ("colour");
  161. const Identifier DrawableShape::FillAndStrokeState::colours ("colours");
  162. const Identifier DrawableShape::FillAndStrokeState::fill ("Fill");
  163. const Identifier DrawableShape::FillAndStrokeState::stroke ("Stroke");
  164. const Identifier DrawableShape::FillAndStrokeState::path ("Path");
  165. const Identifier DrawableShape::FillAndStrokeState::jointStyle ("jointStyle");
  166. const Identifier DrawableShape::FillAndStrokeState::capStyle ("capStyle");
  167. const Identifier DrawableShape::FillAndStrokeState::strokeWidth ("strokeWidth");
  168. const Identifier DrawableShape::FillAndStrokeState::gradientPoint1 ("point1");
  169. const Identifier DrawableShape::FillAndStrokeState::gradientPoint2 ("point2");
  170. const Identifier DrawableShape::FillAndStrokeState::gradientPoint3 ("point3");
  171. const Identifier DrawableShape::FillAndStrokeState::radial ("radial");
  172. const Identifier DrawableShape::FillAndStrokeState::imageId ("imageId");
  173. const Identifier DrawableShape::FillAndStrokeState::imageOpacity ("imageOpacity");
  174. DrawableShape::FillAndStrokeState::FillAndStrokeState (const ValueTree& state_)
  175. : Drawable::ValueTreeWrapperBase (state_)
  176. {
  177. }
  178. const FillType DrawableShape::FillAndStrokeState::getMainFill (Expression::EvaluationContext* nameFinder,
  179. ImageProvider* imageProvider) const
  180. {
  181. return readFillType (state.getChildWithName (fill), 0, 0, 0, nameFinder, imageProvider);
  182. }
  183. ValueTree DrawableShape::FillAndStrokeState::getMainFillState()
  184. {
  185. ValueTree v (state.getChildWithName (fill));
  186. if (v.isValid())
  187. return v;
  188. setMainFill (Colours::black, 0, 0, 0, 0, 0);
  189. return getMainFillState();
  190. }
  191. void DrawableShape::FillAndStrokeState::setMainFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2,
  192. const RelativePoint* gp3, ImageProvider* imageProvider, UndoManager* undoManager)
  193. {
  194. ValueTree v (state.getOrCreateChildWithName (fill, undoManager));
  195. writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
  196. }
  197. const FillType DrawableShape::FillAndStrokeState::getStrokeFill (Expression::EvaluationContext* nameFinder,
  198. ImageProvider* imageProvider) const
  199. {
  200. return readFillType (state.getChildWithName (stroke), 0, 0, 0, nameFinder, imageProvider);
  201. }
  202. ValueTree DrawableShape::FillAndStrokeState::getStrokeFillState()
  203. {
  204. ValueTree v (state.getChildWithName (stroke));
  205. if (v.isValid())
  206. return v;
  207. setStrokeFill (Colours::black, 0, 0, 0, 0, 0);
  208. return getStrokeFillState();
  209. }
  210. void DrawableShape::FillAndStrokeState::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2,
  211. const RelativePoint* gp3, ImageProvider* imageProvider, UndoManager* undoManager)
  212. {
  213. ValueTree v (state.getOrCreateChildWithName (stroke, undoManager));
  214. writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
  215. }
  216. const PathStrokeType DrawableShape::FillAndStrokeState::getStrokeType() const
  217. {
  218. const String jointStyleString (state [jointStyle].toString());
  219. const String capStyleString (state [capStyle].toString());
  220. return PathStrokeType (state [strokeWidth],
  221. jointStyleString == "curved" ? PathStrokeType::curved
  222. : (jointStyleString == "bevel" ? PathStrokeType::beveled
  223. : PathStrokeType::mitered),
  224. capStyleString == "square" ? PathStrokeType::square
  225. : (capStyleString == "round" ? PathStrokeType::rounded
  226. : PathStrokeType::butt));
  227. }
  228. void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager)
  229. {
  230. state.setProperty (strokeWidth, (double) newStrokeType.getStrokeThickness(), undoManager);
  231. state.setProperty (jointStyle, newStrokeType.getJointStyle() == PathStrokeType::mitered
  232. ? "miter" : (newStrokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), undoManager);
  233. state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt
  234. ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
  235. }
  236. const FillType DrawableShape::FillAndStrokeState::readFillType (const ValueTree& v, RelativePoint* const gp1, RelativePoint* const gp2, RelativePoint* const gp3,
  237. Expression::EvaluationContext* const nameFinder, ImageProvider* imageProvider)
  238. {
  239. const String newType (v[type].toString());
  240. if (newType == "solid")
  241. {
  242. const String colourString (v [colour].toString());
  243. return FillType (Colour (colourString.isEmpty() ? (uint32) 0xff000000
  244. : (uint32) colourString.getHexValue32()));
  245. }
  246. else if (newType == "gradient")
  247. {
  248. RelativePoint p1 (v [gradientPoint1]), p2 (v [gradientPoint2]), p3 (v [gradientPoint3]);
  249. ColourGradient g;
  250. if (gp1 != 0) *gp1 = p1;
  251. if (gp2 != 0) *gp2 = p2;
  252. if (gp3 != 0) *gp3 = p3;
  253. g.point1 = p1.resolve (nameFinder);
  254. g.point2 = p2.resolve (nameFinder);
  255. g.isRadial = v[radial];
  256. StringArray colourSteps;
  257. colourSteps.addTokens (v[colours].toString(), false);
  258. for (int i = 0; i < colourSteps.size() / 2; ++i)
  259. g.addColour (colourSteps[i * 2].getDoubleValue(),
  260. Colour ((uint32) colourSteps[i * 2 + 1].getHexValue32()));
  261. FillType fillType (g);
  262. if (g.isRadial)
  263. {
  264. const Point<float> point3 (p3.resolve (nameFinder));
  265. const Point<float> point3Source (g.point1.getX() + g.point2.getY() - g.point1.getY(),
  266. g.point1.getY() + g.point1.getX() - g.point2.getX());
  267. fillType.transform = AffineTransform::fromTargetPoints (g.point1.getX(), g.point1.getY(), g.point1.getX(), g.point1.getY(),
  268. g.point2.getX(), g.point2.getY(), g.point2.getX(), g.point2.getY(),
  269. point3Source.getX(), point3Source.getY(), point3.getX(), point3.getY());
  270. }
  271. return fillType;
  272. }
  273. else if (newType == "image")
  274. {
  275. Image im;
  276. if (imageProvider != 0)
  277. im = imageProvider->getImageForIdentifier (v[imageId]);
  278. FillType f (im, AffineTransform::identity);
  279. f.setOpacity ((float) v.getProperty (imageOpacity, 1.0f));
  280. return f;
  281. }
  282. jassert (! v.isValid());
  283. return FillType();
  284. }
  285. static const Point<float> calcThirdGradientPoint (const FillType& fillType)
  286. {
  287. const ColourGradient& g = *fillType.gradient;
  288. const Point<float> point3Source (g.point1.getX() + g.point2.getY() - g.point1.getY(),
  289. g.point1.getY() + g.point1.getX() - g.point2.getX());
  290. return point3Source.transformedBy (fillType.transform);
  291. }
  292. void DrawableShape::FillAndStrokeState::writeFillType (ValueTree& v, const FillType& fillType,
  293. const RelativePoint* const gp1, const RelativePoint* const gp2, const RelativePoint* gp3,
  294. ImageProvider* imageProvider, UndoManager* const undoManager)
  295. {
  296. if (fillType.isColour())
  297. {
  298. v.setProperty (type, "solid", undoManager);
  299. v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager);
  300. }
  301. else if (fillType.isGradient())
  302. {
  303. v.setProperty (type, "gradient", undoManager);
  304. v.setProperty (gradientPoint1, gp1 != 0 ? gp1->toString() : fillType.gradient->point1.toString(), undoManager);
  305. v.setProperty (gradientPoint2, gp2 != 0 ? gp2->toString() : fillType.gradient->point2.toString(), undoManager);
  306. v.setProperty (gradientPoint3, gp3 != 0 ? gp3->toString() : calcThirdGradientPoint (fillType).toString(), undoManager);
  307. v.setProperty (radial, fillType.gradient->isRadial, undoManager);
  308. String s;
  309. for (int i = 0; i < fillType.gradient->getNumColours(); ++i)
  310. s << ' ' << fillType.gradient->getColourPosition (i)
  311. << ' ' << String::toHexString ((int) fillType.gradient->getColour(i).getARGB());
  312. v.setProperty (colours, s.trimStart(), undoManager);
  313. }
  314. else if (fillType.isTiledImage())
  315. {
  316. v.setProperty (type, "image", undoManager);
  317. if (imageProvider != 0)
  318. v.setProperty (imageId, imageProvider->getIdentifierForImage (fillType.image), undoManager);
  319. if (fillType.getOpacity() < 1.0f)
  320. v.setProperty (imageOpacity, fillType.getOpacity(), undoManager);
  321. else
  322. v.removeProperty (imageOpacity, undoManager);
  323. }
  324. else
  325. {
  326. jassertfalse;
  327. }
  328. }
  329. END_JUCE_NAMESPACE