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.

719 lines
24KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. namespace
  21. {
  22. template <typename Type>
  23. bool areCoordsSensibleNumbers (Type x, Type y, Type w, Type h)
  24. {
  25. const int maxVal = 0x3fffffff;
  26. return (int) x >= -maxVal && (int) x <= maxVal
  27. && (int) y >= -maxVal && (int) y <= maxVal
  28. && (int) w >= -maxVal && (int) w <= maxVal
  29. && (int) h >= -maxVal && (int) h <= maxVal;
  30. }
  31. }
  32. //==============================================================================
  33. LowLevelGraphicsContext::LowLevelGraphicsContext()
  34. {
  35. }
  36. LowLevelGraphicsContext::~LowLevelGraphicsContext()
  37. {
  38. }
  39. //==============================================================================
  40. Graphics::Graphics (const Image& imageToDrawOnto)
  41. : context (imageToDrawOnto.createLowLevelContext()),
  42. contextToDelete (context),
  43. saveStatePending (false)
  44. {
  45. }
  46. Graphics::Graphics (LowLevelGraphicsContext* const internalContext) noexcept
  47. : context (internalContext),
  48. saveStatePending (false)
  49. {
  50. }
  51. Graphics::~Graphics()
  52. {
  53. }
  54. //==============================================================================
  55. void Graphics::resetToDefaultState()
  56. {
  57. saveStateIfPending();
  58. context->setFill (FillType());
  59. context->setFont (Font());
  60. context->setInterpolationQuality (Graphics::mediumResamplingQuality);
  61. }
  62. bool Graphics::isVectorDevice() const
  63. {
  64. return context->isVectorDevice();
  65. }
  66. bool Graphics::reduceClipRegion (const Rectangle<int>& area)
  67. {
  68. saveStateIfPending();
  69. return context->clipToRectangle (area);
  70. }
  71. bool Graphics::reduceClipRegion (const int x, const int y, const int w, const int h)
  72. {
  73. return reduceClipRegion (Rectangle<int> (x, y, w, h));
  74. }
  75. bool Graphics::reduceClipRegion (const RectangleList& clipRegion)
  76. {
  77. saveStateIfPending();
  78. return context->clipToRectangleList (clipRegion);
  79. }
  80. bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform)
  81. {
  82. saveStateIfPending();
  83. context->clipToPath (path, transform);
  84. return ! context->isClipEmpty();
  85. }
  86. bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform)
  87. {
  88. saveStateIfPending();
  89. context->clipToImageAlpha (image, transform);
  90. return ! context->isClipEmpty();
  91. }
  92. void Graphics::excludeClipRegion (const Rectangle<int>& rectangleToExclude)
  93. {
  94. saveStateIfPending();
  95. context->excludeClipRectangle (rectangleToExclude);
  96. }
  97. bool Graphics::isClipEmpty() const
  98. {
  99. return context->isClipEmpty();
  100. }
  101. Rectangle<int> Graphics::getClipBounds() const
  102. {
  103. return context->getClipBounds();
  104. }
  105. void Graphics::saveState()
  106. {
  107. saveStateIfPending();
  108. saveStatePending = true;
  109. }
  110. void Graphics::restoreState()
  111. {
  112. if (saveStatePending)
  113. saveStatePending = false;
  114. else
  115. context->restoreState();
  116. }
  117. void Graphics::saveStateIfPending()
  118. {
  119. if (saveStatePending)
  120. {
  121. saveStatePending = false;
  122. context->saveState();
  123. }
  124. }
  125. void Graphics::setOrigin (const int newOriginX, const int newOriginY)
  126. {
  127. saveStateIfPending();
  128. context->setOrigin (newOriginX, newOriginY);
  129. }
  130. void Graphics::addTransform (const AffineTransform& transform)
  131. {
  132. saveStateIfPending();
  133. context->addTransform (transform);
  134. }
  135. bool Graphics::clipRegionIntersects (const Rectangle<int>& area) const
  136. {
  137. return context->clipRegionIntersects (area);
  138. }
  139. void Graphics::beginTransparencyLayer (float layerOpacity)
  140. {
  141. saveStateIfPending();
  142. context->beginTransparencyLayer (layerOpacity);
  143. }
  144. void Graphics::endTransparencyLayer()
  145. {
  146. context->endTransparencyLayer();
  147. }
  148. //==============================================================================
  149. void Graphics::setColour (const Colour& newColour)
  150. {
  151. saveStateIfPending();
  152. context->setFill (newColour);
  153. }
  154. void Graphics::setOpacity (const float newOpacity)
  155. {
  156. saveStateIfPending();
  157. context->setOpacity (newOpacity);
  158. }
  159. void Graphics::setGradientFill (const ColourGradient& gradient)
  160. {
  161. setFillType (gradient);
  162. }
  163. void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity)
  164. {
  165. saveStateIfPending();
  166. context->setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY)));
  167. context->setOpacity (opacity);
  168. }
  169. void Graphics::setFillType (const FillType& newFill)
  170. {
  171. saveStateIfPending();
  172. context->setFill (newFill);
  173. }
  174. //==============================================================================
  175. void Graphics::setFont (const Font& newFont)
  176. {
  177. saveStateIfPending();
  178. context->setFont (newFont);
  179. }
  180. void Graphics::setFont (const float newFontHeight, const int newFontStyleFlags)
  181. {
  182. saveStateIfPending();
  183. Font f (context->getFont());
  184. f.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0);
  185. context->setFont (f);
  186. }
  187. Font Graphics::getCurrentFont() const
  188. {
  189. return context->getFont();
  190. }
  191. //==============================================================================
  192. void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY) const
  193. {
  194. if (text.isNotEmpty()
  195. && startX < context->getClipBounds().getRight())
  196. {
  197. GlyphArrangement arr;
  198. arr.addLineOfText (context->getFont(), text, (float) startX, (float) baselineY);
  199. arr.draw (*this);
  200. }
  201. }
  202. void Graphics::drawTextAsPath (const String& text, const AffineTransform& transform) const
  203. {
  204. if (text.isNotEmpty())
  205. {
  206. GlyphArrangement arr;
  207. arr.addLineOfText (context->getFont(), text, 0.0f, 0.0f);
  208. arr.draw (*this, transform);
  209. }
  210. }
  211. void Graphics::drawMultiLineText (const String& text, const int startX, const int baselineY, const int maximumLineWidth) const
  212. {
  213. if (text.isNotEmpty()
  214. && startX < context->getClipBounds().getRight())
  215. {
  216. GlyphArrangement arr;
  217. arr.addJustifiedText (context->getFont(), text,
  218. (float) startX, (float) baselineY, (float) maximumLineWidth,
  219. Justification::left);
  220. arr.draw (*this);
  221. }
  222. }
  223. void Graphics::drawText (const String& text,
  224. const int x, const int y, const int width, const int height,
  225. const Justification& justificationType,
  226. const bool useEllipsesIfTooBig) const
  227. {
  228. if (text.isNotEmpty() && context->clipRegionIntersects (Rectangle<int> (x, y, width, height)))
  229. {
  230. GlyphArrangement arr;
  231. arr.addCurtailedLineOfText (context->getFont(), text,
  232. 0.0f, 0.0f, (float) width,
  233. useEllipsesIfTooBig);
  234. arr.justifyGlyphs (0, arr.getNumGlyphs(),
  235. (float) x, (float) y, (float) width, (float) height,
  236. justificationType);
  237. arr.draw (*this);
  238. }
  239. }
  240. void Graphics::drawFittedText (const String& text,
  241. const int x, const int y, const int width, const int height,
  242. const Justification& justification,
  243. const int maximumNumberOfLines,
  244. const float minimumHorizontalScale) const
  245. {
  246. if (text.isNotEmpty()
  247. && width > 0 && height > 0
  248. && context->clipRegionIntersects (Rectangle<int> (x, y, width, height)))
  249. {
  250. GlyphArrangement arr;
  251. arr.addFittedText (context->getFont(), text,
  252. (float) x, (float) y, (float) width, (float) height,
  253. justification,
  254. maximumNumberOfLines,
  255. minimumHorizontalScale);
  256. arr.draw (*this);
  257. }
  258. }
  259. //==============================================================================
  260. void Graphics::fillRect (int x, int y, int width, int height) const
  261. {
  262. // passing in a silly number can cause maths problems in rendering!
  263. jassert (areCoordsSensibleNumbers (x, y, width, height));
  264. context->fillRect (Rectangle<int> (x, y, width, height), false);
  265. }
  266. void Graphics::fillRect (const Rectangle<int>& r) const
  267. {
  268. context->fillRect (r, false);
  269. }
  270. void Graphics::fillRect (const float x, const float y, const float width, const float height) const
  271. {
  272. // passing in a silly number can cause maths problems in rendering!
  273. jassert (areCoordsSensibleNumbers (x, y, width, height));
  274. Path p;
  275. p.addRectangle (x, y, width, height);
  276. fillPath (p);
  277. }
  278. void Graphics::setPixel (int x, int y) const
  279. {
  280. context->fillRect (Rectangle<int> (x, y, 1, 1), false);
  281. }
  282. void Graphics::fillAll() const
  283. {
  284. fillRect (context->getClipBounds());
  285. }
  286. void Graphics::fillAll (const Colour& colourToUse) const
  287. {
  288. if (! colourToUse.isTransparent())
  289. {
  290. const Rectangle<int> clip (context->getClipBounds());
  291. context->saveState();
  292. context->setFill (colourToUse);
  293. context->fillRect (clip, false);
  294. context->restoreState();
  295. }
  296. }
  297. //==============================================================================
  298. void Graphics::fillPath (const Path& path, const AffineTransform& transform) const
  299. {
  300. if ((! context->isClipEmpty()) && ! path.isEmpty())
  301. context->fillPath (path, transform);
  302. }
  303. void Graphics::strokePath (const Path& path,
  304. const PathStrokeType& strokeType,
  305. const AffineTransform& transform) const
  306. {
  307. Path stroke;
  308. strokeType.createStrokedPath (stroke, path, transform, context->getScaleFactor());
  309. fillPath (stroke);
  310. }
  311. //==============================================================================
  312. void Graphics::drawRect (const int x, const int y, const int width, const int height,
  313. const int lineThickness) const
  314. {
  315. // passing in a silly number can cause maths problems in rendering!
  316. jassert (areCoordsSensibleNumbers (x, y, width, height));
  317. context->fillRect (Rectangle<int> (x, y, width, lineThickness), false);
  318. context->fillRect (Rectangle<int> (x, y + lineThickness, lineThickness, height - lineThickness * 2), false);
  319. context->fillRect (Rectangle<int> (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2), false);
  320. context->fillRect (Rectangle<int> (x, y + height - lineThickness, width, lineThickness), false);
  321. }
  322. void Graphics::drawRect (const float x, const float y, const float width, const float height, const float lineThickness) const
  323. {
  324. // passing in a silly number can cause maths problems in rendering!
  325. jassert (areCoordsSensibleNumbers (x, y, width, height));
  326. Path p;
  327. p.addRectangle (x, y, width, lineThickness);
  328. p.addRectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2.0f);
  329. p.addRectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2.0f);
  330. p.addRectangle (x, y + height - lineThickness, width, lineThickness);
  331. fillPath (p);
  332. }
  333. void Graphics::drawRect (const Rectangle<int>& r, const int lineThickness) const
  334. {
  335. drawRect (r.getX(), r.getY(), r.getWidth(), r.getHeight(), lineThickness);
  336. }
  337. void Graphics::drawBevel (const int x, const int y, const int width, const int height,
  338. const int bevelThickness, const Colour& topLeftColour, const Colour& bottomRightColour,
  339. const bool useGradient, const bool sharpEdgeOnOutside) const
  340. {
  341. // passing in a silly number can cause maths problems in rendering!
  342. jassert (areCoordsSensibleNumbers (x, y, width, height));
  343. if (clipRegionIntersects (Rectangle<int> (x, y, width, height)))
  344. {
  345. context->saveState();
  346. const float oldOpacity = 1.0f;//xxx state->colour.getFloatAlpha();
  347. const float ramp = oldOpacity / bevelThickness;
  348. for (int i = bevelThickness; --i >= 0;)
  349. {
  350. const float op = useGradient ? ramp * (sharpEdgeOnOutside ? bevelThickness - i : i)
  351. : oldOpacity;
  352. context->setFill (topLeftColour.withMultipliedAlpha (op));
  353. context->fillRect (Rectangle<int> (x + i, y + i, width - i * 2, 1), false);
  354. context->setFill (topLeftColour.withMultipliedAlpha (op * 0.75f));
  355. context->fillRect (Rectangle<int> (x + i, y + i + 1, 1, height - i * 2 - 2), false);
  356. context->setFill (bottomRightColour.withMultipliedAlpha (op));
  357. context->fillRect (Rectangle<int> (x + i, y + height - i - 1, width - i * 2, 1), false);
  358. context->setFill (bottomRightColour.withMultipliedAlpha (op * 0.75f));
  359. context->fillRect (Rectangle<int> (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2), false);
  360. }
  361. context->restoreState();
  362. }
  363. }
  364. //==============================================================================
  365. void Graphics::fillEllipse (const float x, const float y, const float width, const float height) const
  366. {
  367. // passing in a silly number can cause maths problems in rendering!
  368. jassert (areCoordsSensibleNumbers (x, y, width, height));
  369. Path p;
  370. p.addEllipse (x, y, width, height);
  371. fillPath (p);
  372. }
  373. void Graphics::drawEllipse (const float x, const float y, const float width, const float height,
  374. const float lineThickness) const
  375. {
  376. // passing in a silly number can cause maths problems in rendering!
  377. jassert (areCoordsSensibleNumbers (x, y, width, height));
  378. Path p;
  379. p.addEllipse (x, y, width, height);
  380. strokePath (p, PathStrokeType (lineThickness));
  381. }
  382. void Graphics::fillRoundedRectangle (const float x, const float y, const float width, const float height, const float cornerSize) const
  383. {
  384. // passing in a silly number can cause maths problems in rendering!
  385. jassert (areCoordsSensibleNumbers (x, y, width, height));
  386. Path p;
  387. p.addRoundedRectangle (x, y, width, height, cornerSize);
  388. fillPath (p);
  389. }
  390. void Graphics::fillRoundedRectangle (const Rectangle<float>& r, const float cornerSize) const
  391. {
  392. fillRoundedRectangle (r.getX(), r.getY(), r.getWidth(), r.getHeight(), cornerSize);
  393. }
  394. void Graphics::drawRoundedRectangle (const float x, const float y, const float width, const float height,
  395. const float cornerSize, const float lineThickness) const
  396. {
  397. // passing in a silly number can cause maths problems in rendering!
  398. jassert (areCoordsSensibleNumbers (x, y, width, height));
  399. Path p;
  400. p.addRoundedRectangle (x, y, width, height, cornerSize);
  401. strokePath (p, PathStrokeType (lineThickness));
  402. }
  403. void Graphics::drawRoundedRectangle (const Rectangle<float>& r, const float cornerSize, const float lineThickness) const
  404. {
  405. drawRoundedRectangle (r.getX(), r.getY(), r.getWidth(), r.getHeight(), cornerSize, lineThickness);
  406. }
  407. void Graphics::drawArrow (const Line<float>& line, const float lineThickness, const float arrowheadWidth, const float arrowheadLength) const
  408. {
  409. Path p;
  410. p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength);
  411. fillPath (p);
  412. }
  413. void Graphics::fillCheckerBoard (const Rectangle<int>& area,
  414. const int checkWidth, const int checkHeight,
  415. const Colour& colour1, const Colour& colour2) const
  416. {
  417. jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less!
  418. if (checkWidth > 0 && checkHeight > 0)
  419. {
  420. context->saveState();
  421. if (colour1 == colour2)
  422. {
  423. context->setFill (colour1);
  424. context->fillRect (area, false);
  425. }
  426. else
  427. {
  428. const Rectangle<int> clipped (context->getClipBounds().getIntersection (area));
  429. if (! clipped.isEmpty())
  430. {
  431. context->clipToRectangle (clipped);
  432. const int checkNumX = (clipped.getX() - area.getX()) / checkWidth;
  433. const int checkNumY = (clipped.getY() - area.getY()) / checkHeight;
  434. const int startX = area.getX() + checkNumX * checkWidth;
  435. const int startY = area.getY() + checkNumY * checkHeight;
  436. const int right = clipped.getRight();
  437. const int bottom = clipped.getBottom();
  438. for (int i = 0; i < 2; ++i)
  439. {
  440. context->setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2);
  441. int cy = i;
  442. for (int y = startY; y < bottom; y += checkHeight)
  443. for (int x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2)
  444. context->fillRect (Rectangle<int> (x, y, checkWidth, checkHeight), false);
  445. }
  446. }
  447. }
  448. context->restoreState();
  449. }
  450. }
  451. //==============================================================================
  452. void Graphics::drawVerticalLine (const int x, float top, float bottom) const
  453. {
  454. context->drawVerticalLine (x, top, bottom);
  455. }
  456. void Graphics::drawHorizontalLine (const int y, float left, float right) const
  457. {
  458. context->drawHorizontalLine (y, left, right);
  459. }
  460. void Graphics::drawLine (const float x1, const float y1, const float x2, const float y2) const
  461. {
  462. context->drawLine (Line<float> (x1, y1, x2, y2));
  463. }
  464. void Graphics::drawLine (const Line<float>& line) const
  465. {
  466. context->drawLine (line);
  467. }
  468. void Graphics::drawLine (const float x1, const float y1, const float x2, const float y2, const float lineThickness) const
  469. {
  470. drawLine (Line<float> (x1, y1, x2, y2), lineThickness);
  471. }
  472. void Graphics::drawLine (const Line<float>& line, const float lineThickness) const
  473. {
  474. Path p;
  475. p.addLineSegment (line, lineThickness);
  476. fillPath (p);
  477. }
  478. void Graphics::drawDashedLine (const Line<float>& line, const float* const dashLengths,
  479. const int numDashLengths, const float lineThickness, int n) const
  480. {
  481. jassert (n >= 0 && n < numDashLengths); // your start index must be valid!
  482. const Point<double> delta ((line.getEnd() - line.getStart()).toDouble());
  483. const double totalLen = delta.getDistanceFromOrigin();
  484. if (totalLen >= 0.1)
  485. {
  486. const double onePixAlpha = 1.0 / totalLen;
  487. for (double alpha = 0.0; alpha < 1.0;)
  488. {
  489. jassert (dashLengths[n] > 0); // can't have zero-length dashes!
  490. const double lastAlpha = alpha;
  491. alpha = jmin (1.0, alpha + dashLengths [n] * onePixAlpha);
  492. n = (n + 1) % numDashLengths;
  493. if ((n & 1) != 0)
  494. {
  495. const Line<float> segment (line.getStart() + (delta * lastAlpha).toFloat(),
  496. line.getStart() + (delta * alpha).toFloat());
  497. if (lineThickness != 1.0f)
  498. drawLine (segment, lineThickness);
  499. else
  500. context->drawLine (segment);
  501. }
  502. }
  503. }
  504. }
  505. //==============================================================================
  506. void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality)
  507. {
  508. saveStateIfPending();
  509. context->setInterpolationQuality (newQuality);
  510. }
  511. //==============================================================================
  512. void Graphics::drawImageAt (const Image& imageToDraw,
  513. const int topLeftX, const int topLeftY,
  514. const bool fillAlphaChannelWithCurrentBrush) const
  515. {
  516. const int imageW = imageToDraw.getWidth();
  517. const int imageH = imageToDraw.getHeight();
  518. drawImage (imageToDraw,
  519. topLeftX, topLeftY, imageW, imageH,
  520. 0, 0, imageW, imageH,
  521. fillAlphaChannelWithCurrentBrush);
  522. }
  523. void Graphics::drawImageWithin (const Image& imageToDraw,
  524. const int destX, const int destY,
  525. const int destW, const int destH,
  526. const RectanglePlacement& placementWithinTarget,
  527. const bool fillAlphaChannelWithCurrentBrush) const
  528. {
  529. // passing in a silly number can cause maths problems in rendering!
  530. jassert (areCoordsSensibleNumbers (destX, destY, destW, destH));
  531. if (imageToDraw.isValid())
  532. {
  533. const int imageW = imageToDraw.getWidth();
  534. const int imageH = imageToDraw.getHeight();
  535. if (imageW > 0 && imageH > 0)
  536. {
  537. double newX = 0.0, newY = 0.0;
  538. double newW = imageW;
  539. double newH = imageH;
  540. placementWithinTarget.applyTo (newX, newY, newW, newH,
  541. destX, destY, destW, destH);
  542. if (newW > 0 && newH > 0)
  543. {
  544. drawImage (imageToDraw,
  545. roundToInt (newX), roundToInt (newY),
  546. roundToInt (newW), roundToInt (newH),
  547. 0, 0, imageW, imageH,
  548. fillAlphaChannelWithCurrentBrush);
  549. }
  550. }
  551. }
  552. }
  553. void Graphics::drawImage (const Image& imageToDraw,
  554. int dx, int dy, int dw, int dh,
  555. int sx, int sy, int sw, int sh,
  556. const bool fillAlphaChannelWithCurrentBrush) const
  557. {
  558. // passing in a silly number can cause maths problems in rendering!
  559. jassert (areCoordsSensibleNumbers (dx, dy, dw, dh));
  560. jassert (areCoordsSensibleNumbers (sx, sy, sw, sh));
  561. if (imageToDraw.isValid() && context->clipRegionIntersects (Rectangle<int> (dx, dy, dw, dh)))
  562. {
  563. drawImageTransformed (imageToDraw.getClippedImage (Rectangle<int> (sx, sy, sw, sh)),
  564. AffineTransform::scale (dw / (float) sw, dh / (float) sh)
  565. .translated ((float) dx, (float) dy),
  566. fillAlphaChannelWithCurrentBrush);
  567. }
  568. }
  569. void Graphics::drawImageTransformed (const Image& imageToDraw,
  570. const AffineTransform& transform,
  571. const bool fillAlphaChannelWithCurrentBrush) const
  572. {
  573. if (imageToDraw.isValid() && ! context->isClipEmpty())
  574. {
  575. if (fillAlphaChannelWithCurrentBrush)
  576. {
  577. context->saveState();
  578. context->clipToImageAlpha (imageToDraw, transform);
  579. fillAll();
  580. context->restoreState();
  581. }
  582. else
  583. {
  584. context->drawImage (imageToDraw, transform, false);
  585. }
  586. }
  587. }
  588. //==============================================================================
  589. Graphics::ScopedSaveState::ScopedSaveState (Graphics& g)
  590. : context (g)
  591. {
  592. context.saveState();
  593. }
  594. Graphics::ScopedSaveState::~ScopedSaveState()
  595. {
  596. context.restoreState();
  597. }
  598. END_JUCE_NAMESPACE