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.

juce_GraphicsContext.cpp 29KB

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