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