Audio plugin host https://kx.studio/carla
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.

863 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. struct GraphicsFontHelpers
  16. {
  17. static auto compareFont (const Font& a, const Font& b) { return Font::compare (a, b); }
  18. };
  19. static auto operator< (const Font& a, const Font& b)
  20. {
  21. return GraphicsFontHelpers::compareFont (a, b);
  22. }
  23. template <typename T>
  24. static auto operator< (const Rectangle<T>& a, const Rectangle<T>& b)
  25. {
  26. const auto tie = [] (auto& t) { return std::make_tuple (t.getX(), t.getY(), t.getWidth(), t.getHeight()); };
  27. return tie (a) < tie (b);
  28. }
  29. static auto operator< (const Justification& a, const Justification& b)
  30. {
  31. return a.getFlags() < b.getFlags();
  32. }
  33. //==============================================================================
  34. namespace
  35. {
  36. struct ConfiguredArrangement
  37. {
  38. void draw (const Graphics& g) const { arrangement.draw (g, transform); }
  39. GlyphArrangement arrangement;
  40. AffineTransform transform;
  41. };
  42. template <typename ArrangementArgs>
  43. class GlyphArrangementCache final : public DeletedAtShutdown
  44. {
  45. public:
  46. GlyphArrangementCache() = default;
  47. ~GlyphArrangementCache() override
  48. {
  49. clearSingletonInstance();
  50. }
  51. template <typename ConfigureArrangement>
  52. void draw (const Graphics& g, ArrangementArgs&& args, ConfigureArrangement&& configureArrangement)
  53. {
  54. const ScopedTryLock stl (lock);
  55. if (! stl.isLocked())
  56. {
  57. configureArrangement (args).draw (g);
  58. return;
  59. }
  60. const auto cached = [&]
  61. {
  62. const auto iter = cache.find (args);
  63. if (iter != cache.end())
  64. {
  65. if (iter->second.cachePosition != cacheOrder.begin())
  66. cacheOrder.splice (cacheOrder.begin(), cacheOrder, iter->second.cachePosition);
  67. return iter;
  68. }
  69. auto result = cache.emplace (std::move (args), CachedGlyphArrangement { configureArrangement (args), {} }).first;
  70. cacheOrder.push_front (result);
  71. return result;
  72. }();
  73. cached->second.cachePosition = cacheOrder.begin();
  74. cached->second.configured.draw (g);
  75. while (cache.size() > cacheSize)
  76. {
  77. cache.erase (cacheOrder.back());
  78. cacheOrder.pop_back();
  79. }
  80. }
  81. JUCE_DECLARE_SINGLETON (GlyphArrangementCache<ArrangementArgs>, false)
  82. private:
  83. struct CachedGlyphArrangement
  84. {
  85. using CachePtr = typename std::map<ArrangementArgs, CachedGlyphArrangement>::const_iterator;
  86. ConfiguredArrangement configured;
  87. typename std::list<CachePtr>::const_iterator cachePosition;
  88. };
  89. static constexpr size_t cacheSize = 128;
  90. std::map<ArrangementArgs, CachedGlyphArrangement> cache;
  91. std::list<typename CachedGlyphArrangement::CachePtr> cacheOrder;
  92. CriticalSection lock;
  93. };
  94. template <typename ArrangementArgs>
  95. juce::SingletonHolder<GlyphArrangementCache<ArrangementArgs>, juce::CriticalSection, false> GlyphArrangementCache<ArrangementArgs>::singletonHolder;
  96. //==============================================================================
  97. template <typename Type>
  98. Rectangle<Type> coordsToRectangle (Type x, Type y, Type w, Type h) noexcept
  99. {
  100. #if JUCE_DEBUG
  101. const int maxVal = 0x3fffffff;
  102. jassertquiet ((int) x >= -maxVal && (int) x <= maxVal
  103. && (int) y >= -maxVal && (int) y <= maxVal
  104. && (int) w >= 0 && (int) w <= maxVal
  105. && (int) h >= 0 && (int) h <= maxVal);
  106. #endif
  107. return { x, y, w, h };
  108. }
  109. }
  110. //==============================================================================
  111. Graphics::Graphics (const Image& imageToDrawOnto)
  112. : contextHolder (imageToDrawOnto.createLowLevelContext()),
  113. context (*contextHolder)
  114. {
  115. jassert (imageToDrawOnto.isValid()); // Can't draw into a null image!
  116. }
  117. Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept
  118. : context (internalContext)
  119. {
  120. }
  121. //==============================================================================
  122. void Graphics::resetToDefaultState()
  123. {
  124. saveStateIfPending();
  125. context.setFill (FillType());
  126. context.setFont (Font());
  127. context.setInterpolationQuality (Graphics::mediumResamplingQuality);
  128. }
  129. bool Graphics::isVectorDevice() const
  130. {
  131. return context.isVectorDevice();
  132. }
  133. bool Graphics::reduceClipRegion (Rectangle<int> area)
  134. {
  135. saveStateIfPending();
  136. return context.clipToRectangle (area);
  137. }
  138. bool Graphics::reduceClipRegion (int x, int y, int w, int h)
  139. {
  140. return reduceClipRegion (coordsToRectangle (x, y, w, h));
  141. }
  142. bool Graphics::reduceClipRegion (const RectangleList<int>& clipRegion)
  143. {
  144. saveStateIfPending();
  145. return context.clipToRectangleList (clipRegion);
  146. }
  147. bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform)
  148. {
  149. saveStateIfPending();
  150. context.clipToPath (path, transform);
  151. return ! context.isClipEmpty();
  152. }
  153. bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform)
  154. {
  155. saveStateIfPending();
  156. context.clipToImageAlpha (image, transform);
  157. return ! context.isClipEmpty();
  158. }
  159. void Graphics::excludeClipRegion (Rectangle<int> rectangleToExclude)
  160. {
  161. saveStateIfPending();
  162. context.excludeClipRectangle (rectangleToExclude);
  163. }
  164. bool Graphics::isClipEmpty() const
  165. {
  166. return context.isClipEmpty();
  167. }
  168. Rectangle<int> Graphics::getClipBounds() const
  169. {
  170. return context.getClipBounds();
  171. }
  172. void Graphics::saveState()
  173. {
  174. saveStateIfPending();
  175. saveStatePending = true;
  176. }
  177. void Graphics::restoreState()
  178. {
  179. if (saveStatePending)
  180. saveStatePending = false;
  181. else
  182. context.restoreState();
  183. }
  184. void Graphics::saveStateIfPending()
  185. {
  186. if (saveStatePending)
  187. {
  188. saveStatePending = false;
  189. context.saveState();
  190. }
  191. }
  192. void Graphics::setOrigin (Point<int> newOrigin)
  193. {
  194. saveStateIfPending();
  195. context.setOrigin (newOrigin);
  196. }
  197. void Graphics::setOrigin (int x, int y)
  198. {
  199. setOrigin ({ x, y });
  200. }
  201. void Graphics::addTransform (const AffineTransform& transform)
  202. {
  203. saveStateIfPending();
  204. context.addTransform (transform);
  205. }
  206. bool Graphics::clipRegionIntersects (Rectangle<int> area) const
  207. {
  208. return context.clipRegionIntersects (area);
  209. }
  210. void Graphics::beginTransparencyLayer (float layerOpacity)
  211. {
  212. saveStateIfPending();
  213. context.beginTransparencyLayer (layerOpacity);
  214. }
  215. void Graphics::endTransparencyLayer()
  216. {
  217. context.endTransparencyLayer();
  218. }
  219. //==============================================================================
  220. void Graphics::setColour (Colour newColour)
  221. {
  222. saveStateIfPending();
  223. context.setFill (newColour);
  224. }
  225. void Graphics::setOpacity (float newOpacity)
  226. {
  227. saveStateIfPending();
  228. context.setOpacity (newOpacity);
  229. }
  230. void Graphics::setGradientFill (const ColourGradient& gradient)
  231. {
  232. setFillType (gradient);
  233. }
  234. void Graphics::setGradientFill (ColourGradient&& gradient)
  235. {
  236. setFillType (std::move (gradient));
  237. }
  238. void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity)
  239. {
  240. saveStateIfPending();
  241. context.setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY)));
  242. context.setOpacity (opacity);
  243. }
  244. void Graphics::setFillType (const FillType& newFill)
  245. {
  246. saveStateIfPending();
  247. context.setFill (newFill);
  248. }
  249. //==============================================================================
  250. void Graphics::setFont (const Font& newFont)
  251. {
  252. saveStateIfPending();
  253. context.setFont (newFont);
  254. }
  255. void Graphics::setFont (const float newFontHeight)
  256. {
  257. setFont (context.getFont().withHeight (newFontHeight));
  258. }
  259. Font Graphics::getCurrentFont() const
  260. {
  261. return context.getFont();
  262. }
  263. //==============================================================================
  264. void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY,
  265. Justification justification) const
  266. {
  267. if (text.isEmpty())
  268. return;
  269. // Don't pass any vertical placement flags to this method - they'll be ignored.
  270. jassert (justification.getOnlyVerticalFlags() == 0);
  271. auto flags = justification.getOnlyHorizontalFlags();
  272. if (flags == Justification::right && startX < context.getClipBounds().getX())
  273. return;
  274. if (flags == Justification::left && startX > context.getClipBounds().getRight())
  275. return;
  276. struct ArrangementArgs
  277. {
  278. auto tie() const noexcept { return std::tie (font, text, startX, baselineY); }
  279. bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); }
  280. const Font font;
  281. const String text;
  282. const int startX, baselineY, flags;
  283. };
  284. auto configureArrangement = [] (const ArrangementArgs& args)
  285. {
  286. AffineTransform transform;
  287. GlyphArrangement arrangement;
  288. arrangement.addLineOfText (args.font, args.text, (float) args.startX, (float) args.baselineY);
  289. if (args.flags != Justification::left)
  290. {
  291. auto w = arrangement.getBoundingBox (0, -1, true).getWidth();
  292. if ((args.flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0)
  293. w /= 2.0f;
  294. transform = AffineTransform::translation (-w, 0);
  295. }
  296. return ConfiguredArrangement { std::move (arrangement), std::move (transform) };
  297. };
  298. GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
  299. { context.getFont(), text, startX, baselineY, flags },
  300. std::move (configureArrangement));
  301. }
  302. void Graphics::drawMultiLineText (const String& text, const int startX,
  303. const int baselineY, const int maximumLineWidth,
  304. Justification justification, const float leading) const
  305. {
  306. if (text.isEmpty() || startX >= context.getClipBounds().getRight())
  307. return;
  308. struct ArrangementArgs
  309. {
  310. auto tie() const noexcept { return std::tie (font, text, startX, baselineY, maximumLineWidth, justification, leading); }
  311. bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); }
  312. const Font font;
  313. const String text;
  314. const int startX, baselineY, maximumLineWidth;
  315. const Justification justification;
  316. const float leading;
  317. };
  318. auto configureArrangement = [] (const ArrangementArgs& args)
  319. {
  320. GlyphArrangement arrangement;
  321. arrangement.addJustifiedText (args.font, args.text,
  322. (float) args.startX, (float) args.baselineY, (float) args.maximumLineWidth,
  323. args.justification, args.leading);
  324. return ConfiguredArrangement { std::move (arrangement), {} };
  325. };
  326. GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
  327. { context.getFont(), text, startX, baselineY, maximumLineWidth, justification, leading },
  328. std::move (configureArrangement));
  329. }
  330. void Graphics::drawText (const String& text, Rectangle<float> area,
  331. Justification justificationType, bool useEllipsesIfTooBig) const
  332. {
  333. if (text.isEmpty() || ! context.clipRegionIntersects (area.getSmallestIntegerContainer()))
  334. return;
  335. struct ArrangementArgs
  336. {
  337. auto tie() const noexcept { return std::tie (font, text, area, justificationType, useEllipsesIfTooBig); }
  338. bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); }
  339. const Font font;
  340. const String text;
  341. const Rectangle<float> area;
  342. const Justification justificationType;
  343. const bool useEllipsesIfTooBig;
  344. };
  345. auto configureArrangement = [] (const ArrangementArgs& args)
  346. {
  347. GlyphArrangement arrangement;
  348. arrangement.addCurtailedLineOfText (args.font, args.text, 0.0f, 0.0f,
  349. args.area.getWidth(), args.useEllipsesIfTooBig);
  350. arrangement.justifyGlyphs (0, arrangement.getNumGlyphs(),
  351. args.area.getX(), args.area.getY(), args.area.getWidth(), args.area.getHeight(),
  352. args.justificationType);
  353. return ConfiguredArrangement { std::move (arrangement), {} };
  354. };
  355. GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
  356. { context.getFont(), text, area, justificationType, useEllipsesIfTooBig },
  357. std::move (configureArrangement));
  358. }
  359. void Graphics::drawText (const String& text, Rectangle<int> area,
  360. Justification justificationType, bool useEllipsesIfTooBig) const
  361. {
  362. drawText (text, area.toFloat(), justificationType, useEllipsesIfTooBig);
  363. }
  364. void Graphics::drawText (const String& text, int x, int y, int width, int height,
  365. Justification justificationType, const bool useEllipsesIfTooBig) const
  366. {
  367. drawText (text, coordsToRectangle (x, y, width, height), justificationType, useEllipsesIfTooBig);
  368. }
  369. void Graphics::drawFittedText (const String& text, Rectangle<int> area,
  370. Justification justification,
  371. const int maximumNumberOfLines,
  372. const float minimumHorizontalScale) const
  373. {
  374. if (text.isEmpty() || area.isEmpty() || ! context.clipRegionIntersects (area))
  375. return;
  376. struct ArrangementArgs
  377. {
  378. auto tie() const noexcept { return std::tie (font, text, area, justification, maximumNumberOfLines, minimumHorizontalScale); }
  379. bool operator< (const ArrangementArgs& other) const noexcept { return tie() < other.tie(); }
  380. const Font font;
  381. const String text;
  382. const Rectangle<float> area;
  383. const Justification justification;
  384. const int maximumNumberOfLines;
  385. const float minimumHorizontalScale;
  386. };
  387. auto configureArrangement = [] (const ArrangementArgs& args)
  388. {
  389. GlyphArrangement arrangement;
  390. arrangement.addFittedText (args.font, args.text,
  391. args.area.getX(), args.area.getY(),
  392. args.area.getWidth(), args.area.getHeight(),
  393. args.justification,
  394. args.maximumNumberOfLines,
  395. args.minimumHorizontalScale);
  396. return ConfiguredArrangement { std::move (arrangement), {} };
  397. };
  398. GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
  399. { context.getFont(), text, area.toFloat(), justification, maximumNumberOfLines, minimumHorizontalScale },
  400. std::move (configureArrangement));
  401. }
  402. void Graphics::drawFittedText (const String& text, int x, int y, int width, int height,
  403. Justification justification,
  404. const int maximumNumberOfLines,
  405. const float minimumHorizontalScale) const
  406. {
  407. drawFittedText (text, coordsToRectangle (x, y, width, height),
  408. justification, maximumNumberOfLines, minimumHorizontalScale);
  409. }
  410. //==============================================================================
  411. void Graphics::fillRect (Rectangle<int> r) const
  412. {
  413. context.fillRect (r, false);
  414. }
  415. void Graphics::fillRect (Rectangle<float> r) const
  416. {
  417. context.fillRect (r);
  418. }
  419. void Graphics::fillRect (int x, int y, int width, int height) const
  420. {
  421. context.fillRect (coordsToRectangle (x, y, width, height), false);
  422. }
  423. void Graphics::fillRect (float x, float y, float width, float height) const
  424. {
  425. fillRect (coordsToRectangle (x, y, width, height));
  426. }
  427. void Graphics::fillRectList (const RectangleList<float>& rectangles) const
  428. {
  429. context.fillRectList (rectangles);
  430. }
  431. void Graphics::fillRectList (const RectangleList<int>& rects) const
  432. {
  433. for (auto& r : rects)
  434. context.fillRect (r, false);
  435. }
  436. void Graphics::fillAll() const
  437. {
  438. fillRect (context.getClipBounds());
  439. }
  440. void Graphics::fillAll (Colour colourToUse) const
  441. {
  442. if (! colourToUse.isTransparent())
  443. {
  444. auto clip = context.getClipBounds();
  445. context.saveState();
  446. context.setFill (colourToUse);
  447. context.fillRect (clip, false);
  448. context.restoreState();
  449. }
  450. }
  451. //==============================================================================
  452. void Graphics::fillPath (const Path& path) const
  453. {
  454. if (! (context.isClipEmpty() || path.isEmpty()))
  455. context.fillPath (path, AffineTransform());
  456. }
  457. void Graphics::fillPath (const Path& path, const AffineTransform& transform) const
  458. {
  459. if (! (context.isClipEmpty() || path.isEmpty()))
  460. context.fillPath (path, transform);
  461. }
  462. void Graphics::strokePath (const Path& path,
  463. const PathStrokeType& strokeType,
  464. const AffineTransform& transform) const
  465. {
  466. Path stroke;
  467. strokeType.createStrokedPath (stroke, path, transform, context.getPhysicalPixelScaleFactor());
  468. fillPath (stroke);
  469. }
  470. //==============================================================================
  471. void Graphics::drawRect (float x, float y, float width, float height, float lineThickness) const
  472. {
  473. drawRect (coordsToRectangle (x, y, width, height), lineThickness);
  474. }
  475. void Graphics::drawRect (int x, int y, int width, int height, int lineThickness) const
  476. {
  477. drawRect (coordsToRectangle (x, y, width, height), lineThickness);
  478. }
  479. void Graphics::drawRect (Rectangle<int> r, int lineThickness) const
  480. {
  481. drawRect (r.toFloat(), (float) lineThickness);
  482. }
  483. void Graphics::drawRect (Rectangle<float> r, const float lineThickness) const
  484. {
  485. jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f);
  486. RectangleList<float> rects;
  487. rects.addWithoutMerging (r.removeFromTop (lineThickness));
  488. rects.addWithoutMerging (r.removeFromBottom (lineThickness));
  489. rects.addWithoutMerging (r.removeFromLeft (lineThickness));
  490. rects.addWithoutMerging (r.removeFromRight (lineThickness));
  491. context.fillRectList (rects);
  492. }
  493. //==============================================================================
  494. void Graphics::fillEllipse (Rectangle<float> area) const
  495. {
  496. Path p;
  497. p.addEllipse (area);
  498. fillPath (p);
  499. }
  500. void Graphics::fillEllipse (float x, float y, float w, float h) const
  501. {
  502. fillEllipse (coordsToRectangle (x, y, w, h));
  503. }
  504. void Graphics::drawEllipse (float x, float y, float width, float height, float lineThickness) const
  505. {
  506. drawEllipse (coordsToRectangle (x, y, width, height), lineThickness);
  507. }
  508. void Graphics::drawEllipse (Rectangle<float> area, float lineThickness) const
  509. {
  510. Path p;
  511. if (area.getWidth() == area.getHeight())
  512. {
  513. // For a circle, we can avoid having to generate a stroke
  514. p.addEllipse (area.expanded (lineThickness * 0.5f));
  515. p.addEllipse (area.reduced (lineThickness * 0.5f));
  516. p.setUsingNonZeroWinding (false);
  517. fillPath (p);
  518. }
  519. else
  520. {
  521. p.addEllipse (area);
  522. strokePath (p, PathStrokeType (lineThickness));
  523. }
  524. }
  525. void Graphics::fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const
  526. {
  527. fillRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize);
  528. }
  529. void Graphics::fillRoundedRectangle (Rectangle<float> r, const float cornerSize) const
  530. {
  531. Path p;
  532. p.addRoundedRectangle (r, cornerSize);
  533. fillPath (p);
  534. }
  535. void Graphics::drawRoundedRectangle (float x, float y, float width, float height,
  536. float cornerSize, float lineThickness) const
  537. {
  538. drawRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize, lineThickness);
  539. }
  540. void Graphics::drawRoundedRectangle (Rectangle<float> r, float cornerSize, float lineThickness) const
  541. {
  542. Path p;
  543. p.addRoundedRectangle (r, cornerSize);
  544. strokePath (p, PathStrokeType (lineThickness));
  545. }
  546. void Graphics::drawArrow (Line<float> line, float lineThickness, float arrowheadWidth, float arrowheadLength) const
  547. {
  548. Path p;
  549. p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength);
  550. fillPath (p);
  551. }
  552. void Graphics::fillCheckerBoard (Rectangle<float> area, float checkWidth, float checkHeight,
  553. Colour colour1, Colour colour2) const
  554. {
  555. jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less!
  556. if (checkWidth > 0 && checkHeight > 0)
  557. {
  558. context.saveState();
  559. if (colour1 == colour2)
  560. {
  561. context.setFill (colour1);
  562. context.fillRect (area);
  563. }
  564. else
  565. {
  566. auto clipped = context.getClipBounds().getIntersection (area.getSmallestIntegerContainer());
  567. if (! clipped.isEmpty())
  568. {
  569. const int checkNumX = (int) (((float) clipped.getX() - area.getX()) / checkWidth);
  570. const int checkNumY = (int) (((float) clipped.getY() - area.getY()) / checkHeight);
  571. const float startX = area.getX() + (float) checkNumX * checkWidth;
  572. const float startY = area.getY() + (float) checkNumY * checkHeight;
  573. const float right = (float) clipped.getRight();
  574. const float bottom = (float) clipped.getBottom();
  575. for (int i = 0; i < 2; ++i)
  576. {
  577. int cy = i;
  578. RectangleList<float> checks;
  579. for (float y = startY; y < bottom; y += checkHeight)
  580. for (float x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2.0f)
  581. checks.addWithoutMerging ({ x, y, checkWidth, checkHeight });
  582. checks.clipTo (area);
  583. context.setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2);
  584. context.fillRectList (checks);
  585. }
  586. }
  587. }
  588. context.restoreState();
  589. }
  590. }
  591. //==============================================================================
  592. void Graphics::drawVerticalLine (const int x, float top, float bottom) const
  593. {
  594. if (top < bottom)
  595. context.fillRect (Rectangle<float> ((float) x, top, 1.0f, bottom - top));
  596. }
  597. void Graphics::drawHorizontalLine (const int y, float left, float right) const
  598. {
  599. if (left < right)
  600. context.fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
  601. }
  602. void Graphics::drawLine (Line<float> line) const
  603. {
  604. context.drawLine (line);
  605. }
  606. void Graphics::drawLine (float x1, float y1, float x2, float y2) const
  607. {
  608. context.drawLine (Line<float> (x1, y1, x2, y2));
  609. }
  610. void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThickness) const
  611. {
  612. drawLine (Line<float> (x1, y1, x2, y2), lineThickness);
  613. }
  614. void Graphics::drawLine (Line<float> line, const float lineThickness) const
  615. {
  616. Path p;
  617. p.addLineSegment (line, lineThickness);
  618. fillPath (p);
  619. }
  620. void Graphics::drawDashedLine (Line<float> line, const float* dashLengths,
  621. int numDashLengths, float lineThickness, int n) const
  622. {
  623. jassert (n >= 0 && n < numDashLengths); // your start index must be valid!
  624. const Point<double> delta ((line.getEnd() - line.getStart()).toDouble());
  625. const double totalLen = delta.getDistanceFromOrigin();
  626. if (totalLen >= 0.1)
  627. {
  628. const double onePixAlpha = 1.0 / totalLen;
  629. for (double alpha = 0.0; alpha < 1.0;)
  630. {
  631. jassert (dashLengths[n] > 0); // can't have zero-length dashes!
  632. const double lastAlpha = alpha;
  633. alpha += dashLengths [n] * onePixAlpha;
  634. n = (n + 1) % numDashLengths;
  635. if ((n & 1) != 0)
  636. {
  637. const Line<float> segment (line.getStart() + (delta * lastAlpha).toFloat(),
  638. line.getStart() + (delta * jmin (1.0, alpha)).toFloat());
  639. if (lineThickness != 1.0f)
  640. drawLine (segment, lineThickness);
  641. else
  642. context.drawLine (segment);
  643. }
  644. }
  645. }
  646. }
  647. //==============================================================================
  648. void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality)
  649. {
  650. saveStateIfPending();
  651. context.setInterpolationQuality (newQuality);
  652. }
  653. //==============================================================================
  654. void Graphics::drawImageAt (const Image& imageToDraw, int x, int y, bool fillAlphaChannel) const
  655. {
  656. drawImageTransformed (imageToDraw,
  657. AffineTransform::translation ((float) x, (float) y),
  658. fillAlphaChannel);
  659. }
  660. void Graphics::drawImage (const Image& imageToDraw, Rectangle<float> targetArea,
  661. RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
  662. {
  663. if (imageToDraw.isValid())
  664. drawImageTransformed (imageToDraw,
  665. placementWithinTarget.getTransformToFit (imageToDraw.getBounds().toFloat(), targetArea),
  666. fillAlphaChannelWithCurrentBrush);
  667. }
  668. void Graphics::drawImageWithin (const Image& imageToDraw, int dx, int dy, int dw, int dh,
  669. RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
  670. {
  671. drawImage (imageToDraw, coordsToRectangle (dx, dy, dw, dh).toFloat(),
  672. placementWithinTarget, fillAlphaChannelWithCurrentBrush);
  673. }
  674. void Graphics::drawImage (const Image& imageToDraw,
  675. int dx, int dy, int dw, int dh,
  676. int sx, int sy, int sw, int sh,
  677. const bool fillAlphaChannelWithCurrentBrush) const
  678. {
  679. if (imageToDraw.isValid() && context.clipRegionIntersects (coordsToRectangle (dx, dy, dw, dh)))
  680. drawImageTransformed (imageToDraw.getClippedImage (coordsToRectangle (sx, sy, sw, sh)),
  681. AffineTransform::scale ((float) dw / (float) sw, (float) dh / (float) sh)
  682. .translated ((float) dx, (float) dy),
  683. fillAlphaChannelWithCurrentBrush);
  684. }
  685. void Graphics::drawImageTransformed (const Image& imageToDraw,
  686. const AffineTransform& transform,
  687. const bool fillAlphaChannelWithCurrentBrush) const
  688. {
  689. if (imageToDraw.isValid() && ! context.isClipEmpty())
  690. {
  691. if (fillAlphaChannelWithCurrentBrush)
  692. {
  693. context.saveState();
  694. context.clipToImageAlpha (imageToDraw, transform);
  695. fillAll();
  696. context.restoreState();
  697. }
  698. else
  699. {
  700. context.drawImage (imageToDraw, transform);
  701. }
  702. }
  703. }
  704. //==============================================================================
  705. Graphics::ScopedSaveState::ScopedSaveState (Graphics& g) : context (g)
  706. {
  707. context.saveState();
  708. }
  709. Graphics::ScopedSaveState::~ScopedSaveState()
  710. {
  711. context.restoreState();
  712. }
  713. } // namespace juce