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.

2588 lines
98KB

  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. #ifndef __JUCE_RENDERINGHELPERS_JUCEHEADER__
  19. #define __JUCE_RENDERINGHELPERS_JUCEHEADER__
  20. #if JUCE_MSVC
  21. #pragma warning (push)
  22. #pragma warning (disable: 4127) // "expression is constant" warning
  23. #endif
  24. namespace RenderingHelpers
  25. {
  26. //==============================================================================
  27. /** Holds either a simple integer translation, or an affine transform.
  28. */
  29. class TranslationOrTransform
  30. {
  31. public:
  32. TranslationOrTransform (int x, int y) noexcept
  33. : xOffset (x), yOffset (y), isOnlyTranslated (true), isIntegerScaling (true)
  34. {
  35. }
  36. TranslationOrTransform (const TranslationOrTransform& other) noexcept
  37. : complexTransform (other.complexTransform),
  38. xOffset (other.xOffset), yOffset (other.yOffset),
  39. isOnlyTranslated (other.isOnlyTranslated), isIntegerScaling (other.isIntegerScaling)
  40. {
  41. }
  42. AffineTransform getTransform() const noexcept
  43. {
  44. return isOnlyTranslated ? AffineTransform::translation ((float) xOffset, (float) yOffset)
  45. : complexTransform;
  46. }
  47. AffineTransform getTransformWith (const AffineTransform& userTransform) const noexcept
  48. {
  49. return isOnlyTranslated ? userTransform.translated ((float) xOffset, (float) yOffset)
  50. : userTransform.followedBy (complexTransform);
  51. }
  52. void setOrigin (const int x, const int y) noexcept
  53. {
  54. if (isOnlyTranslated)
  55. {
  56. xOffset += x;
  57. yOffset += y;
  58. }
  59. else
  60. {
  61. complexTransform = AffineTransform::translation ((float) x, (float) y)
  62. .followedBy (complexTransform);
  63. }
  64. }
  65. void addTransform (const AffineTransform& t) noexcept
  66. {
  67. if (isOnlyTranslated
  68. && t.isOnlyTranslation()
  69. && isIntegerTranslation (t))
  70. {
  71. xOffset += (int) t.getTranslationX();
  72. yOffset += (int) t.getTranslationY();
  73. }
  74. else
  75. {
  76. complexTransform = getTransformWith (t);
  77. isOnlyTranslated = false;
  78. isIntegerScaling = isIntegerScale (complexTransform);
  79. }
  80. }
  81. float getScaleFactor() const noexcept
  82. {
  83. return isOnlyTranslated ? 1.0f : complexTransform.getScaleFactor();
  84. }
  85. void moveOriginInDeviceSpace (const int dx, const int dy) noexcept
  86. {
  87. if (isOnlyTranslated)
  88. {
  89. xOffset += dx;
  90. yOffset += dy;
  91. }
  92. else
  93. {
  94. complexTransform = complexTransform.translated ((float) dx, (float) dy);
  95. }
  96. }
  97. template <typename Type>
  98. Rectangle<Type> translated (const Rectangle<Type>& r) const noexcept
  99. {
  100. jassert (isOnlyTranslated);
  101. return r.translated (static_cast <Type> (xOffset),
  102. static_cast <Type> (yOffset));
  103. }
  104. template <typename Type>
  105. Rectangle<float> transformed (const Rectangle<Type>& r) const noexcept
  106. {
  107. return r.toFloat().transformed (complexTransform);
  108. }
  109. Rectangle<int> deviceSpaceToUserSpace (const Rectangle<int>& r) const noexcept
  110. {
  111. return isOnlyTranslated ? r.translated (-xOffset, -yOffset)
  112. : r.toFloat().transformed (complexTransform.inverted()).getSmallestIntegerContainer();
  113. }
  114. AffineTransform complexTransform;
  115. int xOffset, yOffset;
  116. bool isOnlyTranslated, isIntegerScaling;
  117. private:
  118. static inline bool isIntegerTranslation (const AffineTransform& t) noexcept
  119. {
  120. const int tx = (int) (t.getTranslationX() * 256.0f);
  121. const int ty = (int) (t.getTranslationY() * 256.0f);
  122. return ((tx | ty) & 0xf8) == 0;
  123. }
  124. static inline bool isIntegerScale (const AffineTransform& t) noexcept
  125. {
  126. if (t.mat01 != 0 || t.mat10 != 0)
  127. return false;
  128. const int tx = (int) (t.getTranslationX() * 256.0f);
  129. const int ty = (int) (t.getTranslationY() * 256.0f);
  130. const int txs = (int) (t.mat00 * 256.0f);
  131. const int tys = (int) (t.mat11 * 256.0f);
  132. return ((tx | ty | txs | tys) & 0xf8) == 0;
  133. }
  134. };
  135. //==============================================================================
  136. /** Holds a cache of recently-used glyph objects of some type. */
  137. template <class CachedGlyphType, class RenderTargetType>
  138. class GlyphCache : private DeletedAtShutdown
  139. {
  140. public:
  141. GlyphCache()
  142. {
  143. addNewGlyphSlots (120);
  144. }
  145. ~GlyphCache()
  146. {
  147. getSingletonPointer() = nullptr;
  148. }
  149. static GlyphCache& getInstance()
  150. {
  151. GlyphCache*& g = getSingletonPointer();
  152. if (g == nullptr)
  153. g = new GlyphCache();
  154. return *g;
  155. }
  156. //==============================================================================
  157. void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, float x, float y)
  158. {
  159. ++accessCounter;
  160. CachedGlyphType* glyph = nullptr;
  161. const ScopedReadLock srl (lock);
  162. for (int i = glyphs.size(); --i >= 0;)
  163. {
  164. CachedGlyphType* const g = glyphs.getUnchecked (i);
  165. if (g->glyph == glyphNumber && g->font == font)
  166. {
  167. glyph = g;
  168. ++hits;
  169. break;
  170. }
  171. }
  172. if (glyph == nullptr)
  173. {
  174. ++misses;
  175. const ScopedWriteLock swl (lock);
  176. if (hits.value + misses.value > glyphs.size() * 16)
  177. {
  178. if (misses.value * 2 > hits.value)
  179. addNewGlyphSlots (32);
  180. hits.set (0);
  181. misses.set (0);
  182. glyph = glyphs.getLast();
  183. }
  184. else
  185. {
  186. glyph = findLeastRecentlyUsedGlyph();
  187. }
  188. jassert (glyph != nullptr);
  189. glyph->generate (font, glyphNumber);
  190. }
  191. glyph->lastAccessCount = accessCounter.value;
  192. glyph->draw (target, x, y);
  193. }
  194. private:
  195. friend class OwnedArray <CachedGlyphType>;
  196. OwnedArray <CachedGlyphType> glyphs;
  197. Atomic<int> accessCounter, hits, misses;
  198. ReadWriteLock lock;
  199. void addNewGlyphSlots (int num)
  200. {
  201. while (--num >= 0)
  202. glyphs.add (new CachedGlyphType());
  203. }
  204. CachedGlyphType* findLeastRecentlyUsedGlyph() const noexcept
  205. {
  206. CachedGlyphType* oldest = glyphs.getLast();
  207. int oldestCounter = oldest->lastAccessCount;
  208. for (int i = glyphs.size() - 1; --i >= 0;)
  209. {
  210. CachedGlyphType* const glyph = glyphs.getUnchecked(i);
  211. if (glyph->lastAccessCount <= oldestCounter)
  212. {
  213. oldestCounter = glyph->lastAccessCount;
  214. oldest = glyph;
  215. }
  216. }
  217. return oldest;
  218. }
  219. static GlyphCache*& getSingletonPointer() noexcept
  220. {
  221. static GlyphCache* g = nullptr;
  222. return g;
  223. }
  224. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache)
  225. };
  226. //==============================================================================
  227. /** Caches a glyph as an edge-table. */
  228. template <class RendererType>
  229. class CachedGlyphEdgeTable
  230. {
  231. public:
  232. CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {}
  233. void draw (RendererType& state, float x, const float y) const
  234. {
  235. if (snapToIntegerCoordinate)
  236. x = std::floor (x + 0.5f);
  237. if (edgeTable != nullptr)
  238. state.fillEdgeTable (*edgeTable, x, roundToInt (y));
  239. }
  240. void generate (const Font& newFont, const int glyphNumber)
  241. {
  242. font = newFont;
  243. Typeface* const typeface = newFont.getTypeface();
  244. snapToIntegerCoordinate = typeface->isHinted();
  245. glyph = glyphNumber;
  246. const float fontHeight = font.getHeight();
  247. edgeTable = typeface->getEdgeTableForGlyph (glyphNumber,
  248. AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
  249. #if JUCE_MAC || JUCE_IOS
  250. .translated (0.0f, -0.5f)
  251. #endif
  252. );
  253. }
  254. Font font;
  255. int glyph, lastAccessCount;
  256. bool snapToIntegerCoordinate;
  257. private:
  258. ScopedPointer <EdgeTable> edgeTable;
  259. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable)
  260. };
  261. //==============================================================================
  262. /** Calculates the alpha values and positions for rendering the edges of a
  263. non-pixel-aligned rectangle.
  264. */
  265. struct FloatRectangleRasterisingInfo
  266. {
  267. FloatRectangleRasterisingInfo (const Rectangle<float>& area)
  268. : left (roundToInt (256.0f * area.getX())),
  269. top (roundToInt (256.0f * area.getY())),
  270. right (roundToInt (256.0f * area.getRight())),
  271. bottom (roundToInt (256.0f * area.getBottom()))
  272. {
  273. if ((top >> 8) == (bottom >> 8))
  274. {
  275. topAlpha = bottom - top;
  276. bottomAlpha = 0;
  277. totalTop = top >> 8;
  278. totalBottom = bottom = top = totalTop + 1;
  279. }
  280. else
  281. {
  282. if ((top & 255) == 0)
  283. {
  284. topAlpha = 0;
  285. top = totalTop = (top >> 8);
  286. }
  287. else
  288. {
  289. topAlpha = 255 - (top & 255);
  290. totalTop = (top >> 8);
  291. top = totalTop + 1;
  292. }
  293. bottomAlpha = bottom & 255;
  294. bottom >>= 8;
  295. totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0);
  296. }
  297. if ((left >> 8) == (right >> 8))
  298. {
  299. leftAlpha = right - left;
  300. rightAlpha = 0;
  301. totalLeft = (left >> 8);
  302. totalRight = right = left = totalLeft + 1;
  303. }
  304. else
  305. {
  306. if ((left & 255) == 0)
  307. {
  308. leftAlpha = 0;
  309. left = totalLeft = (left >> 8);
  310. }
  311. else
  312. {
  313. leftAlpha = 255 - (left & 255);
  314. totalLeft = (left >> 8);
  315. left = totalLeft + 1;
  316. }
  317. rightAlpha = right & 255;
  318. right >>= 8;
  319. totalRight = right + (rightAlpha != 0 ? 1 : 0);
  320. }
  321. }
  322. template <class Callback>
  323. void iterate (Callback& callback) const
  324. {
  325. if (topAlpha != 0) callback (totalLeft, totalTop, totalRight - totalLeft, 1, topAlpha);
  326. if (bottomAlpha != 0) callback (totalLeft, bottom, totalRight - totalLeft, 1, bottomAlpha);
  327. if (leftAlpha != 0) callback (totalLeft, totalTop, 1, totalBottom - totalTop, leftAlpha);
  328. if (rightAlpha != 0) callback (right, totalTop, 1, totalBottom - totalTop, rightAlpha);
  329. callback (left, top, right - left, bottom - top, 255);
  330. }
  331. inline bool isOnePixelWide() const noexcept { return right - left == 1 && leftAlpha + rightAlpha == 0; }
  332. inline int getTopLeftCornerAlpha() const noexcept { return (topAlpha * leftAlpha) >> 8; }
  333. inline int getTopRightCornerAlpha() const noexcept { return (topAlpha * rightAlpha) >> 8; }
  334. inline int getBottomLeftCornerAlpha() const noexcept { return (bottomAlpha * leftAlpha) >> 8; }
  335. inline int getBottomRightCornerAlpha() const noexcept { return (bottomAlpha * rightAlpha) >> 8; }
  336. //==============================================================================
  337. int left, top, right, bottom; // bounds of the solid central area, excluding anti-aliased edges
  338. int totalTop, totalLeft, totalBottom, totalRight; // bounds of the total area, including edges
  339. int topAlpha, leftAlpha, bottomAlpha, rightAlpha; // alpha of each anti-aliased edge
  340. };
  341. //==============================================================================
  342. /** Contains classes for calculating the colour of pixels within various types of gradient. */
  343. namespace GradientPixelIterators
  344. {
  345. /** Iterates the colour of pixels in a linear gradient */
  346. class Linear
  347. {
  348. public:
  349. Linear (const ColourGradient& gradient, const AffineTransform& transform,
  350. const PixelARGB* const colours, const int numColours)
  351. : lookupTable (colours),
  352. numEntries (numColours)
  353. {
  354. jassert (numColours >= 0);
  355. Point<float> p1 (gradient.point1);
  356. Point<float> p2 (gradient.point2);
  357. if (! transform.isIdentity())
  358. {
  359. const Line<float> l (p2, p1);
  360. Point<float> p3 = l.getPointAlongLine (0.0f, 100.0f);
  361. p1.applyTransform (transform);
  362. p2.applyTransform (transform);
  363. p3.applyTransform (transform);
  364. p2 = Line<float> (p2, p3).findNearestPointTo (p1);
  365. }
  366. vertical = std::abs (p1.x - p2.x) < 0.001f;
  367. horizontal = std::abs (p1.y - p2.y) < 0.001f;
  368. if (vertical)
  369. {
  370. scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.y - p1.y));
  371. start = roundToInt (p1.y * scale);
  372. }
  373. else if (horizontal)
  374. {
  375. scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.x - p1.x));
  376. start = roundToInt (p1.x * scale);
  377. }
  378. else
  379. {
  380. grad = (p2.getY() - p1.y) / (double) (p1.x - p2.x);
  381. yTerm = p1.getY() - p1.x / grad;
  382. scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (p2.y * grad - p2.x)));
  383. grad *= scale;
  384. }
  385. }
  386. forcedinline void setY (const int y) noexcept
  387. {
  388. if (vertical)
  389. linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)];
  390. else if (! horizontal)
  391. start = roundToInt ((y - yTerm) * grad);
  392. }
  393. inline PixelARGB getPixel (const int x) const noexcept
  394. {
  395. return vertical ? linePix
  396. : lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)];
  397. }
  398. private:
  399. const PixelARGB* const lookupTable;
  400. const int numEntries;
  401. PixelARGB linePix;
  402. int start, scale;
  403. double grad, yTerm;
  404. bool vertical, horizontal;
  405. enum { numScaleBits = 12 };
  406. JUCE_DECLARE_NON_COPYABLE (Linear)
  407. };
  408. //==============================================================================
  409. /** Iterates the colour of pixels in a circular radial gradient */
  410. class Radial
  411. {
  412. public:
  413. Radial (const ColourGradient& gradient, const AffineTransform&,
  414. const PixelARGB* const colours, const int numColours)
  415. : lookupTable (colours),
  416. numEntries (numColours),
  417. gx1 (gradient.point1.x),
  418. gy1 (gradient.point1.y)
  419. {
  420. jassert (numColours >= 0);
  421. const Point<float> diff (gradient.point1 - gradient.point2);
  422. maxDist = diff.x * diff.x + diff.y * diff.y;
  423. invScale = numEntries / std::sqrt (maxDist);
  424. jassert (roundToInt (std::sqrt (maxDist) * invScale) <= numEntries);
  425. }
  426. forcedinline void setY (const int y) noexcept
  427. {
  428. dy = y - gy1;
  429. dy *= dy;
  430. }
  431. inline PixelARGB getPixel (const int px) const noexcept
  432. {
  433. double x = px - gx1;
  434. x *= x;
  435. x += dy;
  436. return lookupTable [x >= maxDist ? numEntries : roundToInt (std::sqrt (x) * invScale)];
  437. }
  438. protected:
  439. const PixelARGB* const lookupTable;
  440. const int numEntries;
  441. const double gx1, gy1;
  442. double maxDist, invScale, dy;
  443. JUCE_DECLARE_NON_COPYABLE (Radial)
  444. };
  445. //==============================================================================
  446. /** Iterates the colour of pixels in a skewed radial gradient */
  447. class TransformedRadial : public Radial
  448. {
  449. public:
  450. TransformedRadial (const ColourGradient& gradient, const AffineTransform& transform,
  451. const PixelARGB* const colours, const int numColours)
  452. : Radial (gradient, transform, colours, numColours),
  453. inverseTransform (transform.inverted())
  454. {
  455. tM10 = inverseTransform.mat10;
  456. tM00 = inverseTransform.mat00;
  457. }
  458. forcedinline void setY (const int y) noexcept
  459. {
  460. lineYM01 = inverseTransform.mat01 * y + inverseTransform.mat02 - gx1;
  461. lineYM11 = inverseTransform.mat11 * y + inverseTransform.mat12 - gy1;
  462. }
  463. inline PixelARGB getPixel (const int px) const noexcept
  464. {
  465. double x = px;
  466. const double y = tM10 * x + lineYM11;
  467. x = tM00 * x + lineYM01;
  468. x *= x;
  469. x += y * y;
  470. if (x >= maxDist)
  471. return lookupTable [numEntries];
  472. else
  473. return lookupTable [jmin (numEntries, roundToInt (std::sqrt (x) * invScale))];
  474. }
  475. private:
  476. double tM10, tM00, lineYM01, lineYM11;
  477. const AffineTransform inverseTransform;
  478. JUCE_DECLARE_NON_COPYABLE (TransformedRadial)
  479. };
  480. }
  481. //==============================================================================
  482. /** Contains classes for filling edge tables with various fill types. */
  483. namespace EdgeTableFillers
  484. {
  485. /** Fills an edge-table with a solid colour. */
  486. template <class PixelType, bool replaceExisting = false>
  487. class SolidColour
  488. {
  489. public:
  490. SolidColour (const Image::BitmapData& image, const PixelARGB& colour)
  491. : data (image), sourceColour (colour)
  492. {
  493. if (sizeof (PixelType) == 3 && data.pixelStride == sizeof (PixelType))
  494. {
  495. areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen()
  496. && sourceColour.getGreen() == sourceColour.getBlue();
  497. filler[0].set (sourceColour);
  498. filler[1].set (sourceColour);
  499. filler[2].set (sourceColour);
  500. filler[3].set (sourceColour);
  501. }
  502. }
  503. forcedinline void setEdgeTableYPos (const int y) noexcept
  504. {
  505. linePixels = (PixelType*) data.getLinePointer (y);
  506. }
  507. forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept
  508. {
  509. if (replaceExisting)
  510. getPixel (x)->set (sourceColour);
  511. else
  512. getPixel (x)->blend (sourceColour, (uint32) alphaLevel);
  513. }
  514. forcedinline void handleEdgeTablePixelFull (const int x) const noexcept
  515. {
  516. if (replaceExisting)
  517. getPixel (x)->set (sourceColour);
  518. else
  519. getPixel (x)->blend (sourceColour);
  520. }
  521. forcedinline void handleEdgeTableLine (const int x, const int width, const int alphaLevel) const noexcept
  522. {
  523. PixelARGB p (sourceColour);
  524. p.multiplyAlpha (alphaLevel);
  525. PixelType* dest = getPixel (x);
  526. if (replaceExisting || p.getAlpha() >= 0xff)
  527. replaceLine (dest, p, width);
  528. else
  529. blendLine (dest, p, width);
  530. }
  531. forcedinline void handleEdgeTableLineFull (const int x, const int width) const noexcept
  532. {
  533. PixelType* dest = getPixel (x);
  534. if (replaceExisting || sourceColour.getAlpha() >= 0xff)
  535. replaceLine (dest, sourceColour, width);
  536. else
  537. blendLine (dest, sourceColour, width);
  538. }
  539. private:
  540. const Image::BitmapData& data;
  541. PixelType* linePixels;
  542. PixelARGB sourceColour;
  543. PixelRGB filler [4];
  544. bool areRGBComponentsEqual;
  545. forcedinline PixelType* getPixel (const int x) const noexcept
  546. {
  547. return addBytesToPointer (linePixels, x * data.pixelStride);
  548. }
  549. forcedinline void incDestPixelPointer (PixelType*& p) const noexcept
  550. {
  551. p = addBytesToPointer (p, data.pixelStride);
  552. }
  553. inline void blendLine (PixelType* dest, const PixelARGB& colour, int width) const noexcept
  554. {
  555. do
  556. {
  557. dest->blend (colour);
  558. incDestPixelPointer (dest);
  559. }
  560. while (--width > 0);
  561. }
  562. forcedinline void replaceLine (PixelRGB* dest, const PixelARGB& colour, int width) const noexcept
  563. {
  564. if (data.pixelStride == sizeof (*dest))
  565. {
  566. if (areRGBComponentsEqual) // if all the component values are the same, we can cheat..
  567. {
  568. memset (dest, colour.getRed(), (size_t) width * 3);
  569. }
  570. else
  571. {
  572. if (width >> 5)
  573. {
  574. const int* const intFiller = reinterpret_cast<const int*> (filler);
  575. while (width > 8 && (((pointer_sized_int) dest) & 7) != 0)
  576. {
  577. dest->set (colour);
  578. ++dest;
  579. --width;
  580. }
  581. while (width > 4)
  582. {
  583. int* d = reinterpret_cast<int*> (dest);
  584. *d++ = intFiller[0];
  585. *d++ = intFiller[1];
  586. *d++ = intFiller[2];
  587. dest = reinterpret_cast<PixelRGB*> (d);
  588. width -= 4;
  589. }
  590. }
  591. while (--width >= 0)
  592. {
  593. dest->set (colour);
  594. ++dest;
  595. }
  596. }
  597. }
  598. else
  599. {
  600. do
  601. {
  602. dest->set (colour);
  603. incDestPixelPointer (dest);
  604. }
  605. while (--width > 0);
  606. }
  607. }
  608. forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB& colour, int width) const noexcept
  609. {
  610. if (data.pixelStride == sizeof (*dest))
  611. {
  612. memset (dest, colour.getAlpha(), (size_t) width);
  613. }
  614. else
  615. {
  616. do
  617. {
  618. dest->setAlpha (colour.getAlpha());
  619. incDestPixelPointer (dest);
  620. }
  621. while (--width > 0);
  622. }
  623. }
  624. forcedinline void replaceLine (PixelARGB* dest, const PixelARGB& colour, int width) const noexcept
  625. {
  626. do
  627. {
  628. dest->set (colour);
  629. incDestPixelPointer (dest);
  630. } while (--width > 0);
  631. }
  632. JUCE_DECLARE_NON_COPYABLE (SolidColour)
  633. };
  634. //==============================================================================
  635. /** Fills an edge-table with a gradient. */
  636. template <class PixelType, class GradientType>
  637. class Gradient : public GradientType
  638. {
  639. public:
  640. Gradient (const Image::BitmapData& dest, const ColourGradient& gradient, const AffineTransform& transform,
  641. const PixelARGB* const colours, const int numColours)
  642. : GradientType (gradient, transform, colours, numColours - 1),
  643. destData (dest)
  644. {
  645. }
  646. forcedinline void setEdgeTableYPos (const int y) noexcept
  647. {
  648. linePixels = (PixelType*) destData.getLinePointer (y);
  649. GradientType::setY (y);
  650. }
  651. forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept
  652. {
  653. getPixel (x)->blend (GradientType::getPixel (x), (uint32) alphaLevel);
  654. }
  655. forcedinline void handleEdgeTablePixelFull (const int x) const noexcept
  656. {
  657. getPixel (x)->blend (GradientType::getPixel (x));
  658. }
  659. void handleEdgeTableLine (int x, int width, const int alphaLevel) const noexcept
  660. {
  661. PixelType* dest = getPixel (x);
  662. if (alphaLevel < 0xff)
  663. {
  664. do
  665. {
  666. dest->blend (GradientType::getPixel (x++), (uint32) alphaLevel);
  667. incDestPixelPointer (dest);
  668. } while (--width > 0);
  669. }
  670. else
  671. {
  672. do
  673. {
  674. dest->blend (GradientType::getPixel (x++));
  675. incDestPixelPointer (dest);
  676. }
  677. while (--width > 0);
  678. }
  679. }
  680. void handleEdgeTableLineFull (int x, int width) const noexcept
  681. {
  682. PixelType* dest = getPixel (x);
  683. do
  684. {
  685. dest->blend (GradientType::getPixel (x++));
  686. incDestPixelPointer (dest);
  687. }
  688. while (--width > 0);
  689. }
  690. private:
  691. const Image::BitmapData& destData;
  692. PixelType* linePixels;
  693. forcedinline PixelType* getPixel (const int x) const noexcept
  694. {
  695. return addBytesToPointer (linePixels, x * destData.pixelStride);
  696. }
  697. forcedinline void incDestPixelPointer (PixelType*& p) const noexcept
  698. {
  699. p = addBytesToPointer (p, destData.pixelStride);
  700. }
  701. JUCE_DECLARE_NON_COPYABLE (Gradient)
  702. };
  703. //==============================================================================
  704. /** Fills an edge-table with a non-transformed image. */
  705. template <class DestPixelType, class SrcPixelType, bool repeatPattern>
  706. class ImageFill
  707. {
  708. public:
  709. ImageFill (const Image::BitmapData& dest, const Image::BitmapData& src,
  710. const int alpha, const int x, const int y)
  711. : destData (dest),
  712. srcData (src),
  713. extraAlpha (alpha + 1),
  714. xOffset (repeatPattern ? negativeAwareModulo (x, src.width) - src.width : x),
  715. yOffset (repeatPattern ? negativeAwareModulo (y, src.height) - src.height : y)
  716. {
  717. }
  718. forcedinline void setEdgeTableYPos (int y) noexcept
  719. {
  720. linePixels = (DestPixelType*) destData.getLinePointer (y);
  721. y -= yOffset;
  722. if (repeatPattern)
  723. {
  724. jassert (y >= 0);
  725. y %= srcData.height;
  726. }
  727. sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y);
  728. }
  729. forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const noexcept
  730. {
  731. alphaLevel = (alphaLevel * extraAlpha) >> 8;
  732. getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) alphaLevel);
  733. }
  734. forcedinline void handleEdgeTablePixelFull (const int x) const noexcept
  735. {
  736. getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) extraAlpha);
  737. }
  738. void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept
  739. {
  740. DestPixelType* dest = getDestPixel (x);
  741. alphaLevel = (alphaLevel * extraAlpha) >> 8;
  742. x -= xOffset;
  743. jassert (repeatPattern || (x >= 0 && x + width <= srcData.width));
  744. if (alphaLevel < 0xfe)
  745. {
  746. do
  747. {
  748. dest->blend (*getSrcPixel (repeatPattern ? (x++ % srcData.width) : x++), (uint32) alphaLevel);
  749. incDestPixelPointer (dest);
  750. } while (--width > 0);
  751. }
  752. else
  753. {
  754. if (repeatPattern)
  755. {
  756. do
  757. {
  758. dest->blend (*getSrcPixel (x++ % srcData.width));
  759. incDestPixelPointer (dest);
  760. } while (--width > 0);
  761. }
  762. else
  763. {
  764. copyRow (dest, getSrcPixel (x), width);
  765. }
  766. }
  767. }
  768. void handleEdgeTableLineFull (int x, int width) const noexcept
  769. {
  770. DestPixelType* dest = getDestPixel (x);
  771. x -= xOffset;
  772. jassert (repeatPattern || (x >= 0 && x + width <= srcData.width));
  773. if (extraAlpha < 0xfe)
  774. {
  775. do
  776. {
  777. dest->blend (*getSrcPixel (repeatPattern ? (x++ % srcData.width) : x++), (uint32) extraAlpha);
  778. incDestPixelPointer (dest);
  779. } while (--width > 0);
  780. }
  781. else
  782. {
  783. if (repeatPattern)
  784. {
  785. do
  786. {
  787. dest->blend (*getSrcPixel (x++ % srcData.width));
  788. incDestPixelPointer (dest);
  789. } while (--width > 0);
  790. }
  791. else
  792. {
  793. copyRow (dest, getSrcPixel (x), width);
  794. }
  795. }
  796. }
  797. void clipEdgeTableLine (EdgeTable& et, int x, int y, int width)
  798. {
  799. jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width);
  800. SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset);
  801. uint8* mask = (uint8*) (s + x - xOffset);
  802. if (sizeof (SrcPixelType) == sizeof (PixelARGB))
  803. mask += PixelARGB::indexA;
  804. et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width);
  805. }
  806. private:
  807. const Image::BitmapData& destData;
  808. const Image::BitmapData& srcData;
  809. const int extraAlpha, xOffset, yOffset;
  810. DestPixelType* linePixels;
  811. SrcPixelType* sourceLineStart;
  812. forcedinline DestPixelType* getDestPixel (int const x) const noexcept
  813. {
  814. return addBytesToPointer (linePixels, x * destData.pixelStride);
  815. }
  816. forcedinline SrcPixelType const* getSrcPixel (int const x) const noexcept
  817. {
  818. return addBytesToPointer (sourceLineStart, x * srcData.pixelStride);
  819. }
  820. forcedinline void incDestPixelPointer (DestPixelType*& p) const noexcept
  821. {
  822. p = addBytesToPointer (p, destData.pixelStride);
  823. }
  824. forcedinline void copyRow (DestPixelType* dest, SrcPixelType const* src, int width) const noexcept
  825. {
  826. if (srcData.pixelStride == 3 && destData.pixelStride == 3)
  827. {
  828. memcpy (dest, src, sizeof (PixelRGB) * (size_t) width);
  829. }
  830. else
  831. {
  832. do
  833. {
  834. dest->blend (*src);
  835. incDestPixelPointer (dest);
  836. src = addBytesToPointer (src, srcData.pixelStride);
  837. } while (--width > 0);
  838. }
  839. }
  840. JUCE_DECLARE_NON_COPYABLE (ImageFill)
  841. };
  842. //==============================================================================
  843. /** Fills an edge-table with a transformed image. */
  844. template <class DestPixelType, class SrcPixelType, bool repeatPattern>
  845. class TransformedImageFill
  846. {
  847. public:
  848. TransformedImageFill (const Image::BitmapData& dest, const Image::BitmapData& src,
  849. const AffineTransform& transform, const int alpha, const bool higherQuality)
  850. : interpolator (transform,
  851. higherQuality ? 0.5f : 0.0f,
  852. higherQuality ? -128 : 0),
  853. destData (dest),
  854. srcData (src),
  855. extraAlpha (alpha + 1),
  856. betterQuality (higherQuality),
  857. maxX (src.width - 1),
  858. maxY (src.height - 1),
  859. scratchSize (2048)
  860. {
  861. scratchBuffer.malloc (scratchSize);
  862. }
  863. forcedinline void setEdgeTableYPos (const int newY) noexcept
  864. {
  865. y = newY;
  866. linePixels = (DestPixelType*) destData.getLinePointer (newY);
  867. }
  868. forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept
  869. {
  870. SrcPixelType p;
  871. generate (&p, x, 1);
  872. getDestPixel (x)->blend (p, (uint32) (alphaLevel * extraAlpha) >> 8);
  873. }
  874. forcedinline void handleEdgeTablePixelFull (const int x) noexcept
  875. {
  876. SrcPixelType p;
  877. generate (&p, x, 1);
  878. getDestPixel (x)->blend (p, (uint32) extraAlpha);
  879. }
  880. void handleEdgeTableLine (const int x, int width, int alphaLevel) noexcept
  881. {
  882. if (width > (int) scratchSize)
  883. {
  884. scratchSize = (size_t) width;
  885. scratchBuffer.malloc (scratchSize);
  886. }
  887. SrcPixelType* span = scratchBuffer;
  888. generate (span, x, width);
  889. DestPixelType* dest = getDestPixel (x);
  890. alphaLevel *= extraAlpha;
  891. alphaLevel >>= 8;
  892. if (alphaLevel < 0xfe)
  893. {
  894. do
  895. {
  896. dest->blend (*span++, (uint32) alphaLevel);
  897. incDestPixelPointer (dest);
  898. } while (--width > 0);
  899. }
  900. else
  901. {
  902. do
  903. {
  904. dest->blend (*span++);
  905. incDestPixelPointer (dest);
  906. } while (--width > 0);
  907. }
  908. }
  909. forcedinline void handleEdgeTableLineFull (const int x, int width) noexcept
  910. {
  911. handleEdgeTableLine (x, width, 255);
  912. }
  913. void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width)
  914. {
  915. if (width > (int) scratchSize)
  916. {
  917. scratchSize = (size_t) width;
  918. scratchBuffer.malloc (scratchSize);
  919. }
  920. y = y_;
  921. generate (scratchBuffer.getData(), x, width);
  922. et.clipLineToMask (x, y_,
  923. reinterpret_cast<uint8*> (scratchBuffer.getData()) + SrcPixelType::indexA,
  924. sizeof (SrcPixelType), width);
  925. }
  926. private:
  927. forcedinline DestPixelType* getDestPixel (const int x) const noexcept
  928. {
  929. return addBytesToPointer (linePixels, x * destData.pixelStride);
  930. }
  931. forcedinline void incDestPixelPointer (DestPixelType*& p) const noexcept
  932. {
  933. p = addBytesToPointer (p, destData.pixelStride);
  934. }
  935. //==============================================================================
  936. template <class PixelType>
  937. void generate (PixelType* dest, const int x, int numPixels) noexcept
  938. {
  939. this->interpolator.setStartOfLine ((float) x, (float) y, numPixels);
  940. do
  941. {
  942. int hiResX, hiResY;
  943. this->interpolator.next (hiResX, hiResY);
  944. int loResX = hiResX >> 8;
  945. int loResY = hiResY >> 8;
  946. if (repeatPattern)
  947. {
  948. loResX = negativeAwareModulo (loResX, srcData.width);
  949. loResY = negativeAwareModulo (loResY, srcData.height);
  950. }
  951. if (betterQuality)
  952. {
  953. if (isPositiveAndBelow (loResX, maxX))
  954. {
  955. if (isPositiveAndBelow (loResY, maxY))
  956. {
  957. // In the centre of the image..
  958. render4PixelAverage (dest, this->srcData.getPixelPointer (loResX, loResY),
  959. hiResX & 255, hiResY & 255);
  960. ++dest;
  961. continue;
  962. }
  963. else if (! repeatPattern)
  964. {
  965. // At a top or bottom edge..
  966. if (loResY < 0)
  967. render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, 0), hiResX & 255);
  968. else
  969. render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, maxY), hiResX & 255);
  970. ++dest;
  971. continue;
  972. }
  973. }
  974. else
  975. {
  976. if (isPositiveAndBelow (loResY, maxY) && ! repeatPattern)
  977. {
  978. // At a left or right hand edge..
  979. if (loResX < 0)
  980. render2PixelAverageY (dest, this->srcData.getPixelPointer (0, loResY), hiResY & 255);
  981. else
  982. render2PixelAverageY (dest, this->srcData.getPixelPointer (maxX, loResY), hiResY & 255);
  983. ++dest;
  984. continue;
  985. }
  986. }
  987. }
  988. if (! repeatPattern)
  989. {
  990. if (loResX < 0) loResX = 0;
  991. if (loResY < 0) loResY = 0;
  992. if (loResX > maxX) loResX = maxX;
  993. if (loResY > maxY) loResY = maxY;
  994. }
  995. dest->set (*(const PixelType*) this->srcData.getPixelPointer (loResX, loResY));
  996. ++dest;
  997. } while (--numPixels > 0);
  998. }
  999. //==============================================================================
  1000. void render4PixelAverage (PixelARGB* const dest, const uint8* src, const int subPixelX, const int subPixelY) noexcept
  1001. {
  1002. uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 };
  1003. uint32 weight = (uint32) ((256 - subPixelX) * (256 - subPixelY));
  1004. c[0] += weight * src[0];
  1005. c[1] += weight * src[1];
  1006. c[2] += weight * src[2];
  1007. c[3] += weight * src[3];
  1008. src += this->srcData.pixelStride;
  1009. weight = (uint32) (subPixelX * (256 - subPixelY));
  1010. c[0] += weight * src[0];
  1011. c[1] += weight * src[1];
  1012. c[2] += weight * src[2];
  1013. c[3] += weight * src[3];
  1014. src += this->srcData.lineStride;
  1015. weight = (uint32) (subPixelX * subPixelY);
  1016. c[0] += weight * src[0];
  1017. c[1] += weight * src[1];
  1018. c[2] += weight * src[2];
  1019. c[3] += weight * src[3];
  1020. src -= this->srcData.pixelStride;
  1021. weight = (uint32) ((256 - subPixelX) * subPixelY);
  1022. c[0] += weight * src[0];
  1023. c[1] += weight * src[1];
  1024. c[2] += weight * src[2];
  1025. c[3] += weight * src[3];
  1026. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16),
  1027. (uint8) (c[PixelARGB::indexR] >> 16),
  1028. (uint8) (c[PixelARGB::indexG] >> 16),
  1029. (uint8) (c[PixelARGB::indexB] >> 16));
  1030. }
  1031. void render2PixelAverageX (PixelARGB* const dest, const uint8* src, const uint32 subPixelX) noexcept
  1032. {
  1033. uint32 c[4] = { 128, 128, 128, 128 };
  1034. uint32 weight = 256 - subPixelX;
  1035. c[0] += weight * src[0];
  1036. c[1] += weight * src[1];
  1037. c[2] += weight * src[2];
  1038. c[3] += weight * src[3];
  1039. src += this->srcData.pixelStride;
  1040. weight = subPixelX;
  1041. c[0] += weight * src[0];
  1042. c[1] += weight * src[1];
  1043. c[2] += weight * src[2];
  1044. c[3] += weight * src[3];
  1045. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8),
  1046. (uint8) (c[PixelARGB::indexR] >> 8),
  1047. (uint8) (c[PixelARGB::indexG] >> 8),
  1048. (uint8) (c[PixelARGB::indexB] >> 8));
  1049. }
  1050. void render2PixelAverageY (PixelARGB* const dest, const uint8* src, const uint32 subPixelY) noexcept
  1051. {
  1052. uint32 c[4] = { 128, 128, 128, 128 };
  1053. uint32 weight = 256 - subPixelY;
  1054. c[0] += weight * src[0];
  1055. c[1] += weight * src[1];
  1056. c[2] += weight * src[2];
  1057. c[3] += weight * src[3];
  1058. src += this->srcData.lineStride;
  1059. weight = subPixelY;
  1060. c[0] += weight * src[0];
  1061. c[1] += weight * src[1];
  1062. c[2] += weight * src[2];
  1063. c[3] += weight * src[3];
  1064. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8),
  1065. (uint8) (c[PixelARGB::indexR] >> 8),
  1066. (uint8) (c[PixelARGB::indexG] >> 8),
  1067. (uint8) (c[PixelARGB::indexB] >> 8));
  1068. }
  1069. //==============================================================================
  1070. void render4PixelAverage (PixelRGB* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept
  1071. {
  1072. uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 };
  1073. uint32 weight = (256 - subPixelX) * (256 - subPixelY);
  1074. c[0] += weight * src[0];
  1075. c[1] += weight * src[1];
  1076. c[2] += weight * src[2];
  1077. src += this->srcData.pixelStride;
  1078. weight = subPixelX * (256 - subPixelY);
  1079. c[0] += weight * src[0];
  1080. c[1] += weight * src[1];
  1081. c[2] += weight * src[2];
  1082. src += this->srcData.lineStride;
  1083. weight = subPixelX * subPixelY;
  1084. c[0] += weight * src[0];
  1085. c[1] += weight * src[1];
  1086. c[2] += weight * src[2];
  1087. src -= this->srcData.pixelStride;
  1088. weight = (256 - subPixelX) * subPixelY;
  1089. c[0] += weight * src[0];
  1090. c[1] += weight * src[1];
  1091. c[2] += weight * src[2];
  1092. dest->setARGB ((uint8) 255,
  1093. (uint8) (c[PixelRGB::indexR] >> 16),
  1094. (uint8) (c[PixelRGB::indexG] >> 16),
  1095. (uint8) (c[PixelRGB::indexB] >> 16));
  1096. }
  1097. void render2PixelAverageX (PixelRGB* const dest, const uint8* src, const uint32 subPixelX) noexcept
  1098. {
  1099. uint32 c[3] = { 128, 128, 128 };
  1100. const uint32 weight = 256 - subPixelX;
  1101. c[0] += weight * src[0];
  1102. c[1] += weight * src[1];
  1103. c[2] += weight * src[2];
  1104. src += this->srcData.pixelStride;
  1105. c[0] += subPixelX * src[0];
  1106. c[1] += subPixelX * src[1];
  1107. c[2] += subPixelX * src[2];
  1108. dest->setARGB ((uint8) 255,
  1109. (uint8) (c[PixelRGB::indexR] >> 8),
  1110. (uint8) (c[PixelRGB::indexG] >> 8),
  1111. (uint8) (c[PixelRGB::indexB] >> 8));
  1112. }
  1113. void render2PixelAverageY (PixelRGB* const dest, const uint8* src, const uint32 subPixelY) noexcept
  1114. {
  1115. uint32 c[3] = { 128, 128, 128 };
  1116. const uint32 weight = 256 - subPixelY;
  1117. c[0] += weight * src[0];
  1118. c[1] += weight * src[1];
  1119. c[2] += weight * src[2];
  1120. src += this->srcData.lineStride;
  1121. c[0] += subPixelY * src[0];
  1122. c[1] += subPixelY * src[1];
  1123. c[2] += subPixelY * src[2];
  1124. dest->setARGB ((uint8) 255,
  1125. (uint8) (c[PixelRGB::indexR] >> 8),
  1126. (uint8) (c[PixelRGB::indexG] >> 8),
  1127. (uint8) (c[PixelRGB::indexB] >> 8));
  1128. }
  1129. //==============================================================================
  1130. void render4PixelAverage (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept
  1131. {
  1132. uint32 c = 256 * 128;
  1133. c += src[0] * ((256 - subPixelX) * (256 - subPixelY));
  1134. src += this->srcData.pixelStride;
  1135. c += src[1] * (subPixelX * (256 - subPixelY));
  1136. src += this->srcData.lineStride;
  1137. c += src[1] * (subPixelX * subPixelY);
  1138. src -= this->srcData.pixelStride;
  1139. c += src[0] * ((256 - subPixelX) * subPixelY);
  1140. *((uint8*) dest) = (uint8) (c >> 16);
  1141. }
  1142. void render2PixelAverageX (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX) noexcept
  1143. {
  1144. uint32 c = 128;
  1145. c += src[0] * (256 - subPixelX);
  1146. src += this->srcData.pixelStride;
  1147. c += src[0] * subPixelX;
  1148. *((uint8*) dest) = (uint8) (c >> 8);
  1149. }
  1150. void render2PixelAverageY (PixelAlpha* const dest, const uint8* src, const uint32 subPixelY) noexcept
  1151. {
  1152. uint32 c = 128;
  1153. c += src[0] * (256 - subPixelY);
  1154. src += this->srcData.lineStride;
  1155. c += src[0] * subPixelY;
  1156. *((uint8*) dest) = (uint8) (c >> 8);
  1157. }
  1158. //==============================================================================
  1159. class TransformedImageSpanInterpolator
  1160. {
  1161. public:
  1162. TransformedImageSpanInterpolator (const AffineTransform& transform,
  1163. const float offsetFloat, const int offsetInt) noexcept
  1164. : inverseTransform (transform.inverted()),
  1165. pixelOffset (offsetFloat), pixelOffsetInt (offsetInt)
  1166. {}
  1167. void setStartOfLine (float sx, float sy, const int numPixels) noexcept
  1168. {
  1169. jassert (numPixels > 0);
  1170. sx += pixelOffset;
  1171. sy += pixelOffset;
  1172. float x1 = sx, y1 = sy;
  1173. sx += numPixels;
  1174. inverseTransform.transformPoints (x1, y1, sx, sy);
  1175. xBresenham.set ((int) (x1 * 256.0f), (int) (sx * 256.0f), numPixels, pixelOffsetInt);
  1176. yBresenham.set ((int) (y1 * 256.0f), (int) (sy * 256.0f), numPixels, pixelOffsetInt);
  1177. }
  1178. void next (int& px, int& py) noexcept
  1179. {
  1180. px = xBresenham.n; xBresenham.stepToNext();
  1181. py = yBresenham.n; yBresenham.stepToNext();
  1182. }
  1183. private:
  1184. class BresenhamInterpolator
  1185. {
  1186. public:
  1187. BresenhamInterpolator() noexcept {}
  1188. void set (const int n1, const int n2, const int numSteps_, const int offsetInt) noexcept
  1189. {
  1190. numSteps = numSteps_;
  1191. step = (n2 - n1) / numSteps;
  1192. remainder = modulo = (n2 - n1) % numSteps;
  1193. n = n1 + offsetInt;
  1194. if (modulo <= 0)
  1195. {
  1196. modulo += numSteps;
  1197. remainder += numSteps;
  1198. --step;
  1199. }
  1200. modulo -= numSteps;
  1201. }
  1202. forcedinline void stepToNext() noexcept
  1203. {
  1204. modulo += remainder;
  1205. n += step;
  1206. if (modulo > 0)
  1207. {
  1208. modulo -= numSteps;
  1209. ++n;
  1210. }
  1211. }
  1212. int n;
  1213. private:
  1214. int numSteps, step, modulo, remainder;
  1215. };
  1216. const AffineTransform inverseTransform;
  1217. BresenhamInterpolator xBresenham, yBresenham;
  1218. const float pixelOffset;
  1219. const int pixelOffsetInt;
  1220. JUCE_DECLARE_NON_COPYABLE (TransformedImageSpanInterpolator)
  1221. };
  1222. //==============================================================================
  1223. TransformedImageSpanInterpolator interpolator;
  1224. const Image::BitmapData& destData;
  1225. const Image::BitmapData& srcData;
  1226. const int extraAlpha;
  1227. const bool betterQuality;
  1228. const int maxX, maxY;
  1229. int y;
  1230. DestPixelType* linePixels;
  1231. HeapBlock <SrcPixelType> scratchBuffer;
  1232. size_t scratchSize;
  1233. JUCE_DECLARE_NON_COPYABLE (TransformedImageFill)
  1234. };
  1235. //==============================================================================
  1236. template <class Iterator>
  1237. void renderImageTransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData,
  1238. const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill)
  1239. {
  1240. switch (destData.pixelFormat)
  1241. {
  1242. case Image::ARGB:
  1243. switch (srcData.pixelFormat)
  1244. {
  1245. case Image::ARGB:
  1246. if (tiledFill) { TransformedImageFill <PixelARGB, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1247. else { TransformedImageFill <PixelARGB, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1248. break;
  1249. case Image::RGB:
  1250. if (tiledFill) { TransformedImageFill <PixelARGB, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1251. else { TransformedImageFill <PixelARGB, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1252. break;
  1253. default:
  1254. if (tiledFill) { TransformedImageFill <PixelARGB, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1255. else { TransformedImageFill <PixelARGB, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1256. break;
  1257. }
  1258. break;
  1259. case Image::RGB:
  1260. switch (srcData.pixelFormat)
  1261. {
  1262. case Image::ARGB:
  1263. if (tiledFill) { TransformedImageFill <PixelRGB, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1264. else { TransformedImageFill <PixelRGB, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1265. break;
  1266. case Image::RGB:
  1267. if (tiledFill) { TransformedImageFill <PixelRGB, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1268. else { TransformedImageFill <PixelRGB, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1269. break;
  1270. default:
  1271. if (tiledFill) { TransformedImageFill <PixelRGB, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1272. else { TransformedImageFill <PixelRGB, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1273. break;
  1274. }
  1275. break;
  1276. default:
  1277. switch (srcData.pixelFormat)
  1278. {
  1279. case Image::ARGB:
  1280. if (tiledFill) { TransformedImageFill <PixelAlpha, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1281. else { TransformedImageFill <PixelAlpha, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1282. break;
  1283. case Image::RGB:
  1284. if (tiledFill) { TransformedImageFill <PixelAlpha, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1285. else { TransformedImageFill <PixelAlpha, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1286. break;
  1287. default:
  1288. if (tiledFill) { TransformedImageFill <PixelAlpha, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1289. else { TransformedImageFill <PixelAlpha, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  1290. break;
  1291. }
  1292. break;
  1293. }
  1294. }
  1295. template <class Iterator>
  1296. void renderImageUntransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill)
  1297. {
  1298. switch (destData.pixelFormat)
  1299. {
  1300. case Image::ARGB:
  1301. switch (srcData.pixelFormat)
  1302. {
  1303. case Image::ARGB:
  1304. if (tiledFill) { ImageFill <PixelARGB, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1305. else { ImageFill <PixelARGB, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1306. break;
  1307. case Image::RGB:
  1308. if (tiledFill) { ImageFill <PixelARGB, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1309. else { ImageFill <PixelARGB, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1310. break;
  1311. default:
  1312. if (tiledFill) { ImageFill <PixelARGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1313. else { ImageFill <PixelARGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1314. break;
  1315. }
  1316. break;
  1317. case Image::RGB:
  1318. switch (srcData.pixelFormat)
  1319. {
  1320. case Image::ARGB:
  1321. if (tiledFill) { ImageFill <PixelRGB, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1322. else { ImageFill <PixelRGB, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1323. break;
  1324. case Image::RGB:
  1325. if (tiledFill) { ImageFill <PixelRGB, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1326. else { ImageFill <PixelRGB, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1327. break;
  1328. default:
  1329. if (tiledFill) { ImageFill <PixelRGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1330. else { ImageFill <PixelRGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1331. break;
  1332. }
  1333. break;
  1334. default:
  1335. switch (srcData.pixelFormat)
  1336. {
  1337. case Image::ARGB:
  1338. if (tiledFill) { ImageFill <PixelAlpha, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1339. else { ImageFill <PixelAlpha, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1340. break;
  1341. case Image::RGB:
  1342. if (tiledFill) { ImageFill <PixelAlpha, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1343. else { ImageFill <PixelAlpha, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1344. break;
  1345. default:
  1346. if (tiledFill) { ImageFill <PixelAlpha, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1347. else { ImageFill <PixelAlpha, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  1348. break;
  1349. }
  1350. break;
  1351. }
  1352. }
  1353. template <class Iterator, class DestPixelType>
  1354. void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*)
  1355. {
  1356. if (replaceContents)
  1357. {
  1358. EdgeTableFillers::SolidColour <DestPixelType, true> r (destData, fillColour);
  1359. iter.iterate (r);
  1360. }
  1361. else
  1362. {
  1363. EdgeTableFillers::SolidColour <DestPixelType, false> r (destData, fillColour);
  1364. iter.iterate (r);
  1365. }
  1366. }
  1367. template <class Iterator, class DestPixelType>
  1368. void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform,
  1369. const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*)
  1370. {
  1371. if (g.isRadial)
  1372. {
  1373. if (isIdentity)
  1374. {
  1375. EdgeTableFillers::Gradient <DestPixelType, GradientPixelIterators::Radial> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1376. iter.iterate (renderer);
  1377. }
  1378. else
  1379. {
  1380. EdgeTableFillers::Gradient <DestPixelType, GradientPixelIterators::TransformedRadial> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1381. iter.iterate (renderer);
  1382. }
  1383. }
  1384. else
  1385. {
  1386. EdgeTableFillers::Gradient <DestPixelType, GradientPixelIterators::Linear> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1387. iter.iterate (renderer);
  1388. }
  1389. }
  1390. }
  1391. //==============================================================================
  1392. namespace ClipRegions
  1393. {
  1394. class Base : public SingleThreadedReferenceCountedObject
  1395. {
  1396. public:
  1397. Base() {}
  1398. virtual ~Base() {}
  1399. typedef ReferenceCountedObjectPtr<Base> Ptr;
  1400. virtual Ptr clone() const = 0;
  1401. virtual Ptr applyClipTo (const Ptr& target) const = 0;
  1402. virtual Ptr clipToRectangle (const Rectangle<int>&) = 0;
  1403. virtual Ptr clipToRectangleList (const RectangleList&) = 0;
  1404. virtual Ptr excludeClipRectangle (const Rectangle<int>&) = 0;
  1405. virtual Ptr clipToPath (const Path&, const AffineTransform&) = 0;
  1406. virtual Ptr clipToEdgeTable (const EdgeTable& et) = 0;
  1407. virtual Ptr clipToImageAlpha (const Image&, const AffineTransform&, const bool betterQuality) = 0;
  1408. virtual void translate (const Point<int>& delta) = 0;
  1409. virtual bool clipRegionIntersects (const Rectangle<int>&) const = 0;
  1410. virtual Rectangle<int> getClipBounds() const = 0;
  1411. virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>&, const PixelARGB& colour, bool replaceContents) const = 0;
  1412. virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>&, const PixelARGB& colour) const = 0;
  1413. virtual void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const = 0;
  1414. virtual void fillAllWithGradient (Image::BitmapData& destData, ColourGradient&, const AffineTransform&, bool isIdentity) const = 0;
  1415. virtual void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform&, bool betterQuality, bool tiledFill) const = 0;
  1416. virtual void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const = 0;
  1417. };
  1418. //==============================================================================
  1419. class EdgeTableRegion : public Base
  1420. {
  1421. public:
  1422. EdgeTableRegion (const EdgeTable& e) : edgeTable (e) {}
  1423. EdgeTableRegion (const Rectangle<int>& r) : edgeTable (r) {}
  1424. EdgeTableRegion (const Rectangle<float>& r) : edgeTable (r) {}
  1425. EdgeTableRegion (const RectangleList& r) : edgeTable (r) {}
  1426. EdgeTableRegion (const Rectangle<int>& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {}
  1427. EdgeTableRegion (const EdgeTableRegion& other) : edgeTable (other.edgeTable) {}
  1428. Ptr clone() const { return new EdgeTableRegion (*this); }
  1429. Ptr applyClipTo (const Ptr& target) const { return target->clipToEdgeTable (edgeTable); }
  1430. Ptr clipToRectangle (const Rectangle<int>& r)
  1431. {
  1432. edgeTable.clipToRectangle (r);
  1433. return edgeTable.isEmpty() ? nullptr : this;
  1434. }
  1435. Ptr clipToRectangleList (const RectangleList& r)
  1436. {
  1437. RectangleList inverse (edgeTable.getMaximumBounds());
  1438. if (inverse.subtract (r))
  1439. for (const Rectangle<int>* i = inverse.begin(), * const e = inverse.end(); i != e; ++i)
  1440. edgeTable.excludeRectangle (*i);
  1441. return edgeTable.isEmpty() ? nullptr : this;
  1442. }
  1443. Ptr excludeClipRectangle (const Rectangle<int>& r)
  1444. {
  1445. edgeTable.excludeRectangle (r);
  1446. return edgeTable.isEmpty() ? nullptr : this;
  1447. }
  1448. Ptr clipToPath (const Path& p, const AffineTransform& transform)
  1449. {
  1450. EdgeTable et (edgeTable.getMaximumBounds(), p, transform);
  1451. edgeTable.clipToEdgeTable (et);
  1452. return edgeTable.isEmpty() ? nullptr : this;
  1453. }
  1454. Ptr clipToEdgeTable (const EdgeTable& et)
  1455. {
  1456. edgeTable.clipToEdgeTable (et);
  1457. return edgeTable.isEmpty() ? nullptr : this;
  1458. }
  1459. Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality)
  1460. {
  1461. const Image::BitmapData srcData (image, Image::BitmapData::readOnly);
  1462. if (transform.isOnlyTranslation())
  1463. {
  1464. // If our translation doesn't involve any distortion, just use a simple blit..
  1465. const int tx = (int) (transform.getTranslationX() * 256.0f);
  1466. const int ty = (int) (transform.getTranslationY() * 256.0f);
  1467. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  1468. {
  1469. const int imageX = ((tx + 128) >> 8);
  1470. const int imageY = ((ty + 128) >> 8);
  1471. if (image.getFormat() == Image::ARGB)
  1472. straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0);
  1473. else
  1474. straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0);
  1475. return edgeTable.isEmpty() ? nullptr : this;
  1476. }
  1477. }
  1478. if (transform.isSingularity())
  1479. return Ptr();
  1480. {
  1481. Path p;
  1482. p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height);
  1483. EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform);
  1484. edgeTable.clipToEdgeTable (et2);
  1485. }
  1486. if (! edgeTable.isEmpty())
  1487. {
  1488. if (image.getFormat() == Image::ARGB)
  1489. transformedClipImage (srcData, transform, betterQuality, (PixelARGB*) 0);
  1490. else
  1491. transformedClipImage (srcData, transform, betterQuality, (PixelAlpha*) 0);
  1492. }
  1493. return edgeTable.isEmpty() ? nullptr : this;
  1494. }
  1495. void translate (const Point<int>& delta)
  1496. {
  1497. edgeTable.translate ((float) delta.x, delta.y);
  1498. }
  1499. bool clipRegionIntersects (const Rectangle<int>& r) const
  1500. {
  1501. return edgeTable.getMaximumBounds().intersects (r);
  1502. }
  1503. Rectangle<int> getClipBounds() const
  1504. {
  1505. return edgeTable.getMaximumBounds();
  1506. }
  1507. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB& colour, bool replaceContents) const
  1508. {
  1509. const Rectangle<int> totalClip (edgeTable.getMaximumBounds());
  1510. const Rectangle<int> clipped (totalClip.getIntersection (area));
  1511. if (! clipped.isEmpty())
  1512. {
  1513. EdgeTableRegion et (clipped);
  1514. et.edgeTable.clipToEdgeTable (edgeTable);
  1515. et.fillAllWithColour (destData, colour, replaceContents);
  1516. }
  1517. }
  1518. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>& area, const PixelARGB& colour) const
  1519. {
  1520. const Rectangle<float> totalClip (edgeTable.getMaximumBounds().toFloat());
  1521. const Rectangle<float> clipped (totalClip.getIntersection (area));
  1522. if (! clipped.isEmpty())
  1523. {
  1524. EdgeTableRegion et (clipped);
  1525. et.edgeTable.clipToEdgeTable (edgeTable);
  1526. et.fillAllWithColour (destData, colour, false);
  1527. }
  1528. }
  1529. void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const
  1530. {
  1531. switch (destData.pixelFormat)
  1532. {
  1533. case Image::ARGB: EdgeTableFillers::renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1534. case Image::RGB: EdgeTableFillers::renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1535. default: EdgeTableFillers::renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1536. }
  1537. }
  1538. void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const
  1539. {
  1540. HeapBlock <PixelARGB> lookupTable;
  1541. const int numLookupEntries = gradient.createLookupTable (transform, lookupTable);
  1542. jassert (numLookupEntries > 0);
  1543. switch (destData.pixelFormat)
  1544. {
  1545. case Image::ARGB: EdgeTableFillers::renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
  1546. case Image::RGB: EdgeTableFillers::renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
  1547. default: EdgeTableFillers::renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
  1548. }
  1549. }
  1550. void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const
  1551. {
  1552. EdgeTableFillers::renderImageTransformed (edgeTable, destData, srcData, alpha, transform, betterQuality, tiledFill);
  1553. }
  1554. void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const
  1555. {
  1556. EdgeTableFillers::renderImageUntransformed (edgeTable, destData, srcData, alpha, x, y, tiledFill);
  1557. }
  1558. EdgeTable edgeTable;
  1559. private:
  1560. //==============================================================================
  1561. template <class SrcPixelType>
  1562. void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType*)
  1563. {
  1564. EdgeTableFillers::TransformedImageFill <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, transform, 255, betterQuality);
  1565. for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y)
  1566. renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(),
  1567. edgeTable.getMaximumBounds().getWidth());
  1568. }
  1569. template <class SrcPixelType>
  1570. void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*)
  1571. {
  1572. Rectangle<int> r (imageX, imageY, srcData.width, srcData.height);
  1573. edgeTable.clipToRectangle (r);
  1574. EdgeTableFillers::ImageFill <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, 255, imageX, imageY);
  1575. for (int y = 0; y < r.getHeight(); ++y)
  1576. renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth());
  1577. }
  1578. EdgeTableRegion& operator= (const EdgeTableRegion&);
  1579. };
  1580. //==============================================================================
  1581. class RectangleListRegion : public Base
  1582. {
  1583. public:
  1584. RectangleListRegion (const Rectangle<int>& r) : clip (r) {}
  1585. RectangleListRegion (const RectangleList& r) : clip (r) {}
  1586. RectangleListRegion (const RectangleListRegion& other) : clip (other.clip) {}
  1587. Ptr clone() const { return new RectangleListRegion (*this); }
  1588. Ptr applyClipTo (const Ptr& target) const { return target->clipToRectangleList (clip); }
  1589. Ptr clipToRectangle (const Rectangle<int>& r)
  1590. {
  1591. clip.clipTo (r);
  1592. return clip.isEmpty() ? nullptr : this;
  1593. }
  1594. Ptr clipToRectangleList (const RectangleList& r)
  1595. {
  1596. clip.clipTo (r);
  1597. return clip.isEmpty() ? nullptr : this;
  1598. }
  1599. Ptr excludeClipRectangle (const Rectangle<int>& r)
  1600. {
  1601. clip.subtract (r);
  1602. return clip.isEmpty() ? nullptr : this;
  1603. }
  1604. Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toEdgeTable()->clipToPath (p, transform); }
  1605. Ptr clipToEdgeTable (const EdgeTable& et) { return toEdgeTable()->clipToEdgeTable (et); }
  1606. Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality)
  1607. {
  1608. return toEdgeTable()->clipToImageAlpha (image, transform, betterQuality);
  1609. }
  1610. void translate (const Point<int>& delta)
  1611. {
  1612. clip.offsetAll (delta.x, delta.y);
  1613. }
  1614. bool clipRegionIntersects (const Rectangle<int>& r) const { return clip.intersects (r); }
  1615. Rectangle<int> getClipBounds() const { return clip.getBounds(); }
  1616. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB& colour, bool replaceContents) const
  1617. {
  1618. SubRectangleIterator iter (clip, area);
  1619. switch (destData.pixelFormat)
  1620. {
  1621. case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1622. case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1623. default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1624. }
  1625. }
  1626. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>& area, const PixelARGB& colour) const
  1627. {
  1628. SubRectangleIteratorFloat iter (clip, area);
  1629. switch (destData.pixelFormat)
  1630. {
  1631. case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, false, (PixelARGB*) 0); break;
  1632. case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, false, (PixelRGB*) 0); break;
  1633. default: EdgeTableFillers::renderSolidFill (iter, destData, colour, false, (PixelAlpha*) 0); break;
  1634. }
  1635. }
  1636. void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const
  1637. {
  1638. switch (destData.pixelFormat)
  1639. {
  1640. case Image::ARGB: EdgeTableFillers::renderSolidFill (*this, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1641. case Image::RGB: EdgeTableFillers::renderSolidFill (*this, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1642. default: EdgeTableFillers::renderSolidFill (*this, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1643. }
  1644. }
  1645. void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const
  1646. {
  1647. HeapBlock <PixelARGB> lookupTable;
  1648. const int numLookupEntries = gradient.createLookupTable (transform, lookupTable);
  1649. jassert (numLookupEntries > 0);
  1650. switch (destData.pixelFormat)
  1651. {
  1652. case Image::ARGB: EdgeTableFillers::renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
  1653. case Image::RGB: EdgeTableFillers::renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
  1654. default: EdgeTableFillers::renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
  1655. }
  1656. }
  1657. void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const
  1658. {
  1659. EdgeTableFillers::renderImageTransformed (*this, destData, srcData, alpha, transform, betterQuality, tiledFill);
  1660. }
  1661. void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const
  1662. {
  1663. EdgeTableFillers::renderImageUntransformed (*this, destData, srcData, alpha, x, y, tiledFill);
  1664. }
  1665. RectangleList clip;
  1666. //==============================================================================
  1667. template <class Renderer>
  1668. void iterate (Renderer& r) const noexcept
  1669. {
  1670. for (const Rectangle<int>* i = clip.begin(), * const e = clip.end(); i != e; ++i)
  1671. {
  1672. const int x = i->getX();
  1673. const int w = i->getWidth();
  1674. jassert (w > 0);
  1675. const int bottom = i->getBottom();
  1676. for (int y = i->getY(); y < bottom; ++y)
  1677. {
  1678. r.setEdgeTableYPos (y);
  1679. r.handleEdgeTableLineFull (x, w);
  1680. }
  1681. }
  1682. }
  1683. private:
  1684. //==============================================================================
  1685. class SubRectangleIterator
  1686. {
  1687. public:
  1688. SubRectangleIterator (const RectangleList& clipList, const Rectangle<int>& clipBounds)
  1689. : clip (clipList), area (clipBounds)
  1690. {}
  1691. template <class Renderer>
  1692. void iterate (Renderer& r) const noexcept
  1693. {
  1694. for (const Rectangle<int>* i = clip.begin(), * const e = clip.end(); i != e; ++i)
  1695. {
  1696. const Rectangle<int> rect (i->getIntersection (area));
  1697. if (! rect.isEmpty())
  1698. {
  1699. const int x = rect.getX();
  1700. const int w = rect.getWidth();
  1701. const int bottom = rect.getBottom();
  1702. for (int y = rect.getY(); y < bottom; ++y)
  1703. {
  1704. r.setEdgeTableYPos (y);
  1705. r.handleEdgeTableLineFull (x, w);
  1706. }
  1707. }
  1708. }
  1709. }
  1710. private:
  1711. const RectangleList& clip;
  1712. const Rectangle<int> area;
  1713. JUCE_DECLARE_NON_COPYABLE (SubRectangleIterator)
  1714. };
  1715. //==============================================================================
  1716. class SubRectangleIteratorFloat
  1717. {
  1718. public:
  1719. SubRectangleIteratorFloat (const RectangleList& clipList, const Rectangle<float>& clipBounds) noexcept
  1720. : clip (clipList), area (clipBounds)
  1721. {
  1722. }
  1723. template <class Renderer>
  1724. void iterate (Renderer& r) const noexcept
  1725. {
  1726. const RenderingHelpers::FloatRectangleRasterisingInfo f (area);
  1727. for (const Rectangle<int>* i = clip.begin(), * const e = clip.end(); i != e; ++i)
  1728. {
  1729. const int clipLeft = i->getX();
  1730. const int clipRight = i->getRight();
  1731. const int clipTop = i->getY();
  1732. const int clipBottom = i->getBottom();
  1733. if (f.totalBottom > clipTop && f.totalTop < clipBottom && f.totalRight > clipLeft && f.totalLeft < clipRight)
  1734. {
  1735. if (f.isOnePixelWide())
  1736. {
  1737. if (f.topAlpha != 0 && f.totalTop >= clipTop)
  1738. {
  1739. r.setEdgeTableYPos (f.totalTop);
  1740. r.handleEdgeTablePixel (f.left, f.topAlpha);
  1741. }
  1742. const int endY = jmin (f.bottom, clipBottom);
  1743. for (int y = jmax (clipTop, f.top); y < endY; ++y)
  1744. {
  1745. r.setEdgeTableYPos (y);
  1746. r.handleEdgeTablePixelFull (f.left);
  1747. }
  1748. if (f.bottomAlpha != 0 && f.bottom < clipBottom)
  1749. {
  1750. r.setEdgeTableYPos (f.bottom);
  1751. r.handleEdgeTablePixel (f.left, f.bottomAlpha);
  1752. }
  1753. }
  1754. else
  1755. {
  1756. const int clippedLeft = jmax (f.left, clipLeft);
  1757. const int clippedWidth = jmin (f.right, clipRight) - clippedLeft;
  1758. const bool doLeftAlpha = f.leftAlpha != 0 && f.totalLeft >= clipLeft;
  1759. const bool doRightAlpha = f.rightAlpha != 0 && f.right < clipRight;
  1760. if (f.topAlpha != 0 && f.totalTop >= clipTop)
  1761. {
  1762. r.setEdgeTableYPos (f.totalTop);
  1763. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getTopLeftCornerAlpha());
  1764. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.topAlpha);
  1765. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getTopRightCornerAlpha());
  1766. }
  1767. const int endY = jmin (f.bottom, clipBottom);
  1768. for (int y = jmax (clipTop, f.top); y < endY; ++y)
  1769. {
  1770. r.setEdgeTableYPos (y);
  1771. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.leftAlpha);
  1772. if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth);
  1773. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.rightAlpha);
  1774. }
  1775. if (f.bottomAlpha != 0 && f.bottom < clipBottom)
  1776. {
  1777. r.setEdgeTableYPos (f.bottom);
  1778. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getBottomLeftCornerAlpha());
  1779. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.bottomAlpha);
  1780. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getBottomRightCornerAlpha());
  1781. }
  1782. }
  1783. }
  1784. }
  1785. }
  1786. private:
  1787. const RectangleList& clip;
  1788. const Rectangle<float>& area;
  1789. JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat)
  1790. };
  1791. inline Ptr toEdgeTable() const { return new EdgeTableRegion (clip); }
  1792. RectangleListRegion& operator= (const RectangleListRegion&);
  1793. };
  1794. }
  1795. //==============================================================================
  1796. class SoftwareRendererSavedState
  1797. {
  1798. public:
  1799. SoftwareRendererSavedState (const Image& im, const Rectangle<int>& clipBounds)
  1800. : image (im), clip (new ClipRegions::RectangleListRegion (clipBounds)),
  1801. transform (0, 0),
  1802. interpolationQuality (Graphics::mediumResamplingQuality),
  1803. transparencyLayerAlpha (1.0f)
  1804. {
  1805. }
  1806. SoftwareRendererSavedState (const Image& im, const RectangleList& clipList, const int x, const int y)
  1807. : image (im), clip (new ClipRegions::RectangleListRegion (clipList)),
  1808. transform (x, y),
  1809. interpolationQuality (Graphics::mediumResamplingQuality),
  1810. transparencyLayerAlpha (1.0f)
  1811. {
  1812. }
  1813. SoftwareRendererSavedState (const SoftwareRendererSavedState& other)
  1814. : image (other.image), clip (other.clip), transform (other.transform),
  1815. font (other.font), fillType (other.fillType),
  1816. interpolationQuality (other.interpolationQuality),
  1817. transparencyLayerAlpha (other.transparencyLayerAlpha)
  1818. {
  1819. }
  1820. bool clipToRectangle (const Rectangle<int>& r)
  1821. {
  1822. if (clip != nullptr)
  1823. {
  1824. if (transform.isOnlyTranslated)
  1825. {
  1826. cloneClipIfMultiplyReferenced();
  1827. clip = clip->clipToRectangle (transform.translated (r));
  1828. }
  1829. else if (transform.isIntegerScaling)
  1830. {
  1831. cloneClipIfMultiplyReferenced();
  1832. clip = clip->clipToRectangle (transform.transformed (r).getSmallestIntegerContainer());
  1833. }
  1834. else
  1835. {
  1836. Path p;
  1837. p.addRectangle (r);
  1838. clipToPath (p, AffineTransform::identity);
  1839. }
  1840. }
  1841. return clip != nullptr;
  1842. }
  1843. bool clipToRectangleList (const RectangleList& r)
  1844. {
  1845. if (clip != nullptr)
  1846. {
  1847. if (transform.isOnlyTranslated)
  1848. {
  1849. cloneClipIfMultiplyReferenced();
  1850. RectangleList offsetList (r);
  1851. offsetList.offsetAll (transform.xOffset, transform.yOffset);
  1852. clip = clip->clipToRectangleList (offsetList);
  1853. }
  1854. else if (transform.isIntegerScaling)
  1855. {
  1856. cloneClipIfMultiplyReferenced();
  1857. RectangleList scaledList;
  1858. for (const Rectangle<int>* i = r.begin(), * const e = r.end(); i != e; ++i)
  1859. scaledList.add (transform.transformed (*i).getSmallestIntegerContainer());
  1860. clip = clip->clipToRectangleList (scaledList);
  1861. }
  1862. else
  1863. {
  1864. clipToPath (r.toPath(), AffineTransform::identity);
  1865. }
  1866. }
  1867. return clip != nullptr;
  1868. }
  1869. bool excludeClipRectangle (const Rectangle<int>& r)
  1870. {
  1871. if (clip != nullptr)
  1872. {
  1873. cloneClipIfMultiplyReferenced();
  1874. if (transform.isOnlyTranslated)
  1875. {
  1876. clip = clip->excludeClipRectangle (transform.translated (r));
  1877. }
  1878. else if (transform.isIntegerScaling)
  1879. {
  1880. clip = clip->excludeClipRectangle (transform.transformed (r).getSmallestIntegerContainer());
  1881. }
  1882. else
  1883. {
  1884. Path p;
  1885. p.addRectangle (r.toFloat());
  1886. p.applyTransform (transform.complexTransform);
  1887. p.addRectangle (clip->getClipBounds().toFloat());
  1888. p.setUsingNonZeroWinding (false);
  1889. clip = clip->clipToPath (p, AffineTransform::identity);
  1890. }
  1891. }
  1892. return clip != nullptr;
  1893. }
  1894. void clipToPath (const Path& p, const AffineTransform& t)
  1895. {
  1896. if (clip != nullptr)
  1897. {
  1898. cloneClipIfMultiplyReferenced();
  1899. clip = clip->clipToPath (p, transform.getTransformWith (t));
  1900. }
  1901. }
  1902. void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t)
  1903. {
  1904. if (clip != nullptr)
  1905. {
  1906. if (sourceImage.hasAlphaChannel())
  1907. {
  1908. cloneClipIfMultiplyReferenced();
  1909. clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t),
  1910. interpolationQuality != Graphics::lowResamplingQuality);
  1911. }
  1912. else
  1913. {
  1914. Path p;
  1915. p.addRectangle (sourceImage.getBounds());
  1916. clipToPath (p, t);
  1917. }
  1918. }
  1919. }
  1920. bool clipRegionIntersects (const Rectangle<int>& r) const
  1921. {
  1922. if (clip != nullptr)
  1923. {
  1924. if (transform.isOnlyTranslated)
  1925. return clip->clipRegionIntersects (transform.translated (r));
  1926. else
  1927. return getClipBounds().intersects (r);
  1928. }
  1929. return false;
  1930. }
  1931. Rectangle<int> getClipBounds() const
  1932. {
  1933. return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds())
  1934. : Rectangle<int>();
  1935. }
  1936. SoftwareRendererSavedState* beginTransparencyLayer (float opacity)
  1937. {
  1938. SoftwareRendererSavedState* s = new SoftwareRendererSavedState (*this);
  1939. if (clip != nullptr)
  1940. {
  1941. const Rectangle<int> layerBounds (clip->getClipBounds());
  1942. s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true);
  1943. s->transparencyLayerAlpha = opacity;
  1944. s->transform.moveOriginInDeviceSpace (-layerBounds.getX(), -layerBounds.getY());
  1945. s->cloneClipIfMultiplyReferenced();
  1946. s->clip->translate (-layerBounds.getPosition());
  1947. }
  1948. return s;
  1949. }
  1950. void endTransparencyLayer (SoftwareRendererSavedState& finishedLayerState)
  1951. {
  1952. if (clip != nullptr)
  1953. {
  1954. const Rectangle<int> layerBounds (clip->getClipBounds());
  1955. const ScopedPointer<LowLevelGraphicsContext> g (image.createLowLevelContext());
  1956. g->setOpacity (finishedLayerState.transparencyLayerAlpha);
  1957. g->drawImage (finishedLayerState.image, AffineTransform::translation ((float) layerBounds.getX(),
  1958. (float) layerBounds.getY()));
  1959. }
  1960. }
  1961. //==============================================================================
  1962. void fillTargetRect (const Rectangle<int>& r, const bool replaceContents)
  1963. {
  1964. if (fillType.isColour())
  1965. {
  1966. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1967. clip->fillRectWithColour (destData, r, fillType.colour.getPixelARGB(), replaceContents);
  1968. }
  1969. else
  1970. {
  1971. const Rectangle<int> clipped (clip->getClipBounds().getIntersection (r));
  1972. if (! clipped.isEmpty())
  1973. fillShape (new ClipRegions::RectangleListRegion (clipped), false);
  1974. }
  1975. }
  1976. void fillTargetRect (const Rectangle<float>& r)
  1977. {
  1978. if (fillType.isColour())
  1979. {
  1980. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1981. clip->fillRectWithColour (destData, r, fillType.colour.getPixelARGB());
  1982. }
  1983. else
  1984. {
  1985. const Rectangle<float> clipped (clip->getClipBounds().toFloat().getIntersection (r));
  1986. if (! clipped.isEmpty())
  1987. fillShape (new ClipRegions::EdgeTableRegion (clipped), false);
  1988. }
  1989. }
  1990. template <typename CoordType>
  1991. void fillRectAsPath (const Rectangle<CoordType>& r)
  1992. {
  1993. Path p;
  1994. p.addRectangle (r);
  1995. fillPath (p, AffineTransform::identity);
  1996. }
  1997. void fillRect (const Rectangle<int>& r, const bool replaceContents)
  1998. {
  1999. if (clip != nullptr)
  2000. {
  2001. if (transform.isOnlyTranslated)
  2002. fillTargetRect (transform.translated (r), replaceContents);
  2003. else if (transform.isIntegerScaling)
  2004. fillTargetRect (transform.transformed (r).getSmallestIntegerContainer(), replaceContents);
  2005. else
  2006. fillRectAsPath (r);
  2007. }
  2008. }
  2009. void fillRect (const Rectangle<float>& r)
  2010. {
  2011. if (clip != nullptr)
  2012. {
  2013. if (transform.isOnlyTranslated)
  2014. fillTargetRect (transform.translated (r));
  2015. else if (transform.isIntegerScaling)
  2016. fillTargetRect (transform.transformed (r));
  2017. else
  2018. fillRectAsPath (r);
  2019. }
  2020. }
  2021. void fillPath (const Path& path, const AffineTransform& t)
  2022. {
  2023. if (clip != nullptr)
  2024. fillShape (new ClipRegions::EdgeTableRegion (clip->getClipBounds(), path, transform.getTransformWith (t)), false);
  2025. }
  2026. void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y)
  2027. {
  2028. jassert (transform.isOnlyTranslated);
  2029. if (clip != nullptr)
  2030. {
  2031. ClipRegions::EdgeTableRegion* edgeTableClip = new ClipRegions::EdgeTableRegion (edgeTable);
  2032. edgeTableClip->edgeTable.translate (x + transform.xOffset,
  2033. y + transform.yOffset);
  2034. fillShape (edgeTableClip, false);
  2035. }
  2036. }
  2037. void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& t)
  2038. {
  2039. if (clip != nullptr)
  2040. {
  2041. const ScopedPointer<EdgeTable> et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t)));
  2042. if (et != nullptr)
  2043. fillShape (new ClipRegions::EdgeTableRegion (*et), false);
  2044. }
  2045. }
  2046. void fillShape (ClipRegions::Base::Ptr shapeToFill, const bool replaceContents)
  2047. {
  2048. jassert (clip != nullptr);
  2049. shapeToFill = clip->applyClipTo (shapeToFill);
  2050. if (shapeToFill != nullptr)
  2051. {
  2052. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  2053. if (fillType.isGradient())
  2054. {
  2055. jassert (! replaceContents); // that option is just for solid colours
  2056. ColourGradient g2 (*(fillType.gradient));
  2057. g2.multiplyOpacity (fillType.getOpacity());
  2058. AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f));
  2059. const bool isIdentity = t.isOnlyTranslation();
  2060. if (isIdentity)
  2061. {
  2062. // If our translation doesn't involve any distortion, we can speed it up..
  2063. g2.point1.applyTransform (t);
  2064. g2.point2.applyTransform (t);
  2065. t = AffineTransform::identity;
  2066. }
  2067. shapeToFill->fillAllWithGradient (destData, g2, t, isIdentity);
  2068. }
  2069. else if (fillType.isTiledImage())
  2070. {
  2071. renderImage (fillType.image, fillType.transform, shapeToFill);
  2072. }
  2073. else
  2074. {
  2075. shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents);
  2076. }
  2077. }
  2078. }
  2079. //==============================================================================
  2080. void renderImage (const Image& sourceImage, const AffineTransform& trans,
  2081. const ClipRegions::Base* const tiledFillClipRegion)
  2082. {
  2083. const AffineTransform t (transform.getTransformWith (trans));
  2084. const Image::BitmapData destData (image, Image::BitmapData::readWrite);
  2085. const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly);
  2086. const int alpha = fillType.colour.getAlpha();
  2087. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  2088. if (t.isOnlyTranslation())
  2089. {
  2090. // If our translation doesn't involve any distortion, just use a simple blit..
  2091. int tx = (int) (t.getTranslationX() * 256.0f);
  2092. int ty = (int) (t.getTranslationY() * 256.0f);
  2093. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  2094. {
  2095. tx = ((tx + 128) >> 8);
  2096. ty = ((ty + 128) >> 8);
  2097. if (tiledFillClipRegion != nullptr)
  2098. {
  2099. tiledFillClipRegion->renderImageUntransformed (destData, srcData, alpha, tx, ty, true);
  2100. }
  2101. else
  2102. {
  2103. Rectangle<int> area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight());
  2104. area = area.getIntersection (image.getBounds());
  2105. if (! area.isEmpty())
  2106. {
  2107. ClipRegions::Base::Ptr c (clip->applyClipTo (new ClipRegions::EdgeTableRegion (area)));
  2108. if (c != nullptr)
  2109. c->renderImageUntransformed (destData, srcData, alpha, tx, ty, false);
  2110. }
  2111. }
  2112. return;
  2113. }
  2114. }
  2115. if (! t.isSingularity())
  2116. {
  2117. if (tiledFillClipRegion != nullptr)
  2118. {
  2119. tiledFillClipRegion->renderImageTransformed (destData, srcData, alpha, t, betterQuality, true);
  2120. }
  2121. else
  2122. {
  2123. Path p;
  2124. p.addRectangle (sourceImage.getBounds());
  2125. ClipRegions::Base::Ptr c (clip->clone());
  2126. c = c->clipToPath (p, t);
  2127. if (c != nullptr)
  2128. c->renderImageTransformed (destData, srcData, alpha, t, betterQuality, false);
  2129. }
  2130. }
  2131. }
  2132. //==============================================================================
  2133. Image image;
  2134. ClipRegions::Base::Ptr clip;
  2135. RenderingHelpers::TranslationOrTransform transform;
  2136. Font font;
  2137. FillType fillType;
  2138. Graphics::ResamplingQuality interpolationQuality;
  2139. private:
  2140. float transparencyLayerAlpha;
  2141. void cloneClipIfMultiplyReferenced()
  2142. {
  2143. if (clip->getReferenceCount() > 1)
  2144. clip = clip->clone();
  2145. }
  2146. SoftwareRendererSavedState& operator= (const SoftwareRendererSavedState&);
  2147. };
  2148. //==============================================================================
  2149. template <class StateObjectType>
  2150. class SavedStateStack
  2151. {
  2152. public:
  2153. SavedStateStack (StateObjectType* const initialState) noexcept
  2154. : currentState (initialState)
  2155. {}
  2156. inline StateObjectType* operator->() const noexcept { return currentState; }
  2157. inline StateObjectType& operator*() const noexcept { return *currentState; }
  2158. void save()
  2159. {
  2160. stack.add (new StateObjectType (*currentState));
  2161. }
  2162. void restore()
  2163. {
  2164. if (StateObjectType* const top = stack.getLast())
  2165. {
  2166. currentState = top;
  2167. stack.removeLast (1, false);
  2168. }
  2169. else
  2170. {
  2171. jassertfalse; // trying to pop with an empty stack!
  2172. }
  2173. }
  2174. void beginTransparencyLayer (float opacity)
  2175. {
  2176. save();
  2177. currentState = currentState->beginTransparencyLayer (opacity);
  2178. }
  2179. void endTransparencyLayer()
  2180. {
  2181. const ScopedPointer<StateObjectType> finishedTransparencyLayer (currentState);
  2182. restore();
  2183. currentState->endTransparencyLayer (*finishedTransparencyLayer);
  2184. }
  2185. private:
  2186. ScopedPointer<StateObjectType> currentState;
  2187. OwnedArray<StateObjectType> stack;
  2188. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedStateStack)
  2189. };
  2190. }
  2191. #if JUCE_MSVC
  2192. #pragma warning (pop)
  2193. #endif
  2194. #endif // __JUCE_RENDERINGHELPERS_JUCEHEADER__