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
85KB

  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 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 toEdgeTable()->clipToPath (p, transform);
  1222. }
  1223. Ptr clipToEdgeTable (const EdgeTable& et)
  1224. {
  1225. return toEdgeTable()->clipToEdgeTable (et);
  1226. }
  1227. Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality)
  1228. {
  1229. return toEdgeTable()->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_) noexcept
  1352. : clip (clip_), area (area_)
  1353. {
  1354. }
  1355. template <class Renderer>
  1356. void iterate (Renderer& r) const noexcept
  1357. {
  1358. const RenderingHelpers::FloatRectangleRasterisingInfo f (area);
  1359. RectangleList::Iterator iter (clip);
  1360. while (iter.next())
  1361. {
  1362. const int clipLeft = iter.getRectangle()->getX();
  1363. const int clipRight = iter.getRectangle()->getRight();
  1364. const int clipTop = iter.getRectangle()->getY();
  1365. const int clipBottom = iter.getRectangle()->getBottom();
  1366. if (f.totalBottom > clipTop && f.totalTop < clipBottom && f.totalRight > clipLeft && f.totalLeft < clipRight)
  1367. {
  1368. if (f.isOnePixelWide())
  1369. {
  1370. if (f.topAlpha != 0 && f.totalTop >= clipTop)
  1371. {
  1372. r.setEdgeTableYPos (f.totalTop);
  1373. r.handleEdgeTablePixel (f.left, f.topAlpha);
  1374. }
  1375. const int endY = jmin (f.bottom, clipBottom);
  1376. for (int y = jmax (clipTop, f.top); y < endY; ++y)
  1377. {
  1378. r.setEdgeTableYPos (y);
  1379. r.handleEdgeTablePixelFull (f.left);
  1380. }
  1381. if (f.bottomAlpha != 0 && f.bottom < clipBottom)
  1382. {
  1383. r.setEdgeTableYPos (f.bottom);
  1384. r.handleEdgeTablePixel (f.left, f.bottomAlpha);
  1385. }
  1386. }
  1387. else
  1388. {
  1389. const int clippedLeft = jmax (f.left, clipLeft);
  1390. const int clippedWidth = jmin (f.right, clipRight) - clippedLeft;
  1391. const bool doLeftAlpha = f.leftAlpha != 0 && f.totalLeft >= clipLeft;
  1392. const bool doRightAlpha = f.rightAlpha != 0 && f.right < clipRight;
  1393. if (f.topAlpha != 0 && f.totalTop >= clipTop)
  1394. {
  1395. r.setEdgeTableYPos (f.totalTop);
  1396. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getTopLeftCornerAlpha());
  1397. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.topAlpha);
  1398. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getTopRightCornerAlpha());
  1399. }
  1400. const int endY = jmin (f.bottom, clipBottom);
  1401. for (int y = jmax (clipTop, f.top); y < endY; ++y)
  1402. {
  1403. r.setEdgeTableYPos (y);
  1404. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.leftAlpha);
  1405. if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth);
  1406. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.rightAlpha);
  1407. }
  1408. if (f.bottomAlpha != 0 && f.bottom < clipBottom)
  1409. {
  1410. r.setEdgeTableYPos (f.bottom);
  1411. if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getBottomLeftCornerAlpha());
  1412. if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.bottomAlpha);
  1413. if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getBottomRightCornerAlpha());
  1414. }
  1415. }
  1416. }
  1417. }
  1418. }
  1419. private:
  1420. const RectangleList& clip;
  1421. const Rectangle<float>& area;
  1422. JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat);
  1423. };
  1424. inline Ptr toEdgeTable() const { return new ClipRegion_EdgeTable (clip); }
  1425. ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&);
  1426. };
  1427. }
  1428. //==============================================================================
  1429. class LowLevelGraphicsSoftwareRenderer::SavedState
  1430. {
  1431. public:
  1432. SavedState (const Image& image_, const Rectangle<int>& clip_, const int xOffset_, const int yOffset_)
  1433. : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)),
  1434. transform (xOffset_, yOffset_),
  1435. interpolationQuality (Graphics::mediumResamplingQuality),
  1436. transparencyLayerAlpha (1.0f)
  1437. {
  1438. }
  1439. SavedState (const Image& image_, const RectangleList& clip_, const int xOffset_, const int yOffset_)
  1440. : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)),
  1441. transform (xOffset_, yOffset_),
  1442. interpolationQuality (Graphics::mediumResamplingQuality),
  1443. transparencyLayerAlpha (1.0f)
  1444. {
  1445. }
  1446. SavedState (const SavedState& other)
  1447. : image (other.image), clip (other.clip), transform (other.transform),
  1448. font (other.font), fillType (other.fillType),
  1449. interpolationQuality (other.interpolationQuality),
  1450. transparencyLayerAlpha (other.transparencyLayerAlpha)
  1451. {
  1452. }
  1453. bool clipToRectangle (const Rectangle<int>& r)
  1454. {
  1455. if (clip != nullptr)
  1456. {
  1457. if (transform.isOnlyTranslated)
  1458. {
  1459. cloneClipIfMultiplyReferenced();
  1460. clip = clip->clipToRectangle (transform.translated (r));
  1461. }
  1462. else
  1463. {
  1464. Path p;
  1465. p.addRectangle (r);
  1466. clipToPath (p, AffineTransform::identity);
  1467. }
  1468. }
  1469. return clip != nullptr;
  1470. }
  1471. bool clipToRectangleList (const RectangleList& r)
  1472. {
  1473. if (clip != nullptr)
  1474. {
  1475. if (transform.isOnlyTranslated)
  1476. {
  1477. cloneClipIfMultiplyReferenced();
  1478. RectangleList offsetList (r);
  1479. offsetList.offsetAll (transform.xOffset, transform.yOffset);
  1480. clip = clip->clipToRectangleList (offsetList);
  1481. }
  1482. else
  1483. {
  1484. clipToPath (r.toPath(), AffineTransform::identity);
  1485. }
  1486. }
  1487. return clip != nullptr;
  1488. }
  1489. bool excludeClipRectangle (const Rectangle<int>& r)
  1490. {
  1491. if (clip != nullptr)
  1492. {
  1493. cloneClipIfMultiplyReferenced();
  1494. if (transform.isOnlyTranslated)
  1495. {
  1496. clip = clip->excludeClipRectangle (transform.translated (r));
  1497. }
  1498. else
  1499. {
  1500. Path p;
  1501. p.addRectangle (r.toFloat());
  1502. p.applyTransform (transform.complexTransform);
  1503. p.addRectangle (clip->getClipBounds().toFloat());
  1504. p.setUsingNonZeroWinding (false);
  1505. clip = clip->clipToPath (p, AffineTransform::identity);
  1506. }
  1507. }
  1508. return clip != nullptr;
  1509. }
  1510. void clipToPath (const Path& p, const AffineTransform& t)
  1511. {
  1512. if (clip != nullptr)
  1513. {
  1514. cloneClipIfMultiplyReferenced();
  1515. clip = clip->clipToPath (p, transform.getTransformWith (t));
  1516. }
  1517. }
  1518. void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t)
  1519. {
  1520. if (clip != nullptr)
  1521. {
  1522. if (sourceImage.hasAlphaChannel())
  1523. {
  1524. cloneClipIfMultiplyReferenced();
  1525. clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t),
  1526. interpolationQuality != Graphics::lowResamplingQuality);
  1527. }
  1528. else
  1529. {
  1530. Path p;
  1531. p.addRectangle (sourceImage.getBounds());
  1532. clipToPath (p, t);
  1533. }
  1534. }
  1535. }
  1536. bool clipRegionIntersects (const Rectangle<int>& r) const
  1537. {
  1538. if (clip != nullptr)
  1539. {
  1540. if (transform.isOnlyTranslated)
  1541. return clip->clipRegionIntersects (transform.translated (r));
  1542. else
  1543. return getClipBounds().intersects (r);
  1544. }
  1545. return false;
  1546. }
  1547. Rectangle<int> getUntransformedClipBounds() const
  1548. {
  1549. return clip != nullptr ? clip->getClipBounds()
  1550. : Rectangle<int>();
  1551. }
  1552. Rectangle<int> getClipBounds() const
  1553. {
  1554. return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds())
  1555. : Rectangle<int>();
  1556. }
  1557. SavedState* beginTransparencyLayer (float opacity)
  1558. {
  1559. const Rectangle<int> layerBounds (getUntransformedClipBounds());
  1560. SavedState* s = new SavedState (*this);
  1561. s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true);
  1562. s->transparencyLayerAlpha = opacity;
  1563. s->transform.moveOriginInDeviceSpace (-layerBounds.getX(), -layerBounds.getY());
  1564. s->cloneClipIfMultiplyReferenced();
  1565. s->clip = s->clip->translated (-layerBounds.getPosition());
  1566. return s;
  1567. }
  1568. void endTransparencyLayer (SavedState& finishedLayerState)
  1569. {
  1570. const Rectangle<int> layerBounds (getUntransformedClipBounds());
  1571. const ScopedPointer<LowLevelGraphicsContext> g (image.createLowLevelContext());
  1572. g->setOpacity (finishedLayerState.transparencyLayerAlpha);
  1573. g->drawImage (finishedLayerState.image, AffineTransform::translation ((float) layerBounds.getX(),
  1574. (float) layerBounds.getY()));
  1575. }
  1576. //==============================================================================
  1577. void fillRect (const Rectangle<int>& r, const bool replaceContents)
  1578. {
  1579. if (clip != nullptr)
  1580. {
  1581. if (transform.isOnlyTranslated)
  1582. {
  1583. if (fillType.isColour())
  1584. {
  1585. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1586. clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB(), replaceContents);
  1587. }
  1588. else
  1589. {
  1590. const Rectangle<int> totalClip (clip->getClipBounds());
  1591. const Rectangle<int> clipped (totalClip.getIntersection (transform.translated (r)));
  1592. if (! clipped.isEmpty())
  1593. fillShape (new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false);
  1594. }
  1595. }
  1596. else
  1597. {
  1598. Path p;
  1599. p.addRectangle (r);
  1600. fillPath (p, AffineTransform::identity);
  1601. }
  1602. }
  1603. }
  1604. void fillRect (const Rectangle<float>& r)
  1605. {
  1606. if (clip != nullptr)
  1607. {
  1608. if (transform.isOnlyTranslated)
  1609. {
  1610. if (fillType.isColour())
  1611. {
  1612. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1613. clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB());
  1614. }
  1615. else
  1616. {
  1617. const Rectangle<float> totalClip (clip->getClipBounds().toFloat());
  1618. const Rectangle<float> clipped (totalClip.getIntersection (transform.translated (r)));
  1619. if (! clipped.isEmpty())
  1620. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false);
  1621. }
  1622. }
  1623. else
  1624. {
  1625. Path p;
  1626. p.addRectangle (r);
  1627. fillPath (p, AffineTransform::identity);
  1628. }
  1629. }
  1630. }
  1631. void fillPath (const Path& path, const AffineTransform& t)
  1632. {
  1633. if (clip != nullptr)
  1634. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, transform.getTransformWith (t)), false);
  1635. }
  1636. void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y)
  1637. {
  1638. jassert (transform.isOnlyTranslated);
  1639. if (clip != nullptr)
  1640. {
  1641. SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (edgeTable);
  1642. edgeTableClip->edgeTable.translate (x + transform.xOffset,
  1643. y + transform.yOffset);
  1644. fillShape (edgeTableClip, false);
  1645. }
  1646. }
  1647. void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& t)
  1648. {
  1649. if (clip != nullptr)
  1650. {
  1651. const ScopedPointer<EdgeTable> et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t)));
  1652. if (et != nullptr)
  1653. fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (*et), false);
  1654. }
  1655. }
  1656. void fillShape (SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents)
  1657. {
  1658. jassert (clip != nullptr);
  1659. shapeToFill = clip->applyClipTo (shapeToFill);
  1660. if (shapeToFill != nullptr)
  1661. {
  1662. Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1663. if (fillType.isGradient())
  1664. {
  1665. jassert (! replaceContents); // that option is just for solid colours
  1666. ColourGradient g2 (*(fillType.gradient));
  1667. g2.multiplyOpacity (fillType.getOpacity());
  1668. AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f));
  1669. const bool isIdentity = t.isOnlyTranslation();
  1670. if (isIdentity)
  1671. {
  1672. // If our translation doesn't involve any distortion, we can speed it up..
  1673. g2.point1.applyTransform (t);
  1674. g2.point2.applyTransform (t);
  1675. t = AffineTransform::identity;
  1676. }
  1677. shapeToFill->fillAllWithGradient (destData, g2, t, isIdentity);
  1678. }
  1679. else if (fillType.isTiledImage())
  1680. {
  1681. renderImage (fillType.image, fillType.transform, shapeToFill);
  1682. }
  1683. else
  1684. {
  1685. shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents);
  1686. }
  1687. }
  1688. }
  1689. //==============================================================================
  1690. void renderImage (const Image& sourceImage, const AffineTransform& trans,
  1691. const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion)
  1692. {
  1693. const AffineTransform t (transform.getTransformWith (trans));
  1694. const Image::BitmapData destData (image, Image::BitmapData::readWrite);
  1695. const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly);
  1696. const int alpha = fillType.colour.getAlpha();
  1697. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  1698. if (t.isOnlyTranslation())
  1699. {
  1700. // If our translation doesn't involve any distortion, just use a simple blit..
  1701. int tx = (int) (t.getTranslationX() * 256.0f);
  1702. int ty = (int) (t.getTranslationY() * 256.0f);
  1703. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  1704. {
  1705. tx = ((tx + 128) >> 8);
  1706. ty = ((ty + 128) >> 8);
  1707. if (tiledFillClipRegion != nullptr)
  1708. {
  1709. tiledFillClipRegion->renderImageUntransformed (destData, srcData, alpha, tx, ty, true);
  1710. }
  1711. else
  1712. {
  1713. Rectangle<int> area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight());
  1714. area = area.getIntersection (image.getBounds());
  1715. if (! area.isEmpty())
  1716. {
  1717. SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->applyClipTo (new SoftwareRendererClasses::ClipRegion_EdgeTable (area)));
  1718. if (c != nullptr)
  1719. c->renderImageUntransformed (destData, srcData, alpha, tx, ty, false);
  1720. }
  1721. }
  1722. return;
  1723. }
  1724. }
  1725. if (t.isSingularity())
  1726. return;
  1727. if (tiledFillClipRegion != nullptr)
  1728. {
  1729. tiledFillClipRegion->renderImageTransformed (destData, srcData, alpha, t, betterQuality, true);
  1730. }
  1731. else
  1732. {
  1733. Path p;
  1734. p.addRectangle (sourceImage.getBounds());
  1735. SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->clone());
  1736. c = c->clipToPath (p, t);
  1737. if (c != nullptr)
  1738. c->renderImageTransformed (destData, srcData, alpha, t, betterQuality, false);
  1739. }
  1740. }
  1741. //==============================================================================
  1742. Image image;
  1743. SoftwareRendererClasses::ClipRegionBase::Ptr clip;
  1744. RenderingHelpers::TranslationOrTransform transform;
  1745. Font font;
  1746. FillType fillType;
  1747. Graphics::ResamplingQuality interpolationQuality;
  1748. private:
  1749. float transparencyLayerAlpha;
  1750. void cloneClipIfMultiplyReferenced()
  1751. {
  1752. if (clip->getReferenceCount() > 1)
  1753. clip = clip->clone();
  1754. }
  1755. SavedState& operator= (const SavedState&);
  1756. };
  1757. //==============================================================================
  1758. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image)
  1759. : savedState (new SavedState (image, image.getBounds(), 0, 0))
  1760. {
  1761. }
  1762. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, const int xOffset, const int yOffset,
  1763. const RectangleList& initialClip)
  1764. : savedState (new SavedState (image, initialClip, xOffset, yOffset))
  1765. {
  1766. }
  1767. LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer()
  1768. {
  1769. }
  1770. bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const
  1771. {
  1772. return false;
  1773. }
  1774. //==============================================================================
  1775. void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y)
  1776. {
  1777. savedState->transform.setOrigin (x, y);
  1778. }
  1779. void LowLevelGraphicsSoftwareRenderer::addTransform (const AffineTransform& transform)
  1780. {
  1781. savedState->transform.addTransform (transform);
  1782. }
  1783. float LowLevelGraphicsSoftwareRenderer::getScaleFactor()
  1784. {
  1785. return savedState->transform.getScaleFactor();
  1786. }
  1787. bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle<int>& r)
  1788. {
  1789. return savedState->clipToRectangle (r);
  1790. }
  1791. bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion)
  1792. {
  1793. return savedState->clipToRectangleList (clipRegion);
  1794. }
  1795. void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle<int>& r)
  1796. {
  1797. savedState->excludeClipRectangle (r);
  1798. }
  1799. void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform)
  1800. {
  1801. savedState->clipToPath (path, transform);
  1802. }
  1803. void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
  1804. {
  1805. savedState->clipToImageAlpha (sourceImage, transform);
  1806. }
  1807. bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle<int>& r)
  1808. {
  1809. return savedState->clipRegionIntersects (r);
  1810. }
  1811. Rectangle<int> LowLevelGraphicsSoftwareRenderer::getClipBounds() const
  1812. {
  1813. return savedState->getClipBounds();
  1814. }
  1815. bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const
  1816. {
  1817. return savedState->clip == nullptr;
  1818. }
  1819. //==============================================================================
  1820. void LowLevelGraphicsSoftwareRenderer::saveState() { savedState.save(); }
  1821. void LowLevelGraphicsSoftwareRenderer::restoreState() { savedState.restore(); }
  1822. void LowLevelGraphicsSoftwareRenderer::beginTransparencyLayer (float opacity) { savedState.beginTransparencyLayer (opacity); }
  1823. void LowLevelGraphicsSoftwareRenderer::endTransparencyLayer() { savedState.endTransparencyLayer(); }
  1824. //==============================================================================
  1825. void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType)
  1826. {
  1827. savedState->fillType = fillType;
  1828. }
  1829. void LowLevelGraphicsSoftwareRenderer::setOpacity (float newOpacity)
  1830. {
  1831. savedState->fillType.setOpacity (newOpacity);
  1832. }
  1833. void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality)
  1834. {
  1835. savedState->interpolationQuality = quality;
  1836. }
  1837. //==============================================================================
  1838. void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle<int>& r, const bool replaceExistingContents)
  1839. {
  1840. savedState->fillRect (r, replaceExistingContents);
  1841. }
  1842. void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform)
  1843. {
  1844. savedState->fillPath (path, transform);
  1845. }
  1846. void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform)
  1847. {
  1848. savedState->renderImage (sourceImage, transform, nullptr);
  1849. }
  1850. void LowLevelGraphicsSoftwareRenderer::drawLine (const Line <float>& line)
  1851. {
  1852. Path p;
  1853. p.addLineSegment (line, 1.0f);
  1854. fillPath (p, AffineTransform::identity);
  1855. }
  1856. void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, float top, float bottom)
  1857. {
  1858. if (bottom > top)
  1859. savedState->fillRect (Rectangle<float> ((float) x, top, 1.0f, bottom - top));
  1860. }
  1861. void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, float left, float right)
  1862. {
  1863. if (right > left)
  1864. savedState->fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
  1865. }
  1866. //==============================================================================
  1867. class CachedGlyphEdgeTable
  1868. {
  1869. public:
  1870. CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {}
  1871. void draw (LowLevelGraphicsSoftwareRenderer::SavedState& state, float x, const float y) const
  1872. {
  1873. if (snapToIntegerCoordinate)
  1874. x = std::floor (x + 0.5f);
  1875. if (edgeTable != nullptr)
  1876. state.fillEdgeTable (*edgeTable, x, roundToInt (y));
  1877. }
  1878. void generate (const Font& newFont, const int glyphNumber)
  1879. {
  1880. font = newFont;
  1881. snapToIntegerCoordinate = newFont.getTypeface()->isHinted();
  1882. glyph = glyphNumber;
  1883. const float fontHeight = font.getHeight();
  1884. edgeTable = font.getTypeface()->getEdgeTableForGlyph (glyphNumber,
  1885. AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
  1886. #if JUCE_MAC || JUCE_IOS
  1887. .translated (0.0f, -0.5f)
  1888. #endif
  1889. );
  1890. }
  1891. Font font;
  1892. int glyph, lastAccessCount;
  1893. bool snapToIntegerCoordinate;
  1894. private:
  1895. ScopedPointer <EdgeTable> edgeTable;
  1896. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable);
  1897. };
  1898. void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
  1899. {
  1900. Font& f = savedState->font;
  1901. if (transform.isOnlyTranslation() && savedState->transform.isOnlyTranslated)
  1902. {
  1903. RenderingHelpers::GlyphCache <CachedGlyphEdgeTable, SavedState>::getInstance()
  1904. .drawGlyph (*savedState, f, glyphNumber,
  1905. transform.getTranslationX(),
  1906. transform.getTranslationY());
  1907. }
  1908. else
  1909. {
  1910. const float fontHeight = f.getHeight();
  1911. savedState->drawGlyph (f, glyphNumber,
  1912. AffineTransform::scale (fontHeight * f.getHorizontalScale(), fontHeight)
  1913. .followedBy (transform));
  1914. }
  1915. }
  1916. void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { savedState->font = newFont; }
  1917. Font LowLevelGraphicsSoftwareRenderer::getFont() { return savedState->font; }
  1918. #if JUCE_MSVC
  1919. #pragma warning (pop)
  1920. #if JUCE_DEBUG
  1921. #pragma optimize ("", on) // resets optimisations to the project defaults
  1922. #endif
  1923. #endif
  1924. END_JUCE_NAMESPACE