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.

2370 lines
87KB

  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. template <class DestPixelType, class SrcPixelType, bool repeatPattern>
  336. class ImageFillEdgeTableRenderer
  337. {
  338. public:
  339. ImageFillEdgeTableRenderer (const Image::BitmapData& destData_,
  340. const Image::BitmapData& srcData_,
  341. const int extraAlpha_,
  342. const int x, const int y)
  343. : destData (destData_),
  344. srcData (srcData_),
  345. extraAlpha (extraAlpha_ + 1),
  346. xOffset (repeatPattern ? negativeAwareModulo (x, srcData_.width) - srcData_.width : x),
  347. yOffset (repeatPattern ? negativeAwareModulo (y, srcData_.height) - srcData_.height : y)
  348. {
  349. }
  350. forcedinline void setEdgeTableYPos (int y) noexcept
  351. {
  352. linePixels = (DestPixelType*) destData.getLinePointer (y);
  353. y -= yOffset;
  354. if (repeatPattern)
  355. {
  356. jassert (y >= 0);
  357. y %= srcData.height;
  358. }
  359. sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y);
  360. }
  361. forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const noexcept
  362. {
  363. alphaLevel = (alphaLevel * extraAlpha) >> 8;
  364. linePixels[x].blend (sourceLineStart [repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)], (uint32) alphaLevel);
  365. }
  366. forcedinline void handleEdgeTablePixelFull (const int x) const noexcept
  367. {
  368. linePixels[x].blend (sourceLineStart [repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)], (uint32) extraAlpha);
  369. }
  370. void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept
  371. {
  372. DestPixelType* dest = linePixels + x;
  373. alphaLevel = (alphaLevel * extraAlpha) >> 8;
  374. x -= xOffset;
  375. jassert (repeatPattern || (x >= 0 && x + width <= srcData.width));
  376. if (alphaLevel < 0xfe)
  377. {
  378. do
  379. {
  380. dest++ ->blend (sourceLineStart [repeatPattern ? (x++ % srcData.width) : x++], (uint32) alphaLevel);
  381. } while (--width > 0);
  382. }
  383. else
  384. {
  385. if (repeatPattern)
  386. {
  387. do
  388. {
  389. dest++ ->blend (sourceLineStart [x++ % srcData.width]);
  390. } while (--width > 0);
  391. }
  392. else
  393. {
  394. copyRow (dest, sourceLineStart + x, width);
  395. }
  396. }
  397. }
  398. void handleEdgeTableLineFull (int x, int width) const noexcept
  399. {
  400. DestPixelType* dest = linePixels + x;
  401. x -= xOffset;
  402. jassert (repeatPattern || (x >= 0 && x + width <= srcData.width));
  403. if (extraAlpha < 0xfe)
  404. {
  405. do
  406. {
  407. dest++ ->blend (sourceLineStart [repeatPattern ? (x++ % srcData.width) : x++], (uint32) extraAlpha);
  408. } while (--width > 0);
  409. }
  410. else
  411. {
  412. if (repeatPattern)
  413. {
  414. do
  415. {
  416. dest++ ->blend (sourceLineStart [x++ % srcData.width]);
  417. } while (--width > 0);
  418. }
  419. else
  420. {
  421. copyRow (dest, sourceLineStart + x, width);
  422. }
  423. }
  424. }
  425. void clipEdgeTableLine (EdgeTable& et, int x, int y, int width)
  426. {
  427. jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width);
  428. SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset);
  429. uint8* mask = (uint8*) (s + x - xOffset);
  430. if (sizeof (SrcPixelType) == sizeof (PixelARGB))
  431. mask += PixelARGB::indexA;
  432. et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width);
  433. }
  434. private:
  435. const Image::BitmapData& destData;
  436. const Image::BitmapData& srcData;
  437. const int extraAlpha, xOffset, yOffset;
  438. DestPixelType* linePixels;
  439. SrcPixelType* sourceLineStart;
  440. template <class PixelType1, class PixelType2>
  441. static forcedinline void copyRow (PixelType1* dest, PixelType2* src, int width) noexcept
  442. {
  443. do
  444. {
  445. dest++ ->blend (*src++);
  446. } while (--width > 0);
  447. }
  448. static forcedinline void copyRow (PixelRGB* dest, PixelRGB* src, int width) noexcept
  449. {
  450. memcpy (dest, src, width * sizeof (PixelRGB));
  451. }
  452. JUCE_DECLARE_NON_COPYABLE (ImageFillEdgeTableRenderer);
  453. };
  454. //==============================================================================
  455. template <class DestPixelType, class SrcPixelType, bool repeatPattern>
  456. class TransformedImageFillEdgeTableRenderer
  457. {
  458. public:
  459. TransformedImageFillEdgeTableRenderer (const Image::BitmapData& destData_,
  460. const Image::BitmapData& srcData_,
  461. const AffineTransform& transform,
  462. const int extraAlpha_,
  463. const bool betterQuality_)
  464. : interpolator (transform,
  465. betterQuality_ ? 0.5f : 0.0f,
  466. betterQuality_ ? -128 : 0),
  467. destData (destData_),
  468. srcData (srcData_),
  469. extraAlpha (extraAlpha_ + 1),
  470. betterQuality (betterQuality_),
  471. maxX (srcData_.width - 1),
  472. maxY (srcData_.height - 1),
  473. scratchSize (2048)
  474. {
  475. scratchBuffer.malloc (scratchSize);
  476. }
  477. forcedinline void setEdgeTableYPos (const int newY) noexcept
  478. {
  479. y = newY;
  480. linePixels = (DestPixelType*) destData.getLinePointer (newY);
  481. }
  482. forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept
  483. {
  484. SrcPixelType p;
  485. generate (&p, x, 1);
  486. linePixels[x].blend (p, (uint32) (alphaLevel * extraAlpha) >> 8);
  487. }
  488. forcedinline void handleEdgeTablePixelFull (const int x) noexcept
  489. {
  490. SrcPixelType p;
  491. generate (&p, x, 1);
  492. linePixels[x].blend (p, (uint32) extraAlpha);
  493. }
  494. void handleEdgeTableLine (const int x, int width, int alphaLevel) noexcept
  495. {
  496. if (width > (int) scratchSize)
  497. {
  498. scratchSize = (size_t) width;
  499. scratchBuffer.malloc (scratchSize);
  500. }
  501. SrcPixelType* span = scratchBuffer;
  502. generate (span, x, width);
  503. DestPixelType* dest = linePixels + x;
  504. alphaLevel *= extraAlpha;
  505. alphaLevel >>= 8;
  506. if (alphaLevel < 0xfe)
  507. {
  508. do
  509. {
  510. dest++ ->blend (*span++, (uint32) alphaLevel);
  511. } while (--width > 0);
  512. }
  513. else
  514. {
  515. do
  516. {
  517. dest++ ->blend (*span++);
  518. } while (--width > 0);
  519. }
  520. }
  521. forcedinline void handleEdgeTableLineFull (const int x, int width) noexcept
  522. {
  523. handleEdgeTableLine (x, width, 255);
  524. }
  525. void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width)
  526. {
  527. if (width > (int) scratchSize)
  528. {
  529. scratchSize = (size_t) width;
  530. scratchBuffer.malloc (scratchSize);
  531. }
  532. y = y_;
  533. generate (scratchBuffer.getData(), x, width);
  534. et.clipLineToMask (x, y_,
  535. reinterpret_cast<uint8*> (scratchBuffer.getData()) + SrcPixelType::indexA,
  536. sizeof (SrcPixelType), width);
  537. }
  538. private:
  539. //==============================================================================
  540. template <class PixelType>
  541. void generate (PixelType* dest, const int x, int numPixels) noexcept
  542. {
  543. this->interpolator.setStartOfLine ((float) x, (float) y, numPixels);
  544. do
  545. {
  546. int hiResX, hiResY;
  547. this->interpolator.next (hiResX, hiResY);
  548. int loResX = hiResX >> 8;
  549. int loResY = hiResY >> 8;
  550. if (repeatPattern)
  551. {
  552. loResX = negativeAwareModulo (loResX, srcData.width);
  553. loResY = negativeAwareModulo (loResY, srcData.height);
  554. }
  555. if (betterQuality)
  556. {
  557. if (isPositiveAndBelow (loResX, maxX))
  558. {
  559. if (isPositiveAndBelow (loResY, maxY))
  560. {
  561. // In the centre of the image..
  562. render4PixelAverage (dest, this->srcData.getPixelPointer (loResX, loResY),
  563. hiResX & 255, hiResY & 255);
  564. ++dest;
  565. continue;
  566. }
  567. else
  568. {
  569. // At a top or bottom edge..
  570. if (! repeatPattern)
  571. {
  572. if (loResY < 0)
  573. render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, 0), hiResX & 255);
  574. else
  575. render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, maxY), hiResX & 255);
  576. ++dest;
  577. continue;
  578. }
  579. }
  580. }
  581. else
  582. {
  583. if (isPositiveAndBelow (loResY, maxY))
  584. {
  585. // At a left or right hand edge..
  586. if (! repeatPattern)
  587. {
  588. if (loResX < 0)
  589. render2PixelAverageY (dest, this->srcData.getPixelPointer (0, loResY), hiResY & 255);
  590. else
  591. render2PixelAverageY (dest, this->srcData.getPixelPointer (maxX, loResY), hiResY & 255);
  592. ++dest;
  593. continue;
  594. }
  595. }
  596. }
  597. }
  598. if (! repeatPattern)
  599. {
  600. if (loResX < 0) loResX = 0;
  601. if (loResY < 0) loResY = 0;
  602. if (loResX > maxX) loResX = maxX;
  603. if (loResY > maxY) loResY = maxY;
  604. }
  605. dest->set (*(const PixelType*) this->srcData.getPixelPointer (loResX, loResY));
  606. ++dest;
  607. } while (--numPixels > 0);
  608. }
  609. //==============================================================================
  610. void render4PixelAverage (PixelARGB* const dest, const uint8* src, const int subPixelX, const int subPixelY) noexcept
  611. {
  612. uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 };
  613. uint32 weight = (uint32) ((256 - subPixelX) * (256 - subPixelY));
  614. c[0] += weight * src[0];
  615. c[1] += weight * src[1];
  616. c[2] += weight * src[2];
  617. c[3] += weight * src[3];
  618. weight = (uint32) (subPixelX * (256 - subPixelY));
  619. c[0] += weight * src[4];
  620. c[1] += weight * src[5];
  621. c[2] += weight * src[6];
  622. c[3] += weight * src[7];
  623. src += this->srcData.lineStride;
  624. weight = (uint32) ((256 - subPixelX) * subPixelY);
  625. c[0] += weight * src[0];
  626. c[1] += weight * src[1];
  627. c[2] += weight * src[2];
  628. c[3] += weight * src[3];
  629. weight = (uint32) (subPixelX * subPixelY);
  630. c[0] += weight * src[4];
  631. c[1] += weight * src[5];
  632. c[2] += weight * src[6];
  633. c[3] += weight * src[7];
  634. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16),
  635. (uint8) (c[PixelARGB::indexR] >> 16),
  636. (uint8) (c[PixelARGB::indexG] >> 16),
  637. (uint8) (c[PixelARGB::indexB] >> 16));
  638. }
  639. void render2PixelAverageX (PixelARGB* const dest, const uint8* src, const int subPixelX) noexcept
  640. {
  641. uint32 c[4] = { 128, 128, 128, 128 };
  642. uint32 weight = (uint32) (256 - subPixelX);
  643. c[0] += weight * src[0];
  644. c[1] += weight * src[1];
  645. c[2] += weight * src[2];
  646. c[3] += weight * src[3];
  647. weight = (uint32) subPixelX;
  648. c[0] += weight * src[4];
  649. c[1] += weight * src[5];
  650. c[2] += weight * src[6];
  651. c[3] += weight * src[7];
  652. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8),
  653. (uint8) (c[PixelARGB::indexR] >> 8),
  654. (uint8) (c[PixelARGB::indexG] >> 8),
  655. (uint8) (c[PixelARGB::indexB] >> 8));
  656. }
  657. void render2PixelAverageY (PixelARGB* const dest, const uint8* src, const int subPixelY) noexcept
  658. {
  659. uint32 c[4] = { 128, 128, 128, 128 };
  660. uint32 weight = (uint32) (256 - subPixelY);
  661. c[0] += weight * src[0];
  662. c[1] += weight * src[1];
  663. c[2] += weight * src[2];
  664. c[3] += weight * src[3];
  665. src += this->srcData.lineStride;
  666. weight = (uint32) subPixelY;
  667. c[0] += weight * src[0];
  668. c[1] += weight * src[1];
  669. c[2] += weight * src[2];
  670. c[3] += weight * src[3];
  671. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8),
  672. (uint8) (c[PixelARGB::indexR] >> 8),
  673. (uint8) (c[PixelARGB::indexG] >> 8),
  674. (uint8) (c[PixelARGB::indexB] >> 8));
  675. }
  676. //==============================================================================
  677. void render4PixelAverage (PixelRGB* const dest, const uint8* src, const int subPixelX, const int subPixelY) noexcept
  678. {
  679. uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 };
  680. uint32 weight = (uint32) ((256 - subPixelX) * (256 - subPixelY));
  681. c[0] += weight * src[0];
  682. c[1] += weight * src[1];
  683. c[2] += weight * src[2];
  684. weight = (uint32) (subPixelX * (256 - subPixelY));
  685. c[0] += weight * src[3];
  686. c[1] += weight * src[4];
  687. c[2] += weight * src[5];
  688. src += this->srcData.lineStride;
  689. weight = (uint32) ((256 - subPixelX) * subPixelY);
  690. c[0] += weight * src[0];
  691. c[1] += weight * src[1];
  692. c[2] += weight * src[2];
  693. weight = (uint32) (subPixelX * subPixelY);
  694. c[0] += weight * src[3];
  695. c[1] += weight * src[4];
  696. c[2] += weight * src[5];
  697. dest->setARGB ((uint8) 255,
  698. (uint8) (c[PixelRGB::indexR] >> 16),
  699. (uint8) (c[PixelRGB::indexG] >> 16),
  700. (uint8) (c[PixelRGB::indexB] >> 16));
  701. }
  702. void render2PixelAverageX (PixelRGB* const dest, const uint8* src, const int subPixelX) noexcept
  703. {
  704. uint32 c[3] = { 128, 128, 128 };
  705. const uint32 weight = (uint32) (256 - subPixelX);
  706. c[0] += weight * src[0];
  707. c[1] += weight * src[1];
  708. c[2] += weight * src[2];
  709. c[0] += subPixelX * src[3];
  710. c[1] += subPixelX * src[4];
  711. c[2] += subPixelX * src[5];
  712. dest->setARGB ((uint8) 255,
  713. (uint8) (c[PixelRGB::indexR] >> 8),
  714. (uint8) (c[PixelRGB::indexG] >> 8),
  715. (uint8) (c[PixelRGB::indexB] >> 8));
  716. }
  717. void render2PixelAverageY (PixelRGB* const dest, const uint8* src, const int subPixelY) noexcept
  718. {
  719. uint32 c[3] = { 128, 128, 128 };
  720. const uint32 weight = (uint32) (256 - subPixelY);
  721. c[0] += weight * src[0];
  722. c[1] += weight * src[1];
  723. c[2] += weight * src[2];
  724. src += this->srcData.lineStride;
  725. c[0] += subPixelY * src[0];
  726. c[1] += subPixelY * src[1];
  727. c[2] += subPixelY * src[2];
  728. dest->setARGB ((uint8) 255,
  729. (uint8) (c[PixelRGB::indexR] >> 8),
  730. (uint8) (c[PixelRGB::indexG] >> 8),
  731. (uint8) (c[PixelRGB::indexB] >> 8));
  732. }
  733. //==============================================================================
  734. void render4PixelAverage (PixelAlpha* const dest, const uint8* src, const int subPixelX, const int subPixelY) noexcept
  735. {
  736. uint32 c = 256 * 128;
  737. c += src[0] * ((256 - subPixelX) * (256 - subPixelY));
  738. c += src[1] * (subPixelX * (256 - subPixelY));
  739. src += this->srcData.lineStride;
  740. c += src[0] * ((256 - subPixelX) * subPixelY);
  741. c += src[1] * (subPixelX * subPixelY);
  742. *((uint8*) dest) = (uint8) (c >> 16);
  743. }
  744. void render2PixelAverageX (PixelAlpha* const dest, const uint8* src, const int subPixelX) noexcept
  745. {
  746. uint32 c = 128;
  747. c += src[0] * (256 - subPixelX);
  748. c += src[1] * subPixelX;
  749. *((uint8*) dest) = (uint8) (c >> 8);
  750. }
  751. void render2PixelAverageY (PixelAlpha* const dest, const uint8* src, const int subPixelY) noexcept
  752. {
  753. uint32 c = 128;
  754. c += src[0] * (256 - subPixelY);
  755. src += this->srcData.lineStride;
  756. c += src[0] * subPixelY;
  757. *((uint8*) dest) = (uint8) (c >> 8);
  758. }
  759. //==============================================================================
  760. class TransformedImageSpanInterpolator
  761. {
  762. public:
  763. TransformedImageSpanInterpolator (const AffineTransform& transform, const float pixelOffset_, const int pixelOffsetInt_) noexcept
  764. : inverseTransform (transform.inverted()),
  765. pixelOffset (pixelOffset_), pixelOffsetInt (pixelOffsetInt_)
  766. {}
  767. void setStartOfLine (float x, float y, const int numPixels) noexcept
  768. {
  769. jassert (numPixels > 0);
  770. x += pixelOffset;
  771. y += pixelOffset;
  772. float x1 = x, y1 = y;
  773. x += numPixels;
  774. inverseTransform.transformPoints (x1, y1, x, y);
  775. xBresenham.set ((int) (x1 * 256.0f), (int) (x * 256.0f), numPixels, pixelOffsetInt);
  776. yBresenham.set ((int) (y1 * 256.0f), (int) (y * 256.0f), numPixels, pixelOffsetInt);
  777. }
  778. void next (int& x, int& y) noexcept
  779. {
  780. x = xBresenham.n;
  781. xBresenham.stepToNext();
  782. y = yBresenham.n;
  783. yBresenham.stepToNext();
  784. }
  785. private:
  786. class BresenhamInterpolator
  787. {
  788. public:
  789. BresenhamInterpolator() noexcept {}
  790. void set (const int n1, const int n2, const int numSteps_, const int pixelOffsetInt) noexcept
  791. {
  792. numSteps = numSteps_;
  793. step = (n2 - n1) / numSteps;
  794. remainder = modulo = (n2 - n1) % numSteps;
  795. n = n1 + pixelOffsetInt;
  796. if (modulo <= 0)
  797. {
  798. modulo += numSteps;
  799. remainder += numSteps;
  800. --step;
  801. }
  802. modulo -= numSteps;
  803. }
  804. forcedinline void stepToNext() noexcept
  805. {
  806. modulo += remainder;
  807. n += step;
  808. if (modulo > 0)
  809. {
  810. modulo -= numSteps;
  811. ++n;
  812. }
  813. }
  814. int n;
  815. private:
  816. int numSteps, step, modulo, remainder;
  817. };
  818. const AffineTransform inverseTransform;
  819. BresenhamInterpolator xBresenham, yBresenham;
  820. const float pixelOffset;
  821. const int pixelOffsetInt;
  822. JUCE_DECLARE_NON_COPYABLE (TransformedImageSpanInterpolator);
  823. };
  824. //==============================================================================
  825. TransformedImageSpanInterpolator interpolator;
  826. const Image::BitmapData& destData;
  827. const Image::BitmapData& srcData;
  828. const int extraAlpha;
  829. const bool betterQuality;
  830. const int maxX, maxY;
  831. int y;
  832. DestPixelType* linePixels;
  833. HeapBlock <SrcPixelType> scratchBuffer;
  834. size_t scratchSize;
  835. JUCE_DECLARE_NON_COPYABLE (TransformedImageFillEdgeTableRenderer);
  836. };
  837. //==============================================================================
  838. class ClipRegionBase : public SingleThreadedReferenceCountedObject
  839. {
  840. public:
  841. ClipRegionBase() {}
  842. virtual ~ClipRegionBase() {}
  843. typedef ReferenceCountedObjectPtr<ClipRegionBase> Ptr;
  844. virtual Ptr clone() const = 0;
  845. virtual Ptr applyClipTo (const Ptr& target) const = 0;
  846. virtual Ptr clipToRectangle (const Rectangle<int>& r) = 0;
  847. virtual Ptr clipToRectangleList (const RectangleList& r) = 0;
  848. virtual Ptr excludeClipRectangle (const Rectangle<int>& r) = 0;
  849. virtual Ptr clipToPath (const Path& p, const AffineTransform& transform) = 0;
  850. virtual Ptr clipToEdgeTable (const EdgeTable& et) = 0;
  851. virtual Ptr clipToImageAlpha (const Image& image, const AffineTransform& t, const bool betterQuality) = 0;
  852. virtual Ptr translated (const Point<int>& delta) = 0;
  853. virtual bool clipRegionIntersects (const Rectangle<int>& r) const = 0;
  854. virtual Rectangle<int> getClipBounds() const = 0;
  855. virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB& colour, bool replaceContents) const = 0;
  856. virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>& area, const PixelARGB& colour) const = 0;
  857. virtual void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const = 0;
  858. virtual void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const = 0;
  859. virtual void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& t, bool betterQuality, bool tiledFill) const = 0;
  860. virtual void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const = 0;
  861. protected:
  862. //==============================================================================
  863. template <class Iterator>
  864. static void renderImageTransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData,
  865. const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill)
  866. {
  867. switch (destData.pixelFormat)
  868. {
  869. case Image::ARGB:
  870. switch (srcData.pixelFormat)
  871. {
  872. case Image::ARGB:
  873. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  874. else { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  875. break;
  876. case Image::RGB:
  877. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  878. else { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  879. break;
  880. default:
  881. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  882. else { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  883. break;
  884. }
  885. break;
  886. case Image::RGB:
  887. switch (srcData.pixelFormat)
  888. {
  889. case Image::ARGB:
  890. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  891. else { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  892. break;
  893. case Image::RGB:
  894. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  895. else { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  896. break;
  897. default:
  898. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  899. else { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  900. break;
  901. }
  902. break;
  903. default:
  904. switch (srcData.pixelFormat)
  905. {
  906. case Image::ARGB:
  907. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  908. else { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  909. break;
  910. case Image::RGB:
  911. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  912. else { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  913. break;
  914. default:
  915. if (tiledFill) { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  916. else { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); }
  917. break;
  918. }
  919. break;
  920. }
  921. }
  922. template <class Iterator>
  923. static void renderImageUntransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill)
  924. {
  925. switch (destData.pixelFormat)
  926. {
  927. case Image::ARGB:
  928. switch (srcData.pixelFormat)
  929. {
  930. case Image::ARGB:
  931. if (tiledFill) { ImageFillEdgeTableRenderer <PixelARGB, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  932. else { ImageFillEdgeTableRenderer <PixelARGB, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  933. break;
  934. case Image::RGB:
  935. if (tiledFill) { ImageFillEdgeTableRenderer <PixelARGB, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  936. else { ImageFillEdgeTableRenderer <PixelARGB, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  937. break;
  938. default:
  939. if (tiledFill) { ImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  940. else { ImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  941. break;
  942. }
  943. break;
  944. case Image::RGB:
  945. switch (srcData.pixelFormat)
  946. {
  947. case Image::ARGB:
  948. if (tiledFill) { ImageFillEdgeTableRenderer <PixelRGB, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  949. else { ImageFillEdgeTableRenderer <PixelRGB, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  950. break;
  951. case Image::RGB:
  952. if (tiledFill) { ImageFillEdgeTableRenderer <PixelRGB, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  953. else { ImageFillEdgeTableRenderer <PixelRGB, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  954. break;
  955. default:
  956. if (tiledFill) { ImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  957. else { ImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  958. break;
  959. }
  960. break;
  961. default:
  962. switch (srcData.pixelFormat)
  963. {
  964. case Image::ARGB:
  965. if (tiledFill) { ImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  966. else { ImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  967. break;
  968. case Image::RGB:
  969. if (tiledFill) { ImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  970. else { ImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  971. break;
  972. default:
  973. if (tiledFill) { ImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  974. else { ImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
  975. break;
  976. }
  977. break;
  978. }
  979. }
  980. template <class Iterator, class DestPixelType>
  981. static void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*)
  982. {
  983. jassert (destData.pixelStride == sizeof (DestPixelType));
  984. if (replaceContents)
  985. {
  986. SolidColourEdgeTableRenderer <DestPixelType, true> r (destData, fillColour);
  987. iter.iterate (r);
  988. }
  989. else
  990. {
  991. SolidColourEdgeTableRenderer <DestPixelType, false> r (destData, fillColour);
  992. iter.iterate (r);
  993. }
  994. }
  995. template <class Iterator, class DestPixelType>
  996. static void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform,
  997. const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*)
  998. {
  999. jassert (destData.pixelStride == sizeof (DestPixelType));
  1000. if (g.isRadial)
  1001. {
  1002. if (isIdentity)
  1003. {
  1004. GradientEdgeTableRenderer <DestPixelType, RadialGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1005. iter.iterate (renderer);
  1006. }
  1007. else
  1008. {
  1009. GradientEdgeTableRenderer <DestPixelType, TransformedRadialGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1010. iter.iterate (renderer);
  1011. }
  1012. }
  1013. else
  1014. {
  1015. GradientEdgeTableRenderer <DestPixelType, LinearGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1016. iter.iterate (renderer);
  1017. }
  1018. }
  1019. };
  1020. //==============================================================================
  1021. class ClipRegion_EdgeTable : public ClipRegionBase
  1022. {
  1023. public:
  1024. ClipRegion_EdgeTable (const EdgeTable& e) : edgeTable (e) {}
  1025. ClipRegion_EdgeTable (const Rectangle<int>& r) : edgeTable (r) {}
  1026. ClipRegion_EdgeTable (const Rectangle<float>& r) : edgeTable (r) {}
  1027. ClipRegion_EdgeTable (const RectangleList& r) : edgeTable (r) {}
  1028. ClipRegion_EdgeTable (const Rectangle<int>& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {}
  1029. ClipRegion_EdgeTable (const ClipRegion_EdgeTable& other) : edgeTable (other.edgeTable) {}
  1030. Ptr clone() const
  1031. {
  1032. return new ClipRegion_EdgeTable (*this);
  1033. }
  1034. Ptr applyClipTo (const Ptr& target) const
  1035. {
  1036. return target->clipToEdgeTable (edgeTable);
  1037. }
  1038. Ptr clipToRectangle (const Rectangle<int>& r)
  1039. {
  1040. edgeTable.clipToRectangle (r);
  1041. return edgeTable.isEmpty() ? nullptr : this;
  1042. }
  1043. Ptr clipToRectangleList (const RectangleList& r)
  1044. {
  1045. RectangleList inverse (edgeTable.getMaximumBounds());
  1046. if (inverse.subtract (r))
  1047. for (RectangleList::Iterator iter (inverse); iter.next();)
  1048. edgeTable.excludeRectangle (*iter.getRectangle());
  1049. return edgeTable.isEmpty() ? nullptr : this;
  1050. }
  1051. Ptr excludeClipRectangle (const Rectangle<int>& r)
  1052. {
  1053. edgeTable.excludeRectangle (r);
  1054. return edgeTable.isEmpty() ? nullptr : this;
  1055. }
  1056. Ptr clipToPath (const Path& p, const AffineTransform& transform)
  1057. {
  1058. EdgeTable et (edgeTable.getMaximumBounds(), p, transform);
  1059. edgeTable.clipToEdgeTable (et);
  1060. return edgeTable.isEmpty() ? nullptr : this;
  1061. }
  1062. Ptr clipToEdgeTable (const EdgeTable& et)
  1063. {
  1064. edgeTable.clipToEdgeTable (et);
  1065. return edgeTable.isEmpty() ? nullptr : this;
  1066. }
  1067. Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality)
  1068. {
  1069. const Image::BitmapData srcData (image, Image::BitmapData::readOnly);
  1070. if (transform.isOnlyTranslation())
  1071. {
  1072. // If our translation doesn't involve any distortion, just use a simple blit..
  1073. const int tx = (int) (transform.getTranslationX() * 256.0f);
  1074. const int ty = (int) (transform.getTranslationY() * 256.0f);
  1075. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  1076. {
  1077. const int imageX = ((tx + 128) >> 8);
  1078. const int imageY = ((ty + 128) >> 8);
  1079. if (image.getFormat() == Image::ARGB)
  1080. straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0);
  1081. else
  1082. straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0);
  1083. return edgeTable.isEmpty() ? nullptr : this;
  1084. }
  1085. }
  1086. if (transform.isSingularity())
  1087. return nullptr;
  1088. {
  1089. Path p;
  1090. p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height);
  1091. EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform);
  1092. edgeTable.clipToEdgeTable (et2);
  1093. }
  1094. if (! edgeTable.isEmpty())
  1095. {
  1096. if (image.getFormat() == Image::ARGB)
  1097. transformedClipImage (srcData, transform, betterQuality, (PixelARGB*) 0);
  1098. else
  1099. transformedClipImage (srcData, transform, betterQuality, (PixelAlpha*) 0);
  1100. }
  1101. return edgeTable.isEmpty() ? nullptr : this;
  1102. }
  1103. Ptr translated (const Point<int>& delta)
  1104. {
  1105. edgeTable.translate ((float) delta.getX(), delta.getY());
  1106. return edgeTable.isEmpty() ? nullptr : this;
  1107. }
  1108. bool clipRegionIntersects (const Rectangle<int>& r) const
  1109. {
  1110. return edgeTable.getMaximumBounds().intersects (r);
  1111. }
  1112. Rectangle<int> getClipBounds() const
  1113. {
  1114. return edgeTable.getMaximumBounds();
  1115. }
  1116. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB& colour, bool replaceContents) const
  1117. {
  1118. const Rectangle<int> totalClip (edgeTable.getMaximumBounds());
  1119. const Rectangle<int> clipped (totalClip.getIntersection (area));
  1120. if (! clipped.isEmpty())
  1121. {
  1122. ClipRegion_EdgeTable et (clipped);
  1123. et.edgeTable.clipToEdgeTable (edgeTable);
  1124. et.fillAllWithColour (destData, colour, replaceContents);
  1125. }
  1126. }
  1127. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>& area, const PixelARGB& colour) const
  1128. {
  1129. const Rectangle<float> totalClip (edgeTable.getMaximumBounds().toFloat());
  1130. const Rectangle<float> clipped (totalClip.getIntersection (area));
  1131. if (! clipped.isEmpty())
  1132. {
  1133. ClipRegion_EdgeTable et (clipped);
  1134. et.edgeTable.clipToEdgeTable (edgeTable);
  1135. et.fillAllWithColour (destData, colour, false);
  1136. }
  1137. }
  1138. void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const
  1139. {
  1140. switch (destData.pixelFormat)
  1141. {
  1142. case Image::ARGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1143. case Image::RGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1144. default: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1145. }
  1146. }
  1147. void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const
  1148. {
  1149. HeapBlock <PixelARGB> lookupTable;
  1150. const int numLookupEntries = gradient.createLookupTable (transform, lookupTable);
  1151. jassert (numLookupEntries > 0);
  1152. switch (destData.pixelFormat)
  1153. {
  1154. case Image::ARGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
  1155. case Image::RGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
  1156. default: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
  1157. }
  1158. }
  1159. void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const
  1160. {
  1161. renderImageTransformedInternal (edgeTable, destData, srcData, alpha, transform, betterQuality, tiledFill);
  1162. }
  1163. void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const
  1164. {
  1165. renderImageUntransformedInternal (edgeTable, destData, srcData, alpha, x, y, tiledFill);
  1166. }
  1167. EdgeTable edgeTable;
  1168. private:
  1169. //==============================================================================
  1170. template <class SrcPixelType>
  1171. void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType*)
  1172. {
  1173. TransformedImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, transform, 255, betterQuality);
  1174. for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y)
  1175. renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(),
  1176. edgeTable.getMaximumBounds().getWidth());
  1177. }
  1178. template <class SrcPixelType>
  1179. void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*)
  1180. {
  1181. Rectangle<int> r (imageX, imageY, srcData.width, srcData.height);
  1182. edgeTable.clipToRectangle (r);
  1183. ImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, 255, imageX, imageY);
  1184. for (int y = 0; y < r.getHeight(); ++y)
  1185. renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth());
  1186. }
  1187. ClipRegion_EdgeTable& operator= (const ClipRegion_EdgeTable&);
  1188. };
  1189. //==============================================================================
  1190. class ClipRegion_RectangleList : public ClipRegionBase
  1191. {
  1192. public:
  1193. ClipRegion_RectangleList (const Rectangle<int>& r) : clip (r) {}
  1194. ClipRegion_RectangleList (const RectangleList& r) : clip (r) {}
  1195. ClipRegion_RectangleList (const ClipRegion_RectangleList& other) : clip (other.clip) {}
  1196. Ptr clone() const
  1197. {
  1198. return new ClipRegion_RectangleList (*this);
  1199. }
  1200. Ptr applyClipTo (const Ptr& target) const
  1201. {
  1202. return target->clipToRectangleList (clip);
  1203. }
  1204. Ptr clipToRectangle (const Rectangle<int>& r)
  1205. {
  1206. clip.clipTo (r);
  1207. return clip.isEmpty() ? nullptr : this;
  1208. }
  1209. Ptr clipToRectangleList (const RectangleList& r)
  1210. {
  1211. clip.clipTo (r);
  1212. return clip.isEmpty() ? nullptr : this;
  1213. }
  1214. Ptr excludeClipRectangle (const Rectangle<int>& r)
  1215. {
  1216. clip.subtract (r);
  1217. return clip.isEmpty() ? nullptr : this;
  1218. }
  1219. Ptr clipToPath (const Path& p, const AffineTransform& transform)
  1220. {
  1221. return Ptr (new ClipRegion_EdgeTable (clip))->clipToPath (p, transform);
  1222. }
  1223. Ptr clipToEdgeTable (const EdgeTable& et)
  1224. {
  1225. return Ptr (new ClipRegion_EdgeTable (clip))->clipToEdgeTable (et);
  1226. }
  1227. Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality)
  1228. {
  1229. return Ptr (new ClipRegion_EdgeTable (clip))->clipToImageAlpha (image, transform, betterQuality);
  1230. }
  1231. Ptr translated (const Point<int>& delta)
  1232. {
  1233. clip.offsetAll (delta.getX(), delta.getY());
  1234. return clip.isEmpty() ? nullptr : this;
  1235. }
  1236. bool clipRegionIntersects (const Rectangle<int>& r) const
  1237. {
  1238. return clip.intersects (r);
  1239. }
  1240. Rectangle<int> getClipBounds() const
  1241. {
  1242. return clip.getBounds();
  1243. }
  1244. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB& colour, bool replaceContents) const
  1245. {
  1246. SubRectangleIterator iter (clip, area);
  1247. switch (destData.pixelFormat)
  1248. {
  1249. case Image::ARGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1250. case Image::RGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1251. default: renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1252. }
  1253. }
  1254. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>& area, const PixelARGB& colour) const
  1255. {
  1256. SubRectangleIteratorFloat iter (clip, area);
  1257. switch (destData.pixelFormat)
  1258. {
  1259. case Image::ARGB: renderSolidFill (iter, destData, colour, false, (PixelARGB*) 0); break;
  1260. case Image::RGB: renderSolidFill (iter, destData, colour, false, (PixelRGB*) 0); break;
  1261. default: renderSolidFill (iter, destData, colour, false, (PixelAlpha*) 0); break;
  1262. }
  1263. }
  1264. void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const
  1265. {
  1266. switch (destData.pixelFormat)
  1267. {
  1268. case Image::ARGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1269. case Image::RGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1270. default: renderSolidFill (*this, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1271. }
  1272. }
  1273. void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const
  1274. {
  1275. HeapBlock <PixelARGB> lookupTable;
  1276. const int numLookupEntries = gradient.createLookupTable (transform, lookupTable);
  1277. jassert (numLookupEntries > 0);
  1278. switch (destData.pixelFormat)
  1279. {
  1280. case Image::ARGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
  1281. case Image::RGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
  1282. default: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
  1283. }
  1284. }
  1285. void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const
  1286. {
  1287. renderImageTransformedInternal (*this, destData, srcData, alpha, transform, betterQuality, tiledFill);
  1288. }
  1289. void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const
  1290. {
  1291. renderImageUntransformedInternal (*this, destData, srcData, alpha, x, y, tiledFill);
  1292. }
  1293. RectangleList clip;
  1294. //==============================================================================
  1295. template <class Renderer>
  1296. void iterate (Renderer& r) const noexcept
  1297. {
  1298. RectangleList::Iterator iter (clip);
  1299. while (iter.next())
  1300. {
  1301. const Rectangle<int> rect (*iter.getRectangle());
  1302. const int x = rect.getX();
  1303. const int w = rect.getWidth();
  1304. jassert (w > 0);
  1305. const int bottom = rect.getBottom();
  1306. for (int y = rect.getY(); y < bottom; ++y)
  1307. {
  1308. r.setEdgeTableYPos (y);
  1309. r.handleEdgeTableLineFull (x, w);
  1310. }
  1311. }
  1312. }
  1313. private:
  1314. //==============================================================================
  1315. class SubRectangleIterator
  1316. {
  1317. public:
  1318. SubRectangleIterator (const RectangleList& clip_, const Rectangle<int>& area_)
  1319. : clip (clip_), area (area_)
  1320. {
  1321. }
  1322. template <class Renderer>
  1323. void iterate (Renderer& r) const noexcept
  1324. {
  1325. RectangleList::Iterator iter (clip);
  1326. while (iter.next())
  1327. {
  1328. const Rectangle<int> rect (iter.getRectangle()->getIntersection (area));
  1329. if (! rect.isEmpty())
  1330. {
  1331. const int x = rect.getX();
  1332. const int w = rect.getWidth();
  1333. const int bottom = rect.getBottom();
  1334. for (int y = rect.getY(); y < bottom; ++y)
  1335. {
  1336. r.setEdgeTableYPos (y);
  1337. r.handleEdgeTableLineFull (x, w);
  1338. }
  1339. }
  1340. }
  1341. }
  1342. private:
  1343. const RectangleList& clip;
  1344. const Rectangle<int> area;
  1345. JUCE_DECLARE_NON_COPYABLE (SubRectangleIterator);
  1346. };
  1347. //==============================================================================
  1348. class SubRectangleIteratorFloat
  1349. {
  1350. public:
  1351. SubRectangleIteratorFloat (const RectangleList& clip_, const Rectangle<float>& area_)
  1352. : clip (clip_), area (area_)
  1353. {
  1354. }
  1355. template <class Renderer>
  1356. void iterate (Renderer& r) const noexcept
  1357. {
  1358. int left = roundToInt (area.getX() * 256.0f);
  1359. int top = roundToInt (area.getY() * 256.0f);
  1360. int right = roundToInt (area.getRight() * 256.0f);
  1361. int bottom = roundToInt (area.getBottom() * 256.0f);
  1362. int totalTop, totalLeft, totalBottom, totalRight;
  1363. int topAlpha, leftAlpha, bottomAlpha, rightAlpha;
  1364. if ((top >> 8) == (bottom >> 8))
  1365. {
  1366. topAlpha = bottom - top;
  1367. bottomAlpha = 0;
  1368. totalTop = top >> 8;
  1369. totalBottom = bottom = top = totalTop + 1;
  1370. }
  1371. else
  1372. {
  1373. if ((top & 255) == 0)
  1374. {
  1375. topAlpha = 0;
  1376. top = totalTop = (top >> 8);
  1377. }
  1378. else
  1379. {
  1380. topAlpha = 255 - (top & 255);
  1381. totalTop = (top >> 8);
  1382. top = totalTop + 1;
  1383. }
  1384. bottomAlpha = bottom & 255;
  1385. bottom >>= 8;
  1386. totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0);
  1387. }
  1388. if ((left >> 8) == (right >> 8))
  1389. {
  1390. leftAlpha = right - left;
  1391. rightAlpha = 0;
  1392. totalLeft = (left >> 8);
  1393. totalRight = right = left = totalLeft + 1;
  1394. }
  1395. else
  1396. {
  1397. if ((left & 255) == 0)
  1398. {
  1399. leftAlpha = 0;
  1400. left = totalLeft = (left >> 8);
  1401. }
  1402. else
  1403. {
  1404. leftAlpha = 255 - (left & 255);
  1405. totalLeft = (left >> 8);
  1406. left = totalLeft + 1;
  1407. }
  1408. rightAlpha = right & 255;
  1409. right >>= 8;
  1410. totalRight = right + (rightAlpha != 0 ? 1 : 0);
  1411. }
  1412. RectangleList::Iterator iter (clip);
  1413. while (iter.next())
  1414. {
  1415. const int clipLeft = iter.getRectangle()->getX();
  1416. const int clipRight = iter.getRectangle()->getRight();
  1417. const int clipTop = iter.getRectangle()->getY();
  1418. const int clipBottom = iter.getRectangle()->getBottom();
  1419. if (totalBottom > clipTop && totalTop < clipBottom && totalRight > clipLeft && totalLeft < clipRight)
  1420. {
  1421. if (right - left == 1 && leftAlpha + rightAlpha == 0) // special case for 1-pix vertical lines
  1422. {
  1423. if (topAlpha != 0 && totalTop >= clipTop)
  1424. {
  1425. r.setEdgeTableYPos (totalTop);
  1426. r.handleEdgeTablePixel (left, topAlpha);
  1427. }
  1428. const int endY = jmin (bottom, clipBottom);
  1429. for (int y = jmax (clipTop, top); y < endY; ++y)
  1430. {
  1431. r.setEdgeTableYPos (y);
  1432. r.handleEdgeTablePixelFull (left);
  1433. }
  1434. if (bottomAlpha != 0 && bottom < clipBottom)
  1435. {
  1436. r.setEdgeTableYPos (bottom);
  1437. r.handleEdgeTablePixel (left, bottomAlpha);
  1438. }
  1439. }
  1440. else
  1441. {
  1442. const int clippedLeft = jmax (left, clipLeft);
  1443. const int clippedWidth = jmin (right, clipRight) - clippedLeft;
  1444. const bool doLeftAlpha = leftAlpha != 0 && totalLeft >= clipLeft;
  1445. const bool doRightAlpha = rightAlpha != 0 && right < clipRight;
  1446. if (topAlpha != 0 && totalTop >= clipTop)
  1447. {
  1448. r.setEdgeTableYPos (totalTop);
  1449. if (doLeftAlpha) r.handleEdgeTablePixel (totalLeft, (leftAlpha * topAlpha) >> 8);
  1450. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, topAlpha);
  1451. if (doRightAlpha) r.handleEdgeTablePixel (right, (rightAlpha * topAlpha) >> 8);
  1452. }
  1453. const int endY = jmin (bottom, clipBottom);
  1454. for (int y = jmax (clipTop, top); y < endY; ++y)
  1455. {
  1456. r.setEdgeTableYPos (y);
  1457. if (doLeftAlpha) r.handleEdgeTablePixel (totalLeft, leftAlpha);
  1458. if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth);
  1459. if (doRightAlpha) r.handleEdgeTablePixel (right, rightAlpha);
  1460. }
  1461. if (bottomAlpha != 0 && bottom < clipBottom)
  1462. {
  1463. r.setEdgeTableYPos (bottom);
  1464. if (doLeftAlpha) r.handleEdgeTablePixel (totalLeft, (leftAlpha * bottomAlpha) >> 8);
  1465. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, bottomAlpha);
  1466. if (doRightAlpha) r.handleEdgeTablePixel (right, (rightAlpha * bottomAlpha) >> 8);
  1467. }
  1468. }
  1469. }
  1470. }
  1471. }
  1472. private:
  1473. const RectangleList& clip;
  1474. const Rectangle<float>& area;
  1475. JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat);
  1476. };
  1477. ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&);
  1478. };
  1479. }
  1480. //==============================================================================
  1481. class LowLevelGraphicsSoftwareRenderer::SavedState
  1482. {
  1483. public:
  1484. SavedState (const Image& image_, const Rectangle<int>& clip_, const int xOffset_, const int yOffset_)
  1485. : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)),
  1486. transform (xOffset_, yOffset_),
  1487. interpolationQuality (Graphics::mediumResamplingQuality),
  1488. transparencyLayerAlpha (1.0f)
  1489. {
  1490. }
  1491. SavedState (const Image& image_, const RectangleList& clip_, const int xOffset_, const int yOffset_)
  1492. : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)),
  1493. transform (xOffset_, yOffset_),
  1494. interpolationQuality (Graphics::mediumResamplingQuality),
  1495. transparencyLayerAlpha (1.0f)
  1496. {
  1497. }
  1498. SavedState (const SavedState& other)
  1499. : image (other.image), clip (other.clip), transform (other.transform),
  1500. font (other.font), fillType (other.fillType),
  1501. interpolationQuality (other.interpolationQuality),
  1502. transparencyLayerAlpha (other.transparencyLayerAlpha)
  1503. {
  1504. }
  1505. bool clipToRectangle (const Rectangle<int>& r)
  1506. {
  1507. if (clip != nullptr)
  1508. {
  1509. if (transform.isOnlyTranslated)
  1510. {
  1511. cloneClipIfMultiplyReferenced();
  1512. clip = clip->clipToRectangle (transform.translated (r));
  1513. }
  1514. else
  1515. {
  1516. Path p;
  1517. p.addRectangle (r);
  1518. clipToPath (p, AffineTransform::identity);
  1519. }
  1520. }
  1521. return clip != nullptr;
  1522. }
  1523. bool clipToRectangleList (const RectangleList& r)
  1524. {
  1525. if (clip != nullptr)
  1526. {
  1527. if (transform.isOnlyTranslated)
  1528. {
  1529. cloneClipIfMultiplyReferenced();
  1530. RectangleList offsetList (r);
  1531. offsetList.offsetAll (transform.xOffset, transform.yOffset);
  1532. clip = clip->clipToRectangleList (offsetList);
  1533. }
  1534. else
  1535. {
  1536. clipToPath (r.toPath(), AffineTransform::identity);
  1537. }
  1538. }
  1539. return clip != nullptr;
  1540. }
  1541. bool excludeClipRectangle (const Rectangle<int>& r)
  1542. {
  1543. if (clip != nullptr)
  1544. {
  1545. cloneClipIfMultiplyReferenced();
  1546. if (transform.isOnlyTranslated)
  1547. {
  1548. clip = clip->excludeClipRectangle (transform.translated (r));
  1549. }
  1550. else
  1551. {
  1552. Path p;
  1553. p.addRectangle (r.toFloat());
  1554. p.applyTransform (transform.complexTransform);
  1555. p.addRectangle (clip->getClipBounds().toFloat());
  1556. p.setUsingNonZeroWinding (false);
  1557. clip = clip->clipToPath (p, AffineTransform::identity);
  1558. }
  1559. }
  1560. return clip != nullptr;
  1561. }
  1562. void clipToPath (const Path& p, const AffineTransform& t)
  1563. {
  1564. if (clip != nullptr)
  1565. {
  1566. cloneClipIfMultiplyReferenced();
  1567. clip = clip->clipToPath (p, transform.getTransformWith (t));
  1568. }
  1569. }
  1570. void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t)
  1571. {
  1572. if (clip != nullptr)
  1573. {
  1574. if (sourceImage.hasAlphaChannel())
  1575. {
  1576. cloneClipIfMultiplyReferenced();
  1577. clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t),
  1578. interpolationQuality != Graphics::lowResamplingQuality);
  1579. }
  1580. else
  1581. {
  1582. Path p;
  1583. p.addRectangle (sourceImage.getBounds());
  1584. clipToPath (p, t);
  1585. }
  1586. }
  1587. }
  1588. bool clipRegionIntersects (const Rectangle<int>& r) const
  1589. {
  1590. if (clip != nullptr)
  1591. {
  1592. if (transform.isOnlyTranslated)
  1593. return clip->clipRegionIntersects (transform.translated (r));
  1594. else
  1595. return getClipBounds().intersects (r);
  1596. }
  1597. return false;
  1598. }
  1599. Rectangle<int> getUntransformedClipBounds() const
  1600. {
  1601. return clip != nullptr ? clip->getClipBounds()
  1602. : Rectangle<int>();
  1603. }
  1604. Rectangle<int> getClipBounds() const
  1605. {
  1606. return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds())
  1607. : Rectangle<int>();
  1608. }
  1609. SavedState* beginTransparencyLayer (float opacity)
  1610. {
  1611. const Rectangle<int> layerBounds (getUntransformedClipBounds());
  1612. SavedState* s = new SavedState (*this);
  1613. s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true);
  1614. s->transparencyLayerAlpha = opacity;
  1615. s->transform.moveOriginInDeviceSpace (-layerBounds.getX(), -layerBounds.getY());
  1616. s->cloneClipIfMultiplyReferenced();
  1617. s->clip = s->clip->translated (-layerBounds.getPosition());
  1618. return s;
  1619. }
  1620. void endTransparencyLayer (SavedState& layerState)
  1621. {
  1622. const Rectangle<int> layerBounds (getUntransformedClipBounds());
  1623. const ScopedPointer<LowLevelGraphicsContext> g (image.createLowLevelContext());
  1624. g->setOpacity (layerState.transparencyLayerAlpha);
  1625. g->drawImage (layerState.image, AffineTransform::translation ((float) layerBounds.getX(),
  1626. (float) layerBounds.getY()), false);
  1627. }
  1628. //==============================================================================
  1629. void fillRect (const Rectangle<int>& r, const bool replaceContents)
  1630. {
  1631. if (clip != nullptr)
  1632. {
  1633. if (transform.isOnlyTranslated)
  1634. {
  1635. if (fillType.isColour())
  1636. {
  1637. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1638. clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB(), replaceContents);
  1639. }
  1640. else
  1641. {
  1642. const Rectangle<int> totalClip (clip->getClipBounds());
  1643. const Rectangle<int> clipped (totalClip.getIntersection (transform.translated (r)));
  1644. if (! clipped.isEmpty())
  1645. fillShape (new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false);
  1646. }
  1647. }
  1648. else
  1649. {
  1650. Path p;
  1651. p.addRectangle (r);
  1652. fillPath (p, AffineTransform::identity);
  1653. }
  1654. }
  1655. }
  1656. void fillRect (const Rectangle<float>& r)
  1657. {
  1658. if (clip != nullptr)
  1659. {
  1660. if (transform.isOnlyTranslated)
  1661. {
  1662. if (fillType.isColour())
  1663. {
  1664. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1665. clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB());
  1666. }
  1667. else
  1668. {
  1669. const Rectangle<float> totalClip (clip->getClipBounds().toFloat());
  1670. const Rectangle<float> clipped (totalClip.getIntersection (transform.translated (r)));
  1671. if (! clipped.isEmpty())
  1672. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false);
  1673. }
  1674. }
  1675. else
  1676. {
  1677. Path p;
  1678. p.addRectangle (r);
  1679. fillPath (p, AffineTransform::identity);
  1680. }
  1681. }
  1682. }
  1683. void fillPath (const Path& path, const AffineTransform& t)
  1684. {
  1685. if (clip != nullptr)
  1686. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, transform.getTransformWith (t)), false);
  1687. }
  1688. void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y)
  1689. {
  1690. jassert (transform.isOnlyTranslated);
  1691. if (clip != nullptr)
  1692. {
  1693. SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (edgeTable);
  1694. SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill (edgeTableClip);
  1695. edgeTableClip->edgeTable.translate (x + transform.xOffset, y + transform.yOffset);
  1696. fillShape (shapeToFill, false);
  1697. }
  1698. }
  1699. void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& t)
  1700. {
  1701. const ScopedPointer<EdgeTable> et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t)));
  1702. if (et != nullptr && clip != nullptr)
  1703. {
  1704. SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (*et);
  1705. SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill (edgeTableClip);
  1706. fillShape (shapeToFill, false);
  1707. }
  1708. }
  1709. void fillShape (SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents)
  1710. {
  1711. jassert (clip != nullptr);
  1712. shapeToFill = clip->applyClipTo (shapeToFill);
  1713. if (shapeToFill != nullptr)
  1714. {
  1715. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1716. if (fillType.isGradient())
  1717. {
  1718. jassert (! replaceContents); // that option is just for solid colours
  1719. ColourGradient g2 (*(fillType.gradient));
  1720. g2.multiplyOpacity (fillType.getOpacity());
  1721. AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f));
  1722. const bool isIdentity = t.isOnlyTranslation();
  1723. if (isIdentity)
  1724. {
  1725. // If our translation doesn't involve any distortion, we can speed it up..
  1726. g2.point1.applyTransform (t);
  1727. g2.point2.applyTransform (t);
  1728. t = AffineTransform::identity;
  1729. }
  1730. shapeToFill->fillAllWithGradient (destData, g2, t, isIdentity);
  1731. }
  1732. else if (fillType.isTiledImage())
  1733. {
  1734. renderImage (fillType.image, fillType.transform, shapeToFill);
  1735. }
  1736. else
  1737. {
  1738. shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents);
  1739. }
  1740. }
  1741. }
  1742. //==============================================================================
  1743. void renderImage (const Image& sourceImage, const AffineTransform& trans,
  1744. const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion)
  1745. {
  1746. const AffineTransform t (transform.getTransformWith (trans));
  1747. const Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1748. const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly);
  1749. const int alpha = fillType.colour.getAlpha();
  1750. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  1751. if (t.isOnlyTranslation())
  1752. {
  1753. // If our translation doesn't involve any distortion, just use a simple blit..
  1754. int tx = (int) (t.getTranslationX() * 256.0f);
  1755. int ty = (int) (t.getTranslationY() * 256.0f);
  1756. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  1757. {
  1758. tx = ((tx + 128) >> 8);
  1759. ty = ((ty + 128) >> 8);
  1760. if (tiledFillClipRegion != nullptr)
  1761. {
  1762. tiledFillClipRegion->renderImageUntransformed (destData, srcData, alpha, tx, ty, true);
  1763. }
  1764. else
  1765. {
  1766. Rectangle<int> area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight());
  1767. area = area.getIntersection (image.getBounds());
  1768. if (! area.isEmpty())
  1769. {
  1770. SoftwareRendererClasses::ClipRegionBase::Ptr c (new SoftwareRendererClasses::ClipRegion_EdgeTable (area));
  1771. c = clip->applyClipTo (c);
  1772. if (c != nullptr)
  1773. c->renderImageUntransformed (destData, srcData, alpha, tx, ty, false);
  1774. }
  1775. }
  1776. return;
  1777. }
  1778. }
  1779. if (t.isSingularity())
  1780. return;
  1781. if (tiledFillClipRegion != nullptr)
  1782. {
  1783. tiledFillClipRegion->renderImageTransformed (destData, srcData, alpha, t, betterQuality, true);
  1784. }
  1785. else
  1786. {
  1787. Path p;
  1788. p.addRectangle (sourceImage.getBounds());
  1789. SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->clone());
  1790. c = c->clipToPath (p, t);
  1791. if (c != nullptr)
  1792. c->renderImageTransformed (destData, srcData, alpha, t, betterQuality, false);
  1793. }
  1794. }
  1795. //==============================================================================
  1796. Image image;
  1797. SoftwareRendererClasses::ClipRegionBase::Ptr clip;
  1798. RenderingHelpers::TranslationOrTransform transform;
  1799. Font font;
  1800. FillType fillType;
  1801. Graphics::ResamplingQuality interpolationQuality;
  1802. private:
  1803. float transparencyLayerAlpha;
  1804. void cloneClipIfMultiplyReferenced()
  1805. {
  1806. if (clip->getReferenceCount() > 1)
  1807. clip = clip->clone();
  1808. }
  1809. SavedState& operator= (const SavedState&);
  1810. };
  1811. //==============================================================================
  1812. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image_)
  1813. : image (image_),
  1814. currentState (new SavedState (image_, image_.getBounds(), 0, 0))
  1815. {
  1816. }
  1817. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image_, const int xOffset, const int yOffset,
  1818. const RectangleList& initialClip)
  1819. : image (image_),
  1820. currentState (new SavedState (image_, initialClip, xOffset, yOffset))
  1821. {
  1822. }
  1823. LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer()
  1824. {
  1825. }
  1826. bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const
  1827. {
  1828. return false;
  1829. }
  1830. //==============================================================================
  1831. void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y)
  1832. {
  1833. currentState->transform.setOrigin (x, y);
  1834. }
  1835. void LowLevelGraphicsSoftwareRenderer::addTransform (const AffineTransform& transform)
  1836. {
  1837. currentState->transform.addTransform (transform);
  1838. }
  1839. float LowLevelGraphicsSoftwareRenderer::getScaleFactor()
  1840. {
  1841. return currentState->transform.getScaleFactor();
  1842. }
  1843. bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle<int>& r)
  1844. {
  1845. return currentState->clipToRectangle (r);
  1846. }
  1847. bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion)
  1848. {
  1849. return currentState->clipToRectangleList (clipRegion);
  1850. }
  1851. void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle<int>& r)
  1852. {
  1853. currentState->excludeClipRectangle (r);
  1854. }
  1855. void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform)
  1856. {
  1857. currentState->clipToPath (path, transform);
  1858. }
  1859. void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
  1860. {
  1861. currentState->clipToImageAlpha (sourceImage, transform);
  1862. }
  1863. bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle<int>& r)
  1864. {
  1865. return currentState->clipRegionIntersects (r);
  1866. }
  1867. Rectangle<int> LowLevelGraphicsSoftwareRenderer::getClipBounds() const
  1868. {
  1869. return currentState->getClipBounds();
  1870. }
  1871. bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const
  1872. {
  1873. return currentState->clip == nullptr;
  1874. }
  1875. //==============================================================================
  1876. void LowLevelGraphicsSoftwareRenderer::saveState()
  1877. {
  1878. stateStack.add (new SavedState (*currentState));
  1879. }
  1880. void LowLevelGraphicsSoftwareRenderer::restoreState()
  1881. {
  1882. SavedState* const top = stateStack.getLast();
  1883. if (top != nullptr)
  1884. {
  1885. currentState = top;
  1886. stateStack.removeLast (1, false);
  1887. }
  1888. else
  1889. {
  1890. jassertfalse; // trying to pop with an empty stack!
  1891. }
  1892. }
  1893. void LowLevelGraphicsSoftwareRenderer::beginTransparencyLayer (float opacity)
  1894. {
  1895. saveState();
  1896. currentState = currentState->beginTransparencyLayer (opacity);
  1897. }
  1898. void LowLevelGraphicsSoftwareRenderer::endTransparencyLayer()
  1899. {
  1900. const ScopedPointer<SavedState> layer (currentState);
  1901. restoreState();
  1902. currentState->endTransparencyLayer (*layer);
  1903. }
  1904. //==============================================================================
  1905. void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType)
  1906. {
  1907. currentState->fillType = fillType;
  1908. }
  1909. void LowLevelGraphicsSoftwareRenderer::setOpacity (float newOpacity)
  1910. {
  1911. currentState->fillType.setOpacity (newOpacity);
  1912. }
  1913. void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality)
  1914. {
  1915. currentState->interpolationQuality = quality;
  1916. }
  1917. //==============================================================================
  1918. void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle<int>& r, const bool replaceExistingContents)
  1919. {
  1920. currentState->fillRect (r, replaceExistingContents);
  1921. }
  1922. void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform)
  1923. {
  1924. currentState->fillPath (path, transform);
  1925. }
  1926. void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform, const bool fillEntireClipAsTiles)
  1927. {
  1928. currentState->renderImage (sourceImage, transform, fillEntireClipAsTiles ? currentState->clip : 0);
  1929. }
  1930. void LowLevelGraphicsSoftwareRenderer::drawLine (const Line <float>& line)
  1931. {
  1932. Path p;
  1933. p.addLineSegment (line, 1.0f);
  1934. fillPath (p, AffineTransform::identity);
  1935. }
  1936. void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, float top, float bottom)
  1937. {
  1938. if (bottom > top)
  1939. currentState->fillRect (Rectangle<float> ((float) x, top, 1.0f, bottom - top));
  1940. }
  1941. void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, float left, float right)
  1942. {
  1943. if (right > left)
  1944. currentState->fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
  1945. }
  1946. //==============================================================================
  1947. class CachedGlyphEdgeTable
  1948. {
  1949. public:
  1950. CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {}
  1951. void draw (LowLevelGraphicsSoftwareRenderer::SavedState& state, float x, const float y) const
  1952. {
  1953. if (snapToIntegerCoordinate)
  1954. x = std::floor (x + 0.5f);
  1955. if (edgeTable != nullptr)
  1956. state.fillEdgeTable (*edgeTable, x, roundToInt (y));
  1957. }
  1958. void generate (const Font& newFont, const int glyphNumber)
  1959. {
  1960. font = newFont;
  1961. snapToIntegerCoordinate = newFont.getTypeface()->isHinted();
  1962. glyph = glyphNumber;
  1963. const float fontHeight = font.getHeight();
  1964. edgeTable = font.getTypeface()->getEdgeTableForGlyph (glyphNumber,
  1965. AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
  1966. #if JUCE_MAC || JUCE_IOS
  1967. .translated (0.0f, -0.5f)
  1968. #endif
  1969. );
  1970. }
  1971. Font font;
  1972. int glyph, lastAccessCount;
  1973. bool snapToIntegerCoordinate;
  1974. private:
  1975. ScopedPointer <EdgeTable> edgeTable;
  1976. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable);
  1977. };
  1978. void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
  1979. {
  1980. Font& f = currentState->font;
  1981. if (transform.isOnlyTranslation() && currentState->transform.isOnlyTranslated)
  1982. {
  1983. RenderingHelpers::GlyphCache <CachedGlyphEdgeTable, SavedState>::getInstance()
  1984. .drawGlyph (*currentState, f, glyphNumber,
  1985. transform.getTranslationX(),
  1986. transform.getTranslationY());
  1987. }
  1988. else
  1989. {
  1990. const float fontHeight = f.getHeight();
  1991. currentState->drawGlyph (f, glyphNumber,
  1992. AffineTransform::scale (fontHeight * f.getHorizontalScale(), fontHeight)
  1993. .followedBy (transform));
  1994. }
  1995. }
  1996. void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { currentState->font = newFont; }
  1997. Font LowLevelGraphicsSoftwareRenderer::getFont() { return currentState->font; }
  1998. #if JUCE_MSVC
  1999. #pragma warning (pop)
  2000. #if JUCE_DEBUG
  2001. #pragma optimize ("", on) // resets optimisations to the project defaults
  2002. #endif
  2003. #endif
  2004. END_JUCE_NAMESPACE