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.

293 lines
9.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. DrawableImage::DrawableImage()
  20. : opacity (1.0f),
  21. overlayColour (0x00000000)
  22. {
  23. bounds.topRight = RelativePoint (Point<float> (1.0f, 0.0f));
  24. bounds.bottomLeft = RelativePoint (Point<float> (0.0f, 1.0f));
  25. }
  26. DrawableImage::DrawableImage (const DrawableImage& other)
  27. : Drawable (other),
  28. image (other.image),
  29. opacity (other.opacity),
  30. overlayColour (other.overlayColour),
  31. bounds (other.bounds)
  32. {
  33. setBounds (other.getBounds());
  34. }
  35. DrawableImage::~DrawableImage()
  36. {
  37. }
  38. //==============================================================================
  39. void DrawableImage::setImage (const Image& imageToUse)
  40. {
  41. image = imageToUse;
  42. setBounds (imageToUse.getBounds());
  43. bounds.topLeft = RelativePoint (Point<float> (0.0f, 0.0f));
  44. bounds.topRight = RelativePoint (Point<float> ((float) image.getWidth(), 0.0f));
  45. bounds.bottomLeft = RelativePoint (Point<float> (0.0f, (float) image.getHeight()));
  46. recalculateCoordinates (nullptr);
  47. repaint();
  48. }
  49. void DrawableImage::setOpacity (const float newOpacity)
  50. {
  51. opacity = newOpacity;
  52. }
  53. void DrawableImage::setOverlayColour (Colour newOverlayColour)
  54. {
  55. overlayColour = newOverlayColour;
  56. }
  57. void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds)
  58. {
  59. if (bounds != newBounds)
  60. {
  61. bounds = newBounds;
  62. if (bounds.isDynamic())
  63. {
  64. Drawable::Positioner<DrawableImage>* const p = new Drawable::Positioner<DrawableImage> (*this);
  65. setPositioner (p);
  66. p->apply();
  67. }
  68. else
  69. {
  70. setPositioner (nullptr);
  71. recalculateCoordinates (nullptr);
  72. }
  73. }
  74. }
  75. //==============================================================================
  76. bool DrawableImage::registerCoordinates (RelativeCoordinatePositionerBase& pos)
  77. {
  78. bool ok = pos.addPoint (bounds.topLeft);
  79. ok = pos.addPoint (bounds.topRight) && ok;
  80. return pos.addPoint (bounds.bottomLeft) && ok;
  81. }
  82. void DrawableImage::recalculateCoordinates (Expression::Scope* scope)
  83. {
  84. if (image.isValid())
  85. {
  86. Point<float> resolved[3];
  87. bounds.resolveThreePoints (resolved, scope);
  88. const Point<float> tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth());
  89. const Point<float> bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight());
  90. AffineTransform t (AffineTransform::fromTargetPoints (resolved[0].x, resolved[0].y,
  91. tr.x, tr.y,
  92. bl.x, bl.y));
  93. if (t.isSingularity())
  94. t = AffineTransform();
  95. setTransform (t);
  96. }
  97. }
  98. //==============================================================================
  99. void DrawableImage::paint (Graphics& g)
  100. {
  101. if (image.isValid())
  102. {
  103. if (opacity > 0.0f && ! overlayColour.isOpaque())
  104. {
  105. g.setOpacity (opacity);
  106. g.drawImageAt (image, 0, 0, false);
  107. }
  108. if (! overlayColour.isTransparent())
  109. {
  110. g.setColour (overlayColour.withMultipliedAlpha (opacity));
  111. g.drawImageAt (image, 0, 0, true);
  112. }
  113. }
  114. }
  115. Rectangle<float> DrawableImage::getDrawableBounds() const
  116. {
  117. return image.getBounds().toFloat();
  118. }
  119. bool DrawableImage::hitTest (int x, int y)
  120. {
  121. return Drawable::hitTest (x, y) && image.isValid() && image.getPixelAt (x, y).getAlpha() >= 127;
  122. }
  123. Drawable* DrawableImage::createCopy() const
  124. {
  125. return new DrawableImage (*this);
  126. }
  127. //==============================================================================
  128. const Identifier DrawableImage::valueTreeType ("Image");
  129. const Identifier DrawableImage::ValueTreeWrapper::opacity ("opacity");
  130. const Identifier DrawableImage::ValueTreeWrapper::overlay ("overlay");
  131. const Identifier DrawableImage::ValueTreeWrapper::image ("image");
  132. const Identifier DrawableImage::ValueTreeWrapper::topLeft ("topLeft");
  133. const Identifier DrawableImage::ValueTreeWrapper::topRight ("topRight");
  134. const Identifier DrawableImage::ValueTreeWrapper::bottomLeft ("bottomLeft");
  135. //==============================================================================
  136. DrawableImage::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
  137. : ValueTreeWrapperBase (state_)
  138. {
  139. jassert (state.hasType (valueTreeType));
  140. }
  141. var DrawableImage::ValueTreeWrapper::getImageIdentifier() const
  142. {
  143. return state [image];
  144. }
  145. Value DrawableImage::ValueTreeWrapper::getImageIdentifierValue (UndoManager* undoManager)
  146. {
  147. return state.getPropertyAsValue (image, undoManager);
  148. }
  149. void DrawableImage::ValueTreeWrapper::setImageIdentifier (const var& newIdentifier, UndoManager* undoManager)
  150. {
  151. state.setProperty (image, newIdentifier, undoManager);
  152. }
  153. float DrawableImage::ValueTreeWrapper::getOpacity() const
  154. {
  155. return (float) state.getProperty (opacity, 1.0);
  156. }
  157. Value DrawableImage::ValueTreeWrapper::getOpacityValue (UndoManager* undoManager)
  158. {
  159. if (! state.hasProperty (opacity))
  160. state.setProperty (opacity, 1.0, undoManager);
  161. return state.getPropertyAsValue (opacity, undoManager);
  162. }
  163. void DrawableImage::ValueTreeWrapper::setOpacity (float newOpacity, UndoManager* undoManager)
  164. {
  165. state.setProperty (opacity, newOpacity, undoManager);
  166. }
  167. Colour DrawableImage::ValueTreeWrapper::getOverlayColour() const
  168. {
  169. return Colour::fromString (state [overlay].toString());
  170. }
  171. void DrawableImage::ValueTreeWrapper::setOverlayColour (Colour newColour, UndoManager* undoManager)
  172. {
  173. if (newColour.isTransparent())
  174. state.removeProperty (overlay, undoManager);
  175. else
  176. state.setProperty (overlay, String::toHexString ((int) newColour.getARGB()), undoManager);
  177. }
  178. Value DrawableImage::ValueTreeWrapper::getOverlayColourValue (UndoManager* undoManager)
  179. {
  180. return state.getPropertyAsValue (overlay, undoManager);
  181. }
  182. RelativeParallelogram DrawableImage::ValueTreeWrapper::getBoundingBox() const
  183. {
  184. return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
  185. state.getProperty (topRight, "100, 0"),
  186. state.getProperty (bottomLeft, "0, 100"));
  187. }
  188. void DrawableImage::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
  189. {
  190. state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
  191. state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
  192. state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
  193. }
  194. //==============================================================================
  195. void DrawableImage::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
  196. {
  197. const ValueTreeWrapper controller (tree);
  198. setComponentID (controller.getID());
  199. const float newOpacity = controller.getOpacity();
  200. const Colour newOverlayColour (controller.getOverlayColour());
  201. Image newImage;
  202. const var imageIdentifier (controller.getImageIdentifier());
  203. jassert (builder.getImageProvider() != 0 || imageIdentifier.isVoid()); // if you're using images, you need to provide something that can load and save them!
  204. if (builder.getImageProvider() != nullptr)
  205. newImage = builder.getImageProvider()->getImageForIdentifier (imageIdentifier);
  206. const RelativeParallelogram newBounds (controller.getBoundingBox());
  207. if (bounds != newBounds || newOpacity != opacity
  208. || overlayColour != newOverlayColour || image != newImage)
  209. {
  210. repaint();
  211. opacity = newOpacity;
  212. overlayColour = newOverlayColour;
  213. if (image != newImage)
  214. setImage (newImage);
  215. setBoundingBox (newBounds);
  216. }
  217. }
  218. ValueTree DrawableImage::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
  219. {
  220. ValueTree tree (valueTreeType);
  221. ValueTreeWrapper v (tree);
  222. v.setID (getComponentID());
  223. v.setOpacity (opacity, nullptr);
  224. v.setOverlayColour (overlayColour, nullptr);
  225. v.setBoundingBox (bounds, nullptr);
  226. if (image.isValid())
  227. {
  228. jassert (imageProvider != nullptr); // if you're using images, you need to provide something that can load and save them!
  229. if (imageProvider != nullptr)
  230. v.setImageIdentifier (imageProvider->getIdentifierForImage (image), nullptr);
  231. }
  232. return tree;
  233. }