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.

2553 lines
92KB

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