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.

2282 lines
84KB

  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 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 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 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 void translate (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. void translate (const Point<int>& delta)
  1104. {
  1105. edgeTable.translate ((float) delta.getX(), delta.getY());
  1106. }
  1107. bool clipRegionIntersects (const Rectangle<int>& r) const
  1108. {
  1109. return edgeTable.getMaximumBounds().intersects (r);
  1110. }
  1111. Rectangle<int> getClipBounds() const
  1112. {
  1113. return edgeTable.getMaximumBounds();
  1114. }
  1115. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB& colour, bool replaceContents) const
  1116. {
  1117. const Rectangle<int> totalClip (edgeTable.getMaximumBounds());
  1118. const Rectangle<int> clipped (totalClip.getIntersection (area));
  1119. if (! clipped.isEmpty())
  1120. {
  1121. ClipRegion_EdgeTable et (clipped);
  1122. et.edgeTable.clipToEdgeTable (edgeTable);
  1123. et.fillAllWithColour (destData, colour, replaceContents);
  1124. }
  1125. }
  1126. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>& area, const PixelARGB& colour) const
  1127. {
  1128. const Rectangle<float> totalClip (edgeTable.getMaximumBounds().toFloat());
  1129. const Rectangle<float> clipped (totalClip.getIntersection (area));
  1130. if (! clipped.isEmpty())
  1131. {
  1132. ClipRegion_EdgeTable et (clipped);
  1133. et.edgeTable.clipToEdgeTable (edgeTable);
  1134. et.fillAllWithColour (destData, colour, false);
  1135. }
  1136. }
  1137. void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const
  1138. {
  1139. switch (destData.pixelFormat)
  1140. {
  1141. case Image::ARGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1142. case Image::RGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1143. default: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1144. }
  1145. }
  1146. void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const
  1147. {
  1148. HeapBlock <PixelARGB> lookupTable;
  1149. const int numLookupEntries = gradient.createLookupTable (transform, lookupTable);
  1150. jassert (numLookupEntries > 0);
  1151. switch (destData.pixelFormat)
  1152. {
  1153. case Image::ARGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
  1154. case Image::RGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
  1155. default: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
  1156. }
  1157. }
  1158. void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const
  1159. {
  1160. renderImageTransformedInternal (edgeTable, destData, srcData, alpha, transform, betterQuality, tiledFill);
  1161. }
  1162. void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const
  1163. {
  1164. renderImageUntransformedInternal (edgeTable, destData, srcData, alpha, x, y, tiledFill);
  1165. }
  1166. EdgeTable edgeTable;
  1167. private:
  1168. //==============================================================================
  1169. template <class SrcPixelType>
  1170. void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType*)
  1171. {
  1172. TransformedImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, transform, 255, betterQuality);
  1173. for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y)
  1174. renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(),
  1175. edgeTable.getMaximumBounds().getWidth());
  1176. }
  1177. template <class SrcPixelType>
  1178. void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*)
  1179. {
  1180. Rectangle<int> r (imageX, imageY, srcData.width, srcData.height);
  1181. edgeTable.clipToRectangle (r);
  1182. ImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, 255, imageX, imageY);
  1183. for (int y = 0; y < r.getHeight(); ++y)
  1184. renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth());
  1185. }
  1186. ClipRegion_EdgeTable& operator= (const ClipRegion_EdgeTable&);
  1187. };
  1188. //==============================================================================
  1189. class ClipRegion_RectangleList : public ClipRegionBase
  1190. {
  1191. public:
  1192. ClipRegion_RectangleList (const Rectangle<int>& r) : clip (r) {}
  1193. ClipRegion_RectangleList (const RectangleList& r) : clip (r) {}
  1194. ClipRegion_RectangleList (const ClipRegion_RectangleList& other) : clip (other.clip) {}
  1195. Ptr clone() const
  1196. {
  1197. return new ClipRegion_RectangleList (*this);
  1198. }
  1199. Ptr applyClipTo (const Ptr& target) const
  1200. {
  1201. return target->clipToRectangleList (clip);
  1202. }
  1203. Ptr clipToRectangle (const Rectangle<int>& r)
  1204. {
  1205. clip.clipTo (r);
  1206. return clip.isEmpty() ? nullptr : this;
  1207. }
  1208. Ptr clipToRectangleList (const RectangleList& r)
  1209. {
  1210. clip.clipTo (r);
  1211. return clip.isEmpty() ? nullptr : this;
  1212. }
  1213. Ptr excludeClipRectangle (const Rectangle<int>& r)
  1214. {
  1215. clip.subtract (r);
  1216. return clip.isEmpty() ? nullptr : this;
  1217. }
  1218. Ptr clipToPath (const Path& p, const AffineTransform& transform)
  1219. {
  1220. return toEdgeTable()->clipToPath (p, transform);
  1221. }
  1222. Ptr clipToEdgeTable (const EdgeTable& et)
  1223. {
  1224. return toEdgeTable()->clipToEdgeTable (et);
  1225. }
  1226. Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality)
  1227. {
  1228. return toEdgeTable()->clipToImageAlpha (image, transform, betterQuality);
  1229. }
  1230. void translate (const Point<int>& delta)
  1231. {
  1232. clip.offsetAll (delta.getX(), delta.getY());
  1233. }
  1234. bool clipRegionIntersects (const Rectangle<int>& r) const
  1235. {
  1236. return clip.intersects (r);
  1237. }
  1238. Rectangle<int> getClipBounds() const
  1239. {
  1240. return clip.getBounds();
  1241. }
  1242. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB& colour, bool replaceContents) const
  1243. {
  1244. SubRectangleIterator iter (clip, area);
  1245. switch (destData.pixelFormat)
  1246. {
  1247. case Image::ARGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1248. case Image::RGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1249. default: renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1250. }
  1251. }
  1252. void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>& area, const PixelARGB& colour) const
  1253. {
  1254. SubRectangleIteratorFloat iter (clip, area);
  1255. switch (destData.pixelFormat)
  1256. {
  1257. case Image::ARGB: renderSolidFill (iter, destData, colour, false, (PixelARGB*) 0); break;
  1258. case Image::RGB: renderSolidFill (iter, destData, colour, false, (PixelRGB*) 0); break;
  1259. default: renderSolidFill (iter, destData, colour, false, (PixelAlpha*) 0); break;
  1260. }
  1261. }
  1262. void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const
  1263. {
  1264. switch (destData.pixelFormat)
  1265. {
  1266. case Image::ARGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelARGB*) 0); break;
  1267. case Image::RGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelRGB*) 0); break;
  1268. default: renderSolidFill (*this, destData, colour, replaceContents, (PixelAlpha*) 0); break;
  1269. }
  1270. }
  1271. void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const
  1272. {
  1273. HeapBlock <PixelARGB> lookupTable;
  1274. const int numLookupEntries = gradient.createLookupTable (transform, lookupTable);
  1275. jassert (numLookupEntries > 0);
  1276. switch (destData.pixelFormat)
  1277. {
  1278. case Image::ARGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
  1279. case Image::RGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
  1280. default: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
  1281. }
  1282. }
  1283. void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const
  1284. {
  1285. renderImageTransformedInternal (*this, destData, srcData, alpha, transform, betterQuality, tiledFill);
  1286. }
  1287. void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const
  1288. {
  1289. renderImageUntransformedInternal (*this, destData, srcData, alpha, x, y, tiledFill);
  1290. }
  1291. RectangleList clip;
  1292. //==============================================================================
  1293. template <class Renderer>
  1294. void iterate (Renderer& r) const noexcept
  1295. {
  1296. RectangleList::Iterator iter (clip);
  1297. while (iter.next())
  1298. {
  1299. const Rectangle<int> rect (*iter.getRectangle());
  1300. const int x = rect.getX();
  1301. const int w = rect.getWidth();
  1302. jassert (w > 0);
  1303. const int bottom = rect.getBottom();
  1304. for (int y = rect.getY(); y < bottom; ++y)
  1305. {
  1306. r.setEdgeTableYPos (y);
  1307. r.handleEdgeTableLineFull (x, w);
  1308. }
  1309. }
  1310. }
  1311. private:
  1312. //==============================================================================
  1313. class SubRectangleIterator
  1314. {
  1315. public:
  1316. SubRectangleIterator (const RectangleList& clip_, const Rectangle<int>& area_)
  1317. : clip (clip_), area (area_)
  1318. {
  1319. }
  1320. template <class Renderer>
  1321. void iterate (Renderer& r) const noexcept
  1322. {
  1323. RectangleList::Iterator iter (clip);
  1324. while (iter.next())
  1325. {
  1326. const Rectangle<int> rect (iter.getRectangle()->getIntersection (area));
  1327. if (! rect.isEmpty())
  1328. {
  1329. const int x = rect.getX();
  1330. const int w = rect.getWidth();
  1331. const int bottom = rect.getBottom();
  1332. for (int y = rect.getY(); y < bottom; ++y)
  1333. {
  1334. r.setEdgeTableYPos (y);
  1335. r.handleEdgeTableLineFull (x, w);
  1336. }
  1337. }
  1338. }
  1339. }
  1340. private:
  1341. const RectangleList& clip;
  1342. const Rectangle<int> area;
  1343. JUCE_DECLARE_NON_COPYABLE (SubRectangleIterator);
  1344. };
  1345. //==============================================================================
  1346. class SubRectangleIteratorFloat
  1347. {
  1348. public:
  1349. SubRectangleIteratorFloat (const RectangleList& clip_, const Rectangle<float>& area_) noexcept
  1350. : clip (clip_), area (area_)
  1351. {
  1352. }
  1353. template <class Renderer>
  1354. void iterate (Renderer& r) const noexcept
  1355. {
  1356. const RenderingHelpers::FloatRectangleRasterisingInfo f (area);
  1357. RectangleList::Iterator iter (clip);
  1358. while (iter.next())
  1359. {
  1360. const int clipLeft = iter.getRectangle()->getX();
  1361. const int clipRight = iter.getRectangle()->getRight();
  1362. const int clipTop = iter.getRectangle()->getY();
  1363. const int clipBottom = iter.getRectangle()->getBottom();
  1364. if (f.totalBottom > clipTop && f.totalTop < clipBottom && f.totalRight > clipLeft && f.totalLeft < clipRight)
  1365. {
  1366. if (f.isOnePixelWide())
  1367. {
  1368. if (f.topAlpha != 0 && f.totalTop >= clipTop)
  1369. {
  1370. r.setEdgeTableYPos (f.totalTop);
  1371. r.handleEdgeTablePixel (f.left, f.topAlpha);
  1372. }
  1373. const int endY = jmin (f.bottom, clipBottom);
  1374. for (int y = jmax (clipTop, f.top); y < endY; ++y)
  1375. {
  1376. r.setEdgeTableYPos (y);
  1377. r.handleEdgeTablePixelFull (f.left);
  1378. }
  1379. if (f.bottomAlpha != 0 && f.bottom < clipBottom)
  1380. {
  1381. r.setEdgeTableYPos (f.bottom);
  1382. r.handleEdgeTablePixel (f.left, f.bottomAlpha);
  1383. }
  1384. }
  1385. else
  1386. {
  1387. const int clippedLeft = jmax (f.left, clipLeft);
  1388. const int clippedWidth = jmin (f.right, clipRight) - clippedLeft;
  1389. const bool doLeftAlpha = f.leftAlpha != 0 && f.totalLeft >= clipLeft;
  1390. const bool doRightAlpha = f.rightAlpha != 0 && f.right < clipRight;
  1391. if (f.topAlpha != 0 && f.totalTop >= clipTop)
  1392. {
  1393. r.setEdgeTableYPos (f.totalTop);
  1394. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getTopLeftCornerAlpha());
  1395. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.topAlpha);
  1396. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getTopRightCornerAlpha());
  1397. }
  1398. const int endY = jmin (f.bottom, clipBottom);
  1399. for (int y = jmax (clipTop, f.top); y < endY; ++y)
  1400. {
  1401. r.setEdgeTableYPos (y);
  1402. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.leftAlpha);
  1403. if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth);
  1404. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.rightAlpha);
  1405. }
  1406. if (f.bottomAlpha != 0 && f.bottom < clipBottom)
  1407. {
  1408. r.setEdgeTableYPos (f.bottom);
  1409. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getBottomLeftCornerAlpha());
  1410. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.bottomAlpha);
  1411. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getBottomRightCornerAlpha());
  1412. }
  1413. }
  1414. }
  1415. }
  1416. }
  1417. private:
  1418. const RectangleList& clip;
  1419. const Rectangle<float>& area;
  1420. JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat);
  1421. };
  1422. inline Ptr toEdgeTable() const { return new ClipRegion_EdgeTable (clip); }
  1423. ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&);
  1424. };
  1425. }
  1426. //==============================================================================
  1427. class LowLevelGraphicsSoftwareRenderer::SavedState
  1428. {
  1429. public:
  1430. SavedState (const Image& image_, const Rectangle<int>& clip_, const int xOffset_, const int yOffset_)
  1431. : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)),
  1432. transform (xOffset_, yOffset_),
  1433. interpolationQuality (Graphics::mediumResamplingQuality),
  1434. transparencyLayerAlpha (1.0f)
  1435. {
  1436. }
  1437. SavedState (const Image& image_, const RectangleList& clip_, const int xOffset_, const int yOffset_)
  1438. : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)),
  1439. transform (xOffset_, yOffset_),
  1440. interpolationQuality (Graphics::mediumResamplingQuality),
  1441. transparencyLayerAlpha (1.0f)
  1442. {
  1443. }
  1444. SavedState (const SavedState& other)
  1445. : image (other.image), clip (other.clip), transform (other.transform),
  1446. font (other.font), fillType (other.fillType),
  1447. interpolationQuality (other.interpolationQuality),
  1448. transparencyLayerAlpha (other.transparencyLayerAlpha)
  1449. {
  1450. }
  1451. bool clipToRectangle (const Rectangle<int>& r)
  1452. {
  1453. if (clip != nullptr)
  1454. {
  1455. if (transform.isOnlyTranslated)
  1456. {
  1457. cloneClipIfMultiplyReferenced();
  1458. clip = clip->clipToRectangle (transform.translated (r));
  1459. }
  1460. else
  1461. {
  1462. Path p;
  1463. p.addRectangle (r);
  1464. clipToPath (p, AffineTransform::identity);
  1465. }
  1466. }
  1467. return clip != nullptr;
  1468. }
  1469. bool clipToRectangleList (const RectangleList& r)
  1470. {
  1471. if (clip != nullptr)
  1472. {
  1473. if (transform.isOnlyTranslated)
  1474. {
  1475. cloneClipIfMultiplyReferenced();
  1476. RectangleList offsetList (r);
  1477. offsetList.offsetAll (transform.xOffset, transform.yOffset);
  1478. clip = clip->clipToRectangleList (offsetList);
  1479. }
  1480. else
  1481. {
  1482. clipToPath (r.toPath(), AffineTransform::identity);
  1483. }
  1484. }
  1485. return clip != nullptr;
  1486. }
  1487. bool excludeClipRectangle (const Rectangle<int>& r)
  1488. {
  1489. if (clip != nullptr)
  1490. {
  1491. cloneClipIfMultiplyReferenced();
  1492. if (transform.isOnlyTranslated)
  1493. {
  1494. clip = clip->excludeClipRectangle (transform.translated (r));
  1495. }
  1496. else
  1497. {
  1498. Path p;
  1499. p.addRectangle (r.toFloat());
  1500. p.applyTransform (transform.complexTransform);
  1501. p.addRectangle (clip->getClipBounds().toFloat());
  1502. p.setUsingNonZeroWinding (false);
  1503. clip = clip->clipToPath (p, AffineTransform::identity);
  1504. }
  1505. }
  1506. return clip != nullptr;
  1507. }
  1508. void clipToPath (const Path& p, const AffineTransform& t)
  1509. {
  1510. if (clip != nullptr)
  1511. {
  1512. cloneClipIfMultiplyReferenced();
  1513. clip = clip->clipToPath (p, transform.getTransformWith (t));
  1514. }
  1515. }
  1516. void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t)
  1517. {
  1518. if (clip != nullptr)
  1519. {
  1520. if (sourceImage.hasAlphaChannel())
  1521. {
  1522. cloneClipIfMultiplyReferenced();
  1523. clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t),
  1524. interpolationQuality != Graphics::lowResamplingQuality);
  1525. }
  1526. else
  1527. {
  1528. Path p;
  1529. p.addRectangle (sourceImage.getBounds());
  1530. clipToPath (p, t);
  1531. }
  1532. }
  1533. }
  1534. bool clipRegionIntersects (const Rectangle<int>& r) const
  1535. {
  1536. if (clip != nullptr)
  1537. {
  1538. if (transform.isOnlyTranslated)
  1539. return clip->clipRegionIntersects (transform.translated (r));
  1540. else
  1541. return getClipBounds().intersects (r);
  1542. }
  1543. return false;
  1544. }
  1545. Rectangle<int> getClipBounds() const
  1546. {
  1547. return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds())
  1548. : Rectangle<int>();
  1549. }
  1550. SavedState* beginTransparencyLayer (float opacity)
  1551. {
  1552. SavedState* s = new SavedState (*this);
  1553. if (clip != nullptr)
  1554. {
  1555. const Rectangle<int> layerBounds (clip->getClipBounds());
  1556. s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true);
  1557. s->transparencyLayerAlpha = opacity;
  1558. s->transform.moveOriginInDeviceSpace (-layerBounds.getX(), -layerBounds.getY());
  1559. s->cloneClipIfMultiplyReferenced();
  1560. s->clip->translate (-layerBounds.getPosition());
  1561. }
  1562. return s;
  1563. }
  1564. void endTransparencyLayer (SavedState& finishedLayerState)
  1565. {
  1566. if (clip != nullptr)
  1567. {
  1568. const Rectangle<int> layerBounds (clip->getClipBounds());
  1569. const ScopedPointer<LowLevelGraphicsContext> g (image.createLowLevelContext());
  1570. g->setOpacity (finishedLayerState.transparencyLayerAlpha);
  1571. g->drawImage (finishedLayerState.image, AffineTransform::translation ((float) layerBounds.getX(),
  1572. (float) layerBounds.getY()));
  1573. }
  1574. }
  1575. //==============================================================================
  1576. void fillRect (const Rectangle<int>& r, const bool replaceContents)
  1577. {
  1578. if (clip != nullptr)
  1579. {
  1580. if (transform.isOnlyTranslated)
  1581. {
  1582. if (fillType.isColour())
  1583. {
  1584. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1585. clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB(), replaceContents);
  1586. }
  1587. else
  1588. {
  1589. const Rectangle<int> totalClip (clip->getClipBounds());
  1590. const Rectangle<int> clipped (totalClip.getIntersection (transform.translated (r)));
  1591. if (! clipped.isEmpty())
  1592. fillShape (new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false);
  1593. }
  1594. }
  1595. else
  1596. {
  1597. Path p;
  1598. p.addRectangle (r);
  1599. fillPath (p, AffineTransform::identity);
  1600. }
  1601. }
  1602. }
  1603. void fillRect (const Rectangle<float>& r)
  1604. {
  1605. if (clip != nullptr)
  1606. {
  1607. if (transform.isOnlyTranslated)
  1608. {
  1609. if (fillType.isColour())
  1610. {
  1611. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1612. clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB());
  1613. }
  1614. else
  1615. {
  1616. const Rectangle<float> totalClip (clip->getClipBounds().toFloat());
  1617. const Rectangle<float> clipped (totalClip.getIntersection (transform.translated (r)));
  1618. if (! clipped.isEmpty())
  1619. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false);
  1620. }
  1621. }
  1622. else
  1623. {
  1624. Path p;
  1625. p.addRectangle (r);
  1626. fillPath (p, AffineTransform::identity);
  1627. }
  1628. }
  1629. }
  1630. void fillPath (const Path& path, const AffineTransform& t)
  1631. {
  1632. if (clip != nullptr)
  1633. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, transform.getTransformWith (t)), false);
  1634. }
  1635. void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y)
  1636. {
  1637. jassert (transform.isOnlyTranslated);
  1638. if (clip != nullptr)
  1639. {
  1640. SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (edgeTable);
  1641. edgeTableClip->edgeTable.translate (x + transform.xOffset,
  1642. y + transform.yOffset);
  1643. fillShape (edgeTableClip, false);
  1644. }
  1645. }
  1646. void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& t)
  1647. {
  1648. if (clip != nullptr)
  1649. {
  1650. const ScopedPointer<EdgeTable> et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t)));
  1651. if (et != nullptr)
  1652. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (*et), false);
  1653. }
  1654. }
  1655. void fillShape (SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents)
  1656. {
  1657. jassert (clip != nullptr);
  1658. shapeToFill = clip->applyClipTo (shapeToFill);
  1659. if (shapeToFill != nullptr)
  1660. {
  1661. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1662. if (fillType.isGradient())
  1663. {
  1664. jassert (! replaceContents); // that option is just for solid colours
  1665. ColourGradient g2 (*(fillType.gradient));
  1666. g2.multiplyOpacity (fillType.getOpacity());
  1667. AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f));
  1668. const bool isIdentity = t.isOnlyTranslation();
  1669. if (isIdentity)
  1670. {
  1671. // If our translation doesn't involve any distortion, we can speed it up..
  1672. g2.point1.applyTransform (t);
  1673. g2.point2.applyTransform (t);
  1674. t = AffineTransform::identity;
  1675. }
  1676. shapeToFill->fillAllWithGradient (destData, g2, t, isIdentity);
  1677. }
  1678. else if (fillType.isTiledImage())
  1679. {
  1680. renderImage (fillType.image, fillType.transform, shapeToFill);
  1681. }
  1682. else
  1683. {
  1684. shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents);
  1685. }
  1686. }
  1687. }
  1688. //==============================================================================
  1689. void renderImage (const Image& sourceImage, const AffineTransform& trans,
  1690. const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion)
  1691. {
  1692. const AffineTransform t (transform.getTransformWith (trans));
  1693. const Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1694. const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly);
  1695. const int alpha = fillType.colour.getAlpha();
  1696. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  1697. if (t.isOnlyTranslation())
  1698. {
  1699. // If our translation doesn't involve any distortion, just use a simple blit..
  1700. int tx = (int) (t.getTranslationX() * 256.0f);
  1701. int ty = (int) (t.getTranslationY() * 256.0f);
  1702. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  1703. {
  1704. tx = ((tx + 128) >> 8);
  1705. ty = ((ty + 128) >> 8);
  1706. if (tiledFillClipRegion != nullptr)
  1707. {
  1708. tiledFillClipRegion->renderImageUntransformed (destData, srcData, alpha, tx, ty, true);
  1709. }
  1710. else
  1711. {
  1712. Rectangle<int> area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight());
  1713. area = area.getIntersection (image.getBounds());
  1714. if (! area.isEmpty())
  1715. {
  1716. SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->applyClipTo (new SoftwareRendererClasses::ClipRegion_EdgeTable (area)));
  1717. if (c != nullptr)
  1718. c->renderImageUntransformed (destData, srcData, alpha, tx, ty, false);
  1719. }
  1720. }
  1721. return;
  1722. }
  1723. }
  1724. if (t.isSingularity())
  1725. return;
  1726. if (tiledFillClipRegion != nullptr)
  1727. {
  1728. tiledFillClipRegion->renderImageTransformed (destData, srcData, alpha, t, betterQuality, true);
  1729. }
  1730. else
  1731. {
  1732. Path p;
  1733. p.addRectangle (sourceImage.getBounds());
  1734. SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->clone());
  1735. c = c->clipToPath (p, t);
  1736. if (c != nullptr)
  1737. c->renderImageTransformed (destData, srcData, alpha, t, betterQuality, false);
  1738. }
  1739. }
  1740. //==============================================================================
  1741. Image image;
  1742. SoftwareRendererClasses::ClipRegionBase::Ptr clip;
  1743. RenderingHelpers::TranslationOrTransform transform;
  1744. Font font;
  1745. FillType fillType;
  1746. Graphics::ResamplingQuality interpolationQuality;
  1747. private:
  1748. float transparencyLayerAlpha;
  1749. void cloneClipIfMultiplyReferenced()
  1750. {
  1751. if (clip->getReferenceCount() > 1)
  1752. clip = clip->clone();
  1753. }
  1754. SavedState& operator= (const SavedState&);
  1755. };
  1756. //==============================================================================
  1757. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image)
  1758. : savedState (new SavedState (image, image.getBounds(), 0, 0))
  1759. {
  1760. }
  1761. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, const int xOffset, const int yOffset,
  1762. const RectangleList& initialClip)
  1763. : savedState (new SavedState (image, initialClip, xOffset, yOffset))
  1764. {
  1765. }
  1766. LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer()
  1767. {
  1768. }
  1769. bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const
  1770. {
  1771. return false;
  1772. }
  1773. //==============================================================================
  1774. void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y)
  1775. {
  1776. savedState->transform.setOrigin (x, y);
  1777. }
  1778. void LowLevelGraphicsSoftwareRenderer::addTransform (const AffineTransform& transform)
  1779. {
  1780. savedState->transform.addTransform (transform);
  1781. }
  1782. float LowLevelGraphicsSoftwareRenderer::getScaleFactor()
  1783. {
  1784. return savedState->transform.getScaleFactor();
  1785. }
  1786. bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle<int>& r)
  1787. {
  1788. return savedState->clipToRectangle (r);
  1789. }
  1790. bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion)
  1791. {
  1792. return savedState->clipToRectangleList (clipRegion);
  1793. }
  1794. void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle<int>& r)
  1795. {
  1796. savedState->excludeClipRectangle (r);
  1797. }
  1798. void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform)
  1799. {
  1800. savedState->clipToPath (path, transform);
  1801. }
  1802. void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
  1803. {
  1804. savedState->clipToImageAlpha (sourceImage, transform);
  1805. }
  1806. bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle<int>& r)
  1807. {
  1808. return savedState->clipRegionIntersects (r);
  1809. }
  1810. Rectangle<int> LowLevelGraphicsSoftwareRenderer::getClipBounds() const
  1811. {
  1812. return savedState->getClipBounds();
  1813. }
  1814. bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const
  1815. {
  1816. return savedState->clip == nullptr;
  1817. }
  1818. //==============================================================================
  1819. void LowLevelGraphicsSoftwareRenderer::saveState() { savedState.save(); }
  1820. void LowLevelGraphicsSoftwareRenderer::restoreState() { savedState.restore(); }
  1821. void LowLevelGraphicsSoftwareRenderer::beginTransparencyLayer (float opacity) { savedState.beginTransparencyLayer (opacity); }
  1822. void LowLevelGraphicsSoftwareRenderer::endTransparencyLayer() { savedState.endTransparencyLayer(); }
  1823. //==============================================================================
  1824. void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType)
  1825. {
  1826. savedState->fillType = fillType;
  1827. }
  1828. void LowLevelGraphicsSoftwareRenderer::setOpacity (float newOpacity)
  1829. {
  1830. savedState->fillType.setOpacity (newOpacity);
  1831. }
  1832. void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality)
  1833. {
  1834. savedState->interpolationQuality = quality;
  1835. }
  1836. //==============================================================================
  1837. void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle<int>& r, const bool replaceExistingContents)
  1838. {
  1839. savedState->fillRect (r, replaceExistingContents);
  1840. }
  1841. void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform)
  1842. {
  1843. savedState->fillPath (path, transform);
  1844. }
  1845. void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform)
  1846. {
  1847. savedState->renderImage (sourceImage, transform, nullptr);
  1848. }
  1849. void LowLevelGraphicsSoftwareRenderer::drawLine (const Line <float>& line)
  1850. {
  1851. Path p;
  1852. p.addLineSegment (line, 1.0f);
  1853. fillPath (p, AffineTransform::identity);
  1854. }
  1855. void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, float top, float bottom)
  1856. {
  1857. if (bottom > top)
  1858. savedState->fillRect (Rectangle<float> ((float) x, top, 1.0f, bottom - top));
  1859. }
  1860. void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, float left, float right)
  1861. {
  1862. if (right > left)
  1863. savedState->fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
  1864. }
  1865. //==============================================================================
  1866. class CachedGlyphEdgeTable
  1867. {
  1868. public:
  1869. CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {}
  1870. void draw (LowLevelGraphicsSoftwareRenderer::SavedState& state, float x, const float y) const
  1871. {
  1872. if (snapToIntegerCoordinate)
  1873. x = std::floor (x + 0.5f);
  1874. if (edgeTable != nullptr)
  1875. state.fillEdgeTable (*edgeTable, x, roundToInt (y));
  1876. }
  1877. void generate (const Font& newFont, const int glyphNumber)
  1878. {
  1879. font = newFont;
  1880. snapToIntegerCoordinate = newFont.getTypeface()->isHinted();
  1881. glyph = glyphNumber;
  1882. const float fontHeight = font.getHeight();
  1883. edgeTable = font.getTypeface()->getEdgeTableForGlyph (glyphNumber,
  1884. AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
  1885. #if JUCE_MAC || JUCE_IOS
  1886. .translated (0.0f, -0.5f)
  1887. #endif
  1888. );
  1889. }
  1890. Font font;
  1891. int glyph, lastAccessCount;
  1892. bool snapToIntegerCoordinate;
  1893. private:
  1894. ScopedPointer <EdgeTable> edgeTable;
  1895. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable);
  1896. };
  1897. void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
  1898. {
  1899. Font& f = savedState->font;
  1900. if (transform.isOnlyTranslation() && savedState->transform.isOnlyTranslated)
  1901. {
  1902. RenderingHelpers::GlyphCache <CachedGlyphEdgeTable, SavedState>::getInstance()
  1903. .drawGlyph (*savedState, f, glyphNumber,
  1904. transform.getTranslationX(),
  1905. transform.getTranslationY());
  1906. }
  1907. else
  1908. {
  1909. const float fontHeight = f.getHeight();
  1910. savedState->drawGlyph (f, glyphNumber,
  1911. AffineTransform::scale (fontHeight * f.getHorizontalScale(), fontHeight)
  1912. .followedBy (transform));
  1913. }
  1914. }
  1915. void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { savedState->font = newFont; }
  1916. Font LowLevelGraphicsSoftwareRenderer::getFont() { return savedState->font; }
  1917. #if JUCE_MSVC
  1918. #pragma warning (pop)
  1919. #if JUCE_DEBUG
  1920. #pragma optimize ("", on) // resets optimisations to the project defaults
  1921. #endif
  1922. #endif
  1923. END_JUCE_NAMESPACE