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.

2318 lines
85KB

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