The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2531 lines
92KB

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