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.

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