Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2735 lines
103KB

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