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.

2238 lines
83KB

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