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.

371 lines
14KB

  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. {
  28. }
  29. DrawableShape::DrawableShape (const DrawableShape& other)
  30. : strokeType (other.strokeType),
  31. mainFill (other.mainFill),
  32. strokeFill (other.strokeFill)
  33. {
  34. }
  35. DrawableShape::~DrawableShape()
  36. {
  37. }
  38. void DrawableShape::setFill (const FillType& newFill)
  39. {
  40. mainFill = newFill;
  41. }
  42. void DrawableShape::setStrokeFill (const FillType& newFill)
  43. {
  44. strokeFill = newFill;
  45. }
  46. void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
  47. {
  48. strokeType = newStrokeType;
  49. strokeChanged();
  50. }
  51. void DrawableShape::setStrokeThickness (const float newThickness)
  52. {
  53. setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle()));
  54. }
  55. bool DrawableShape::isStrokeVisible() const throw()
  56. {
  57. return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible();
  58. }
  59. bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState,
  60. Expression::EvaluationContext* /*nameFinder*/,
  61. ComponentBuilder::ImageProvider* imageProvider)
  62. {
  63. bool hasChanged = false;
  64. {
  65. const FillType f (newState.getMainFill (getParent(), imageProvider));
  66. if (mainFill != f)
  67. {
  68. hasChanged = true;
  69. mainFill = f;
  70. }
  71. }
  72. {
  73. const FillType f (newState.getStrokeFill (getParent(), imageProvider));
  74. if (strokeFill != f)
  75. {
  76. hasChanged = true;
  77. strokeFill = f;
  78. }
  79. }
  80. return hasChanged;
  81. }
  82. void DrawableShape::writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const
  83. {
  84. state.setMainFill (mainFill, 0, 0, 0, imageProvider, undoManager);
  85. state.setStrokeFill (strokeFill, 0, 0, 0, imageProvider, undoManager);
  86. state.setStrokeType (strokeType, undoManager);
  87. }
  88. //==============================================================================
  89. void DrawableShape::paint (Graphics& g)
  90. {
  91. transformContextToCorrectOrigin (g);
  92. g.setFillType (mainFill);
  93. g.fillPath (path);
  94. if (isStrokeVisible())
  95. {
  96. g.setFillType (strokeFill);
  97. g.fillPath (strokePath);
  98. }
  99. }
  100. void DrawableShape::pathChanged()
  101. {
  102. rebuildPath (path);
  103. strokeChanged();
  104. }
  105. void DrawableShape::strokeChanged()
  106. {
  107. strokePath.clear();
  108. strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f);
  109. setBoundsToEnclose (getDrawableBounds());
  110. repaint();
  111. }
  112. const Rectangle<float> DrawableShape::getDrawableBounds() const
  113. {
  114. if (isStrokeVisible())
  115. return strokePath.getBounds();
  116. else
  117. return path.getBounds();
  118. }
  119. bool DrawableShape::hitTest (int x, int y) const
  120. {
  121. const float globalX = (float) (x - originRelativeToComponent.getX());
  122. const float globalY = (float) (y - originRelativeToComponent.getY());
  123. return path.contains (globalX, globalY)
  124. || (isStrokeVisible() && strokePath.contains (globalX, globalY));
  125. }
  126. //==============================================================================
  127. const Identifier DrawableShape::FillAndStrokeState::type ("type");
  128. const Identifier DrawableShape::FillAndStrokeState::colour ("colour");
  129. const Identifier DrawableShape::FillAndStrokeState::colours ("colours");
  130. const Identifier DrawableShape::FillAndStrokeState::fill ("Fill");
  131. const Identifier DrawableShape::FillAndStrokeState::stroke ("Stroke");
  132. const Identifier DrawableShape::FillAndStrokeState::path ("Path");
  133. const Identifier DrawableShape::FillAndStrokeState::jointStyle ("jointStyle");
  134. const Identifier DrawableShape::FillAndStrokeState::capStyle ("capStyle");
  135. const Identifier DrawableShape::FillAndStrokeState::strokeWidth ("strokeWidth");
  136. const Identifier DrawableShape::FillAndStrokeState::gradientPoint1 ("point1");
  137. const Identifier DrawableShape::FillAndStrokeState::gradientPoint2 ("point2");
  138. const Identifier DrawableShape::FillAndStrokeState::gradientPoint3 ("point3");
  139. const Identifier DrawableShape::FillAndStrokeState::radial ("radial");
  140. const Identifier DrawableShape::FillAndStrokeState::imageId ("imageId");
  141. const Identifier DrawableShape::FillAndStrokeState::imageOpacity ("imageOpacity");
  142. DrawableShape::FillAndStrokeState::FillAndStrokeState (const ValueTree& state_)
  143. : Drawable::ValueTreeWrapperBase (state_)
  144. {
  145. }
  146. const FillType DrawableShape::FillAndStrokeState::getMainFill (Expression::EvaluationContext* nameFinder,
  147. ComponentBuilder::ImageProvider* imageProvider) const
  148. {
  149. return readFillType (state.getChildWithName (fill), 0, 0, 0, nameFinder, imageProvider);
  150. }
  151. ValueTree DrawableShape::FillAndStrokeState::getMainFillState()
  152. {
  153. ValueTree v (state.getChildWithName (fill));
  154. if (v.isValid())
  155. return v;
  156. setMainFill (Colours::black, 0, 0, 0, 0, 0);
  157. return getMainFillState();
  158. }
  159. void DrawableShape::FillAndStrokeState::setMainFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2,
  160. const RelativePoint* gp3, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager)
  161. {
  162. ValueTree v (state.getOrCreateChildWithName (fill, undoManager));
  163. writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
  164. }
  165. const FillType DrawableShape::FillAndStrokeState::getStrokeFill (Expression::EvaluationContext* nameFinder,
  166. ComponentBuilder::ImageProvider* imageProvider) const
  167. {
  168. return readFillType (state.getChildWithName (stroke), 0, 0, 0, nameFinder, imageProvider);
  169. }
  170. ValueTree DrawableShape::FillAndStrokeState::getStrokeFillState()
  171. {
  172. ValueTree v (state.getChildWithName (stroke));
  173. if (v.isValid())
  174. return v;
  175. setStrokeFill (Colours::black, 0, 0, 0, 0, 0);
  176. return getStrokeFillState();
  177. }
  178. void DrawableShape::FillAndStrokeState::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2,
  179. const RelativePoint* gp3, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager)
  180. {
  181. ValueTree v (state.getOrCreateChildWithName (stroke, undoManager));
  182. writeFillType (v, newFill, gp1, gp2, gp3, imageProvider, undoManager);
  183. }
  184. const PathStrokeType DrawableShape::FillAndStrokeState::getStrokeType() const
  185. {
  186. const String jointStyleString (state [jointStyle].toString());
  187. const String capStyleString (state [capStyle].toString());
  188. return PathStrokeType (state [strokeWidth],
  189. jointStyleString == "curved" ? PathStrokeType::curved
  190. : (jointStyleString == "bevel" ? PathStrokeType::beveled
  191. : PathStrokeType::mitered),
  192. capStyleString == "square" ? PathStrokeType::square
  193. : (capStyleString == "round" ? PathStrokeType::rounded
  194. : PathStrokeType::butt));
  195. }
  196. void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager)
  197. {
  198. state.setProperty (strokeWidth, (double) newStrokeType.getStrokeThickness(), undoManager);
  199. state.setProperty (jointStyle, newStrokeType.getJointStyle() == PathStrokeType::mitered
  200. ? "miter" : (newStrokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), undoManager);
  201. state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt
  202. ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
  203. }
  204. const FillType DrawableShape::FillAndStrokeState::readFillType (const ValueTree& v, RelativePoint* const gp1, RelativePoint* const gp2, RelativePoint* const gp3,
  205. Expression::EvaluationContext* const nameFinder, ComponentBuilder::ImageProvider* imageProvider)
  206. {
  207. const String newType (v[type].toString());
  208. if (newType == "solid")
  209. {
  210. const String colourString (v [colour].toString());
  211. return FillType (Colour (colourString.isEmpty() ? (uint32) 0xff000000
  212. : (uint32) colourString.getHexValue32()));
  213. }
  214. else if (newType == "gradient")
  215. {
  216. RelativePoint p1 (v [gradientPoint1]), p2 (v [gradientPoint2]), p3 (v [gradientPoint3]);
  217. ColourGradient g;
  218. if (gp1 != 0) *gp1 = p1;
  219. if (gp2 != 0) *gp2 = p2;
  220. if (gp3 != 0) *gp3 = p3;
  221. g.point1 = p1.resolve (nameFinder);
  222. g.point2 = p2.resolve (nameFinder);
  223. g.isRadial = v[radial];
  224. StringArray colourSteps;
  225. colourSteps.addTokens (v[colours].toString(), false);
  226. for (int i = 0; i < colourSteps.size() / 2; ++i)
  227. g.addColour (colourSteps[i * 2].getDoubleValue(),
  228. Colour ((uint32) colourSteps[i * 2 + 1].getHexValue32()));
  229. FillType fillType (g);
  230. if (g.isRadial)
  231. {
  232. const Point<float> point3 (p3.resolve (nameFinder));
  233. const Point<float> point3Source (g.point1.getX() + g.point2.getY() - g.point1.getY(),
  234. g.point1.getY() + g.point1.getX() - g.point2.getX());
  235. fillType.transform = AffineTransform::fromTargetPoints (g.point1.getX(), g.point1.getY(), g.point1.getX(), g.point1.getY(),
  236. g.point2.getX(), g.point2.getY(), g.point2.getX(), g.point2.getY(),
  237. point3Source.getX(), point3Source.getY(), point3.getX(), point3.getY());
  238. }
  239. return fillType;
  240. }
  241. else if (newType == "image")
  242. {
  243. Image im;
  244. if (imageProvider != 0)
  245. im = imageProvider->getImageForIdentifier (v[imageId]);
  246. FillType f (im, AffineTransform::identity);
  247. f.setOpacity ((float) v.getProperty (imageOpacity, 1.0f));
  248. return f;
  249. }
  250. jassert (! v.isValid());
  251. return FillType();
  252. }
  253. namespace DrawableShapeHelpers
  254. {
  255. const Point<float> calcThirdGradientPoint (const FillType& fillType)
  256. {
  257. const ColourGradient& g = *fillType.gradient;
  258. const Point<float> point3Source (g.point1.getX() + g.point2.getY() - g.point1.getY(),
  259. g.point1.getY() + g.point1.getX() - g.point2.getX());
  260. return point3Source.transformedBy (fillType.transform);
  261. }
  262. }
  263. void DrawableShape::FillAndStrokeState::writeFillType (ValueTree& v, const FillType& fillType,
  264. const RelativePoint* const gp1, const RelativePoint* const gp2, const RelativePoint* gp3,
  265. ComponentBuilder::ImageProvider* imageProvider, UndoManager* const undoManager)
  266. {
  267. if (fillType.isColour())
  268. {
  269. v.setProperty (type, "solid", undoManager);
  270. v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager);
  271. }
  272. else if (fillType.isGradient())
  273. {
  274. v.setProperty (type, "gradient", undoManager);
  275. v.setProperty (gradientPoint1, gp1 != 0 ? gp1->toString() : fillType.gradient->point1.toString(), undoManager);
  276. v.setProperty (gradientPoint2, gp2 != 0 ? gp2->toString() : fillType.gradient->point2.toString(), undoManager);
  277. v.setProperty (gradientPoint3, gp3 != 0 ? gp3->toString() : DrawableShapeHelpers::calcThirdGradientPoint (fillType).toString(), undoManager);
  278. v.setProperty (radial, fillType.gradient->isRadial, undoManager);
  279. String s;
  280. for (int i = 0; i < fillType.gradient->getNumColours(); ++i)
  281. s << ' ' << fillType.gradient->getColourPosition (i)
  282. << ' ' << String::toHexString ((int) fillType.gradient->getColour(i).getARGB());
  283. v.setProperty (colours, s.trimStart(), undoManager);
  284. }
  285. else if (fillType.isTiledImage())
  286. {
  287. v.setProperty (type, "image", undoManager);
  288. if (imageProvider != 0)
  289. v.setProperty (imageId, imageProvider->getIdentifierForImage (fillType.image), undoManager);
  290. if (fillType.getOpacity() < 1.0f)
  291. v.setProperty (imageOpacity, fillType.getOpacity(), undoManager);
  292. else
  293. v.removeProperty (imageOpacity, undoManager);
  294. }
  295. else
  296. {
  297. jassertfalse;
  298. }
  299. }
  300. END_JUCE_NAMESPACE