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.

1661 lines
62KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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 && JUCE_DEBUG
  33. #pragma warning (disable: 4714) // warning about forcedinline methods not being inlined
  34. #endif
  35. #if JUCE_MSVC
  36. #pragma warning (push)
  37. #pragma warning (disable: 4127) // "expression is constant" warning
  38. #endif
  39. //==============================================================================
  40. template <class PixelType, bool replaceExisting = false>
  41. class SolidColourEdgeTableRenderer
  42. {
  43. public:
  44. SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) throw()
  45. : data (data_),
  46. sourceColour (colour)
  47. {
  48. if (sizeof (PixelType) == 3)
  49. {
  50. areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen()
  51. && sourceColour.getGreen() == sourceColour.getBlue();
  52. filler[0].set (sourceColour);
  53. filler[1].set (sourceColour);
  54. filler[2].set (sourceColour);
  55. filler[3].set (sourceColour);
  56. }
  57. }
  58. forcedinline void setEdgeTableYPos (const int y) throw()
  59. {
  60. linePixels = (PixelType*) data.getLinePointer (y);
  61. }
  62. forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw()
  63. {
  64. if (replaceExisting)
  65. linePixels[x].set (sourceColour);
  66. else
  67. linePixels[x].blend (sourceColour, alphaLevel);
  68. }
  69. forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw()
  70. {
  71. PixelARGB p (sourceColour);
  72. p.multiplyAlpha (alphaLevel);
  73. PixelType* dest = linePixels + x;
  74. if (replaceExisting || p.getAlpha() >= 0xff)
  75. replaceLine (dest, p, width);
  76. else
  77. blendLine (dest, p, width);
  78. }
  79. private:
  80. const Image::BitmapData& data;
  81. PixelType* linePixels;
  82. PixelARGB sourceColour;
  83. PixelRGB filler [4];
  84. bool areRGBComponentsEqual;
  85. inline void blendLine (PixelType* dest, const PixelARGB& colour, int width) const
  86. {
  87. do
  88. {
  89. dest->blend (colour);
  90. ++dest;
  91. } while (--width > 0);
  92. }
  93. forcedinline void replaceLine (PixelRGB* dest, const PixelARGB& colour, int width) const throw()
  94. {
  95. if (areRGBComponentsEqual) // if all the component values are the same, we can cheat..
  96. {
  97. memset (dest, colour.getRed(), width * 3);
  98. }
  99. else
  100. {
  101. if (width >> 5)
  102. {
  103. const int* const intFiller = (const int*) filler;
  104. while (width > 8 && (((pointer_sized_int) dest) & 7) != 0)
  105. {
  106. dest->set (colour);
  107. ++dest;
  108. --width;
  109. }
  110. while (width > 4)
  111. {
  112. ((int*) dest) [0] = intFiller[0];
  113. ((int*) dest) [1] = intFiller[1];
  114. ((int*) dest) [2] = intFiller[2];
  115. dest = (PixelRGB*) (((uint8*) dest) + 12);
  116. width -= 4;
  117. }
  118. }
  119. while (--width >= 0)
  120. {
  121. dest->set (colour);
  122. ++dest;
  123. }
  124. }
  125. }
  126. forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB& colour, int width) const throw()
  127. {
  128. memset (dest, colour.getAlpha(), width);
  129. }
  130. forcedinline void replaceLine (PixelARGB* dest, const PixelARGB& colour, int width) const throw()
  131. {
  132. do
  133. {
  134. dest->set (colour);
  135. ++dest;
  136. } while (--width > 0);
  137. }
  138. SolidColourEdgeTableRenderer (const SolidColourEdgeTableRenderer&);
  139. SolidColourEdgeTableRenderer& operator= (const SolidColourEdgeTableRenderer&);
  140. };
  141. //==============================================================================
  142. class LinearGradientPixelGenerator
  143. {
  144. public:
  145. LinearGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform& transform, const PixelARGB* const lookupTable_, const int numEntries_)
  146. : lookupTable (lookupTable_), numEntries (numEntries_)
  147. {
  148. jassert (numEntries_ >= 0);
  149. float x1 = gradient.x1;
  150. float y1 = gradient.y1;
  151. float x2 = gradient.x2;
  152. float y2 = gradient.y2;
  153. if (! transform.isIdentity())
  154. {
  155. const Line l (x2, y2, x1, y1);
  156. const Point<float> p3 = l.getPointAlongLine (0.0f, 100.0f);
  157. float x3 = p3.getX();
  158. float y3 = p3.getY();
  159. transform.transformPoint (x1, y1);
  160. transform.transformPoint (x2, y2);
  161. transform.transformPoint (x3, y3);
  162. const Line l2 (x2, y2, x3, y3);
  163. const float prop = l2.findNearestPointTo (x1, y1);
  164. const Point<float> newP2 (l2.getPointAlongLineProportionally (prop));
  165. x2 = newP2.getX();
  166. y2 = newP2.getY();
  167. }
  168. vertical = fabs (x1 - x2) < 0.001f;
  169. horizontal = fabs (y1 - y2) < 0.001f;
  170. if (vertical)
  171. {
  172. scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (y2 - y1));
  173. start = roundToInt (y1 * scale);
  174. }
  175. else if (horizontal)
  176. {
  177. scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (x2 - x1));
  178. start = roundToInt (x1 * scale);
  179. }
  180. else
  181. {
  182. grad = (y2 - y1) / (double) (x1 - x2);
  183. yTerm = y1 - x1 / grad;
  184. scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (y2 * grad - x2)));
  185. grad *= scale;
  186. }
  187. }
  188. forcedinline void setY (const int y) throw()
  189. {
  190. if (vertical)
  191. linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)];
  192. else if (! horizontal)
  193. start = roundToInt ((y - yTerm) * grad);
  194. }
  195. inline const PixelARGB getPixel (const int x) const throw()
  196. {
  197. return vertical ? linePix
  198. : lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)];
  199. }
  200. private:
  201. const PixelARGB* const lookupTable;
  202. const int numEntries;
  203. PixelARGB linePix;
  204. int start, scale;
  205. double grad, yTerm;
  206. bool vertical, horizontal;
  207. enum { numScaleBits = 12 };
  208. LinearGradientPixelGenerator (const LinearGradientPixelGenerator&);
  209. LinearGradientPixelGenerator& operator= (const LinearGradientPixelGenerator&);
  210. };
  211. //==============================================================================
  212. class RadialGradientPixelGenerator
  213. {
  214. public:
  215. RadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform&,
  216. const PixelARGB* const lookupTable_, const int numEntries_) throw()
  217. : lookupTable (lookupTable_),
  218. numEntries (numEntries_),
  219. gx1 (gradient.x1),
  220. gy1 (gradient.y1)
  221. {
  222. jassert (numEntries_ >= 0);
  223. const float gdx = gradient.x1 - gradient.x2;
  224. const float gdy = gradient.y1 - gradient.y2;
  225. maxDist = gdx * gdx + gdy * gdy;
  226. invScale = numEntries / sqrt (maxDist);
  227. jassert (roundToInt (sqrt (maxDist) * invScale) <= numEntries);
  228. }
  229. forcedinline void setY (const int y) throw()
  230. {
  231. dy = y - gy1;
  232. dy *= dy;
  233. }
  234. inline const PixelARGB getPixel (const int px) const throw()
  235. {
  236. double x = px - gx1;
  237. x *= x;
  238. x += dy;
  239. return lookupTable [x >= maxDist ? numEntries : roundToInt (sqrt (x) * invScale)];
  240. }
  241. protected:
  242. const PixelARGB* const lookupTable;
  243. const int numEntries;
  244. const double gx1, gy1;
  245. double maxDist, invScale, dy;
  246. RadialGradientPixelGenerator (const RadialGradientPixelGenerator&);
  247. RadialGradientPixelGenerator& operator= (const RadialGradientPixelGenerator&);
  248. };
  249. //==============================================================================
  250. class TransformedRadialGradientPixelGenerator : public RadialGradientPixelGenerator
  251. {
  252. public:
  253. TransformedRadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform& transform,
  254. const PixelARGB* const lookupTable_, const int numEntries_) throw()
  255. : RadialGradientPixelGenerator (gradient, transform, lookupTable_, numEntries_),
  256. inverseTransform (transform.inverted())
  257. {
  258. tM10 = inverseTransform.mat10;
  259. tM00 = inverseTransform.mat00;
  260. }
  261. forcedinline void setY (const int y) throw()
  262. {
  263. lineYM01 = inverseTransform.mat01 * y + inverseTransform.mat02 - gx1;
  264. lineYM11 = inverseTransform.mat11 * y + inverseTransform.mat12 - gy1;
  265. }
  266. inline const PixelARGB getPixel (const int px) const throw()
  267. {
  268. double x = px;
  269. const double y = tM10 * x + lineYM11;
  270. x = tM00 * x + lineYM01;
  271. x *= x;
  272. x += y * y;
  273. if (x >= maxDist)
  274. return lookupTable [numEntries];
  275. else
  276. return lookupTable [jmin (numEntries, roundToInt (sqrt (x) * invScale))];
  277. }
  278. private:
  279. double tM10, tM00, lineYM01, lineYM11;
  280. const AffineTransform inverseTransform;
  281. TransformedRadialGradientPixelGenerator (const TransformedRadialGradientPixelGenerator&);
  282. TransformedRadialGradientPixelGenerator& operator= (const TransformedRadialGradientPixelGenerator&);
  283. };
  284. //==============================================================================
  285. template <class PixelType, class GradientType>
  286. class GradientEdgeTableRenderer : public GradientType
  287. {
  288. public:
  289. GradientEdgeTableRenderer (const Image::BitmapData& destData_, const ColourGradient& gradient, const AffineTransform& transform,
  290. const PixelARGB* const lookupTable_, const int numEntries_) throw()
  291. : GradientType (gradient, transform, lookupTable_, numEntries_ - 1),
  292. destData (destData_)
  293. {
  294. }
  295. forcedinline void setEdgeTableYPos (const int y) throw()
  296. {
  297. linePixels = (PixelType*) destData.getLinePointer (y);
  298. GradientType::setY (y);
  299. }
  300. forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw()
  301. {
  302. linePixels[x].blend (GradientType::getPixel (x), alphaLevel);
  303. }
  304. forcedinline void handleEdgeTableLine (int x, int width, const int alphaLevel) const throw()
  305. {
  306. PixelType* dest = linePixels + x;
  307. if (alphaLevel < 0xff)
  308. {
  309. do
  310. {
  311. (dest++)->blend (GradientType::getPixel (x++), alphaLevel);
  312. } while (--width > 0);
  313. }
  314. else
  315. {
  316. do
  317. {
  318. (dest++)->blend (GradientType::getPixel (x++));
  319. } while (--width > 0);
  320. }
  321. }
  322. private:
  323. const Image::BitmapData& destData;
  324. PixelType* linePixels;
  325. GradientEdgeTableRenderer (const GradientEdgeTableRenderer&);
  326. GradientEdgeTableRenderer& operator= (const GradientEdgeTableRenderer&);
  327. };
  328. //==============================================================================
  329. static forcedinline int safeModulo (int n, const int divisor) throw()
  330. {
  331. jassert (divisor > 0);
  332. n %= divisor;
  333. return (n < 0) ? (n + divisor) : n;
  334. }
  335. //==============================================================================
  336. template <class DestPixelType, class SrcPixelType, bool repeatPattern>
  337. class ImageFillEdgeTableRenderer
  338. {
  339. public:
  340. ImageFillEdgeTableRenderer (const Image::BitmapData& destData_,
  341. const Image::BitmapData& srcData_,
  342. const int extraAlpha_,
  343. const int x, const int y) throw()
  344. : destData (destData_),
  345. srcData (srcData_),
  346. extraAlpha (extraAlpha_ + 1),
  347. xOffset (repeatPattern ? safeModulo (x, srcData_.width) - srcData_.width : x),
  348. yOffset (repeatPattern ? safeModulo (y, srcData_.height) - srcData_.height : y)
  349. {
  350. }
  351. forcedinline void setEdgeTableYPos (int y) throw()
  352. {
  353. linePixels = (DestPixelType*) destData.getLinePointer (y);
  354. y -= yOffset;
  355. if (repeatPattern)
  356. {
  357. jassert (y >= 0);
  358. y %= srcData.height;
  359. }
  360. sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y);
  361. }
  362. forcedinline void handleEdgeTablePixel (int x, int alphaLevel) const throw()
  363. {
  364. alphaLevel = (alphaLevel * extraAlpha) >> 8;
  365. linePixels[x].blend (sourceLineStart [repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)], alphaLevel);
  366. }
  367. forcedinline void handleEdgeTableLine (int x, int width, int alphaLevel) const throw()
  368. {
  369. DestPixelType* dest = linePixels + x;
  370. alphaLevel = (alphaLevel * extraAlpha) >> 8;
  371. x -= xOffset;
  372. jassert (repeatPattern || (x >= 0 && x + width <= srcData.width));
  373. if (alphaLevel < 0xfe)
  374. {
  375. do
  376. {
  377. dest++ ->blend (sourceLineStart [repeatPattern ? (x++ % srcData.width) : x++], alphaLevel);
  378. } while (--width > 0);
  379. }
  380. else
  381. {
  382. if (repeatPattern)
  383. {
  384. do
  385. {
  386. dest++ ->blend (sourceLineStart [x++ % srcData.width]);
  387. } while (--width > 0);
  388. }
  389. else
  390. {
  391. copyRow (dest, sourceLineStart + x, width);
  392. }
  393. }
  394. }
  395. void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) throw()
  396. {
  397. jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width);
  398. SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset);
  399. uint8* mask = (uint8*) (s + x - xOffset);
  400. if (sizeof (SrcPixelType) == sizeof (PixelARGB))
  401. mask += PixelARGB::indexA;
  402. et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width);
  403. }
  404. private:
  405. const Image::BitmapData& destData;
  406. const Image::BitmapData& srcData;
  407. const int extraAlpha, xOffset, yOffset;
  408. DestPixelType* linePixels;
  409. SrcPixelType* sourceLineStart;
  410. template <class PixelType1, class PixelType2>
  411. forcedinline static void copyRow (PixelType1* dest, PixelType2* src, int width) throw()
  412. {
  413. do
  414. {
  415. dest++ ->blend (*src++);
  416. } while (--width > 0);
  417. }
  418. forcedinline static void copyRow (PixelRGB* dest, PixelRGB* src, int width) throw()
  419. {
  420. memcpy (dest, src, width * sizeof (PixelRGB));
  421. }
  422. ImageFillEdgeTableRenderer (const ImageFillEdgeTableRenderer&);
  423. ImageFillEdgeTableRenderer& operator= (const ImageFillEdgeTableRenderer&);
  424. };
  425. //==============================================================================
  426. template <class DestPixelType, class SrcPixelType, bool repeatPattern>
  427. class TransformedImageFillEdgeTableRenderer
  428. {
  429. public:
  430. TransformedImageFillEdgeTableRenderer (const Image::BitmapData& destData_,
  431. const Image::BitmapData& srcData_,
  432. const AffineTransform& transform,
  433. const int extraAlpha_,
  434. const bool betterQuality_) throw()
  435. : interpolator (transform),
  436. destData (destData_),
  437. srcData (srcData_),
  438. extraAlpha (extraAlpha_ + 1),
  439. betterQuality (betterQuality_),
  440. pixelOffset (betterQuality_ ? 0.5f : 0.0f),
  441. pixelOffsetInt (betterQuality_ ? -128 : 0),
  442. maxX (srcData_.width - 1),
  443. maxY (srcData_.height - 1),
  444. scratchSize (2048)
  445. {
  446. scratchBuffer.malloc (scratchSize);
  447. }
  448. ~TransformedImageFillEdgeTableRenderer() throw()
  449. {
  450. }
  451. forcedinline void setEdgeTableYPos (const int newY) throw()
  452. {
  453. y = newY;
  454. linePixels = (DestPixelType*) destData.getLinePointer (newY);
  455. }
  456. forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) throw()
  457. {
  458. alphaLevel *= extraAlpha;
  459. alphaLevel >>= 8;
  460. SrcPixelType p;
  461. generate (&p, x, 1);
  462. linePixels[x].blend (p, alphaLevel);
  463. }
  464. forcedinline void handleEdgeTableLine (const int x, int width, int alphaLevel) throw()
  465. {
  466. if (width > scratchSize)
  467. {
  468. scratchSize = width;
  469. scratchBuffer.malloc (scratchSize);
  470. }
  471. SrcPixelType* span = scratchBuffer;
  472. generate (span, x, width);
  473. DestPixelType* dest = linePixels + x;
  474. alphaLevel *= extraAlpha;
  475. alphaLevel >>= 8;
  476. if (alphaLevel < 0xfe)
  477. {
  478. do
  479. {
  480. dest++ ->blend (*span++, alphaLevel);
  481. } while (--width > 0);
  482. }
  483. else
  484. {
  485. do
  486. {
  487. dest++ ->blend (*span++);
  488. } while (--width > 0);
  489. }
  490. }
  491. void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) throw()
  492. {
  493. if (width > scratchSize)
  494. {
  495. scratchSize = width;
  496. scratchBuffer.malloc (scratchSize);
  497. }
  498. uint8* mask = (uint8*) scratchBuffer;
  499. y = y_;
  500. generate ((SrcPixelType*) mask, x, width);
  501. if (sizeof (SrcPixelType) == sizeof (PixelARGB))
  502. mask += PixelARGB::indexA;
  503. et.clipLineToMask (x, y_, mask, sizeof (SrcPixelType), width);
  504. }
  505. private:
  506. //==============================================================================
  507. void generate (PixelARGB* dest, const int x, int numPixels) throw()
  508. {
  509. this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels);
  510. do
  511. {
  512. int hiResX, hiResY;
  513. this->interpolator.next (hiResX, hiResY);
  514. hiResX += pixelOffsetInt;
  515. hiResY += pixelOffsetInt;
  516. int loResX = hiResX >> 8;
  517. int loResY = hiResY >> 8;
  518. if (repeatPattern)
  519. {
  520. loResX = safeModulo (loResX, srcData.width);
  521. loResY = safeModulo (loResY, srcData.height);
  522. }
  523. if (betterQuality
  524. && ((unsigned int) loResX) < (unsigned int) maxX
  525. && ((unsigned int) loResY) < (unsigned int) maxY)
  526. {
  527. uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 };
  528. hiResX &= 255;
  529. hiResY &= 255;
  530. const uint8* src = this->srcData.getPixelPointer (loResX, loResY);
  531. uint32 weight = (256 - hiResX) * (256 - hiResY);
  532. c[0] += weight * src[0];
  533. c[1] += weight * src[1];
  534. c[2] += weight * src[2];
  535. c[3] += weight * src[3];
  536. weight = hiResX * (256 - hiResY);
  537. c[0] += weight * src[4];
  538. c[1] += weight * src[5];
  539. c[2] += weight * src[6];
  540. c[3] += weight * src[7];
  541. src += this->srcData.lineStride;
  542. weight = (256 - hiResX) * hiResY;
  543. c[0] += weight * src[0];
  544. c[1] += weight * src[1];
  545. c[2] += weight * src[2];
  546. c[3] += weight * src[3];
  547. weight = hiResX * hiResY;
  548. c[0] += weight * src[4];
  549. c[1] += weight * src[5];
  550. c[2] += weight * src[6];
  551. c[3] += weight * src[7];
  552. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16),
  553. (uint8) (c[PixelARGB::indexR] >> 16),
  554. (uint8) (c[PixelARGB::indexG] >> 16),
  555. (uint8) (c[PixelARGB::indexB] >> 16));
  556. }
  557. else
  558. {
  559. if (! repeatPattern)
  560. {
  561. // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable
  562. if (loResX < 0) loResX = 0;
  563. if (loResY < 0) loResY = 0;
  564. if (loResX > maxX) loResX = maxX;
  565. if (loResY > maxY) loResY = maxY;
  566. }
  567. dest->set (*(const PixelARGB*) this->srcData.getPixelPointer (loResX, loResY));
  568. }
  569. ++dest;
  570. } while (--numPixels > 0);
  571. }
  572. void generate (PixelRGB* dest, const int x, int numPixels) throw()
  573. {
  574. this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels);
  575. do
  576. {
  577. int hiResX, hiResY;
  578. this->interpolator.next (hiResX, hiResY);
  579. hiResX += pixelOffsetInt;
  580. hiResY += pixelOffsetInt;
  581. int loResX = hiResX >> 8;
  582. int loResY = hiResY >> 8;
  583. if (repeatPattern)
  584. {
  585. loResX = safeModulo (loResX, srcData.width);
  586. loResY = safeModulo (loResY, srcData.height);
  587. }
  588. if (betterQuality
  589. && ((unsigned int) loResX) < (unsigned int) maxX
  590. && ((unsigned int) loResY) < (unsigned int) maxY)
  591. {
  592. uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 };
  593. hiResX &= 255;
  594. hiResY &= 255;
  595. const uint8* src = this->srcData.getPixelPointer (loResX, loResY);
  596. unsigned int weight = (256 - hiResX) * (256 - hiResY);
  597. c[0] += weight * src[0];
  598. c[1] += weight * src[1];
  599. c[2] += weight * src[2];
  600. weight = hiResX * (256 - hiResY);
  601. c[0] += weight * src[3];
  602. c[1] += weight * src[4];
  603. c[2] += weight * src[5];
  604. src += this->srcData.lineStride;
  605. weight = (256 - hiResX) * hiResY;
  606. c[0] += weight * src[0];
  607. c[1] += weight * src[1];
  608. c[2] += weight * src[2];
  609. weight = hiResX * hiResY;
  610. c[0] += weight * src[3];
  611. c[1] += weight * src[4];
  612. c[2] += weight * src[5];
  613. dest->setARGB ((uint8) 255,
  614. (uint8) (c[PixelRGB::indexR] >> 16),
  615. (uint8) (c[PixelRGB::indexG] >> 16),
  616. (uint8) (c[PixelRGB::indexB] >> 16));
  617. }
  618. else
  619. {
  620. if (! repeatPattern)
  621. {
  622. // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable
  623. if (loResX < 0) loResX = 0;
  624. if (loResY < 0) loResY = 0;
  625. if (loResX > maxX) loResX = maxX;
  626. if (loResY > maxY) loResY = maxY;
  627. }
  628. dest->set (*(const PixelRGB*) this->srcData.getPixelPointer (loResX, loResY));
  629. }
  630. ++dest;
  631. } while (--numPixels > 0);
  632. }
  633. void generate (PixelAlpha* dest, const int x, int numPixels) throw()
  634. {
  635. this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels);
  636. do
  637. {
  638. int hiResX, hiResY;
  639. this->interpolator.next (hiResX, hiResY);
  640. hiResX += pixelOffsetInt;
  641. hiResY += pixelOffsetInt;
  642. int loResX = hiResX >> 8;
  643. int loResY = hiResY >> 8;
  644. if (repeatPattern)
  645. {
  646. loResX = safeModulo (loResX, srcData.width);
  647. loResY = safeModulo (loResY, srcData.height);
  648. }
  649. if (betterQuality
  650. && ((unsigned int) loResX) < (unsigned int) maxX
  651. && ((unsigned int) loResY) < (unsigned int) maxY)
  652. {
  653. hiResX &= 255;
  654. hiResY &= 255;
  655. const uint8* src = this->srcData.getPixelPointer (loResX, loResY);
  656. uint32 c = 256 * 128;
  657. c += src[0] * ((256 - hiResX) * (256 - hiResY));
  658. c += src[1] * (hiResX * (256 - hiResY));
  659. src += this->srcData.lineStride;
  660. c += src[0] * ((256 - hiResX) * hiResY);
  661. c += src[1] * (hiResX * hiResY);
  662. *((uint8*) dest) = (uint8) c;
  663. }
  664. else
  665. {
  666. if (! repeatPattern)
  667. {
  668. // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable
  669. if (loResX < 0) loResX = 0;
  670. if (loResY < 0) loResY = 0;
  671. if (loResX > maxX) loResX = maxX;
  672. if (loResY > maxY) loResY = maxY;
  673. }
  674. *((uint8*) dest) = *(this->srcData.getPixelPointer (loResX, loResY));
  675. }
  676. ++dest;
  677. } while (--numPixels > 0);
  678. }
  679. //==============================================================================
  680. class TransformedImageSpanInterpolator
  681. {
  682. public:
  683. TransformedImageSpanInterpolator (const AffineTransform& transform) throw()
  684. : inverseTransform (transform.inverted())
  685. {}
  686. void setStartOfLine (float x, float y, const int numPixels) throw()
  687. {
  688. float x1 = x, y1 = y;
  689. inverseTransform.transformPoint (x1, y1);
  690. x += numPixels;
  691. inverseTransform.transformPoint (x, y);
  692. xBresenham.set ((int) (x1 * 256.0f), (int) (x * 256.0f), numPixels);
  693. yBresenham.set ((int) (y1 * 256.0f), (int) (y * 256.0f), numPixels);
  694. }
  695. void next (int& x, int& y) throw()
  696. {
  697. x = xBresenham.n;
  698. xBresenham.stepToNext();
  699. y = yBresenham.n;
  700. yBresenham.stepToNext();
  701. }
  702. private:
  703. class BresenhamInterpolator
  704. {
  705. public:
  706. BresenhamInterpolator() throw() {}
  707. void set (const int n1, const int n2, const int numSteps_) throw()
  708. {
  709. numSteps = jmax (1, numSteps_);
  710. step = (n2 - n1) / numSteps;
  711. remainder = modulo = (n2 - n1) % numSteps;
  712. n = n1;
  713. if (modulo <= 0)
  714. {
  715. modulo += numSteps;
  716. remainder += numSteps;
  717. --step;
  718. }
  719. modulo -= numSteps;
  720. }
  721. forcedinline void stepToNext() throw()
  722. {
  723. modulo += remainder;
  724. n += step;
  725. if (modulo > 0)
  726. {
  727. modulo -= numSteps;
  728. ++n;
  729. }
  730. }
  731. int n;
  732. private:
  733. int numSteps, step, modulo, remainder;
  734. };
  735. const AffineTransform inverseTransform;
  736. BresenhamInterpolator xBresenham, yBresenham;
  737. TransformedImageSpanInterpolator (const TransformedImageSpanInterpolator&);
  738. TransformedImageSpanInterpolator& operator= (const TransformedImageSpanInterpolator&);
  739. };
  740. //==============================================================================
  741. TransformedImageSpanInterpolator interpolator;
  742. const Image::BitmapData& destData;
  743. const Image::BitmapData& srcData;
  744. const int extraAlpha;
  745. const bool betterQuality;
  746. const float pixelOffset;
  747. const int pixelOffsetInt, maxX, maxY;
  748. int y;
  749. DestPixelType* linePixels;
  750. HeapBlock <SrcPixelType> scratchBuffer;
  751. int scratchSize;
  752. TransformedImageFillEdgeTableRenderer (const TransformedImageFillEdgeTableRenderer&);
  753. TransformedImageFillEdgeTableRenderer& operator= (const TransformedImageFillEdgeTableRenderer&);
  754. };
  755. //==============================================================================
  756. class LLGCSavedState
  757. {
  758. public:
  759. LLGCSavedState (const Rectangle<int>& clip_, const int xOffset_, const int yOffset_,
  760. const Font& font_, const FillType& fillType_,
  761. const Graphics::ResamplingQuality interpolationQuality_) throw()
  762. : edgeTable (new EdgeTableHolder (EdgeTable (clip_))),
  763. xOffset (xOffset_), yOffset (yOffset_),
  764. font (font_), fillType (fillType_),
  765. interpolationQuality (interpolationQuality_)
  766. {
  767. }
  768. LLGCSavedState (const LLGCSavedState& other) throw()
  769. : edgeTable (other.edgeTable), xOffset (other.xOffset),
  770. yOffset (other.yOffset), font (other.font),
  771. fillType (other.fillType), interpolationQuality (other.interpolationQuality)
  772. {
  773. }
  774. ~LLGCSavedState() throw()
  775. {
  776. }
  777. bool clipToRectangle (const Rectangle<int>& r) throw()
  778. {
  779. dupeEdgeTableIfMultiplyReferenced();
  780. edgeTable->edgeTable.clipToRectangle (r.translated (xOffset, yOffset));
  781. return ! edgeTable->edgeTable.isEmpty();
  782. }
  783. bool clipToRectangleList (const RectangleList& r) throw()
  784. {
  785. dupeEdgeTableIfMultiplyReferenced();
  786. RectangleList offsetList (r);
  787. offsetList.offsetAll (xOffset, yOffset);
  788. EdgeTable e2 (offsetList);
  789. edgeTable->edgeTable.clipToEdgeTable (e2);
  790. return ! edgeTable->edgeTable.isEmpty();
  791. }
  792. bool excludeClipRectangle (const Rectangle<int>& r) throw()
  793. {
  794. dupeEdgeTableIfMultiplyReferenced();
  795. edgeTable->edgeTable.excludeRectangle (r.translated (xOffset, yOffset));
  796. return ! edgeTable->edgeTable.isEmpty();
  797. }
  798. void clipToPath (const Path& p, const AffineTransform& transform) throw()
  799. {
  800. dupeEdgeTableIfMultiplyReferenced();
  801. EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform.translated ((float) xOffset, (float) yOffset));
  802. edgeTable->edgeTable.clipToEdgeTable (et);
  803. }
  804. //==============================================================================
  805. void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw()
  806. {
  807. et.clipToEdgeTable (edgeTable->edgeTable);
  808. Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true);
  809. if (fillType.isGradient())
  810. {
  811. jassert (! replaceContents); // that option is just for solid colours
  812. ColourGradient g2 (*(fillType.gradient));
  813. g2.multiplyOpacity (fillType.getOpacity());
  814. g2.x1 -= 0.5f; g2.y1 -= 0.5f;
  815. g2.x2 -= 0.5f; g2.y2 -= 0.5f;
  816. AffineTransform transform (fillType.transform.translated ((float) xOffset, (float) yOffset));
  817. const bool isIdentity = transform.isOnlyTranslation();
  818. if (isIdentity)
  819. {
  820. // If our translation doesn't involve any distortion, we can speed it up..
  821. transform.transformPoint (g2.x1, g2.y1);
  822. transform.transformPoint (g2.x2, g2.y2);
  823. transform = AffineTransform::identity;
  824. }
  825. HeapBlock <PixelARGB> lookupTable;
  826. const int numLookupEntries = g2.createLookupTable (transform, lookupTable);
  827. jassert (numLookupEntries > 0);
  828. switch (image.getFormat())
  829. {
  830. case Image::ARGB: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
  831. case Image::RGB: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
  832. default: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
  833. }
  834. }
  835. else if (fillType.isTiledImage())
  836. {
  837. renderImage (image, *(fillType.image), fillType.image->getBounds(), fillType.transform, &et);
  838. }
  839. else
  840. {
  841. const PixelARGB fillColour (fillType.colour.getPixelARGB());
  842. switch (image.getFormat())
  843. {
  844. case Image::ARGB: renderSolidFill (et, destData, fillColour, replaceContents, (PixelARGB*) 0); break;
  845. case Image::RGB: renderSolidFill (et, destData, fillColour, replaceContents, (PixelRGB*) 0); break;
  846. default: renderSolidFill (et, destData, fillColour, replaceContents, (PixelAlpha*) 0); break;
  847. }
  848. }
  849. }
  850. //==============================================================================
  851. void renderImage (Image& destImage, const Image& sourceImage, const Rectangle<int>& srcClip,
  852. const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw()
  853. {
  854. const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset));
  855. const Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true);
  856. const Image::BitmapData srcData (sourceImage, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight());
  857. const int alpha = fillType.colour.getAlpha();
  858. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  859. if (transform.isOnlyTranslation())
  860. {
  861. // If our translation doesn't involve any distortion, just use a simple blit..
  862. int tx = (int) (transform.getTranslationX() * 256.0f);
  863. int ty = (int) (transform.getTranslationY() * 256.0f);
  864. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  865. {
  866. tx = ((tx + 128) >> 8);
  867. ty = ((ty + 128) >> 8);
  868. if (tiledFillClipRegion != 0)
  869. {
  870. blittedRenderImage (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, tx, ty, true);
  871. }
  872. else
  873. {
  874. EdgeTable et (Rectangle<int> (tx, ty, srcClip.getWidth(), srcClip.getHeight()).getIntersection (destImage.getBounds()));
  875. et.clipToEdgeTable (edgeTable->edgeTable);
  876. if (! et.isEmpty())
  877. blittedRenderImage (sourceImage, destImage, et, destData, srcData, alpha, tx, ty, false);
  878. }
  879. return;
  880. }
  881. }
  882. if (transform.isSingularity())
  883. return;
  884. if (tiledFillClipRegion != 0)
  885. {
  886. transformedRenderImage (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality, true);
  887. }
  888. else
  889. {
  890. Path p;
  891. p.addRectangle (0.0f, 0.0f, (float) srcClip.getWidth(), (float) srcClip.getHeight());
  892. EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform);
  893. et.clipToEdgeTable (edgeTable->edgeTable);
  894. if (! et.isEmpty())
  895. transformedRenderImage (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality, false);
  896. }
  897. }
  898. //==============================================================================
  899. void clipToImageAlpha (const Image& image, const Rectangle<int>& srcClip, const AffineTransform& t) throw()
  900. {
  901. if (! image.hasAlphaChannel())
  902. {
  903. Path p;
  904. p.addRectangle (srcClip);
  905. clipToPath (p, t);
  906. return;
  907. }
  908. dupeEdgeTableIfMultiplyReferenced();
  909. const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset));
  910. const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight());
  911. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  912. EdgeTable& et = edgeTable->edgeTable;
  913. if (transform.isOnlyTranslation())
  914. {
  915. // If our translation doesn't involve any distortion, just use a simple blit..
  916. const int tx = (int) (transform.getTranslationX() * 256.0f);
  917. const int ty = (int) (transform.getTranslationY() * 256.0f);
  918. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  919. {
  920. const int imageX = ((tx + 128) >> 8);
  921. const int imageY = ((ty + 128) >> 8);
  922. if (image.getFormat() == Image::ARGB)
  923. straightClipImage (et, srcData, imageX, imageY, (PixelARGB*)0);
  924. else
  925. straightClipImage (et, srcData, imageX, imageY, (PixelAlpha*)0);
  926. return;
  927. }
  928. }
  929. if (transform.isSingularity())
  930. {
  931. et.clipToRectangle (Rectangle<int>());
  932. return;
  933. }
  934. {
  935. Path p;
  936. p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height);
  937. EdgeTable et2 (et.getMaximumBounds(), p, transform);
  938. et.clipToEdgeTable (et2);
  939. }
  940. if (! et.isEmpty())
  941. {
  942. if (image.getFormat() == Image::ARGB)
  943. transformedClipImage (et, srcData, transform, betterQuality, (PixelARGB*)0);
  944. else
  945. transformedClipImage (et, srcData, transform, betterQuality, (PixelAlpha*)0);
  946. }
  947. }
  948. template <class SrcPixelType>
  949. void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType *) throw()
  950. {
  951. TransformedImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, transform, 255, betterQuality);
  952. for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y)
  953. renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(),
  954. et.getMaximumBounds().getWidth());
  955. }
  956. template <class SrcPixelType>
  957. void straightClipImage (EdgeTable& et, const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType *) throw()
  958. {
  959. Rectangle<int> r (imageX, imageY, srcData.width, srcData.height);
  960. et.clipToRectangle (r);
  961. ImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, 255, imageX, imageY);
  962. for (int y = 0; y < r.getHeight(); ++y)
  963. renderer.clipEdgeTableLine (et, r.getX(), y + r.getY(), r.getWidth());
  964. }
  965. //==============================================================================
  966. class EdgeTableHolder : public ReferenceCountedObject
  967. {
  968. public:
  969. EdgeTableHolder (const EdgeTable& e) throw() : edgeTable (e) {}
  970. EdgeTable edgeTable;
  971. };
  972. ReferenceCountedObjectPtr<EdgeTableHolder> edgeTable;
  973. int xOffset, yOffset;
  974. Font font;
  975. FillType fillType;
  976. Graphics::ResamplingQuality interpolationQuality;
  977. private:
  978. LLGCSavedState& operator= (const LLGCSavedState&);
  979. void dupeEdgeTableIfMultiplyReferenced() throw()
  980. {
  981. if (edgeTable->getReferenceCount() > 1)
  982. edgeTable = new EdgeTableHolder (edgeTable->edgeTable);
  983. }
  984. //==============================================================================
  985. template <class DestPixelType>
  986. void renderGradient (EdgeTable& et, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform,
  987. const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) throw()
  988. {
  989. jassert (destData.pixelStride == sizeof (DestPixelType));
  990. if (g.isRadial)
  991. {
  992. if (isIdentity)
  993. {
  994. GradientEdgeTableRenderer <DestPixelType, RadialGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  995. et.iterate (renderer);
  996. }
  997. else
  998. {
  999. GradientEdgeTableRenderer <DestPixelType, TransformedRadialGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1000. et.iterate (renderer);
  1001. }
  1002. }
  1003. else
  1004. {
  1005. GradientEdgeTableRenderer <DestPixelType, LinearGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  1006. et.iterate (renderer);
  1007. }
  1008. }
  1009. //==============================================================================
  1010. template <class DestPixelType>
  1011. void renderSolidFill (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*) throw()
  1012. {
  1013. jassert (destData.pixelStride == sizeof (DestPixelType));
  1014. if (replaceContents)
  1015. {
  1016. SolidColourEdgeTableRenderer <DestPixelType, true> r (destData, fillColour);
  1017. et.iterate (r);
  1018. }
  1019. else
  1020. {
  1021. SolidColourEdgeTableRenderer <DestPixelType, false> r (destData, fillColour);
  1022. et.iterate (r);
  1023. }
  1024. }
  1025. //==============================================================================
  1026. void transformedRenderImage (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData,
  1027. const int alpha, const AffineTransform& transform, const bool betterQuality, const bool repeatPattern) throw()
  1028. {
  1029. switch (destImage.getFormat())
  1030. {
  1031. case Image::ARGB:
  1032. switch (srcImage.getFormat())
  1033. {
  1034. case Image::ARGB:
  1035. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1036. else { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1037. break;
  1038. case Image::RGB:
  1039. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1040. else { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1041. break;
  1042. default:
  1043. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1044. else { TransformedImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1045. break;
  1046. }
  1047. break;
  1048. case Image::RGB:
  1049. switch (srcImage.getFormat())
  1050. {
  1051. case Image::ARGB:
  1052. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1053. else { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1054. break;
  1055. case Image::RGB:
  1056. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1057. else { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1058. break;
  1059. default:
  1060. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1061. else { TransformedImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1062. break;
  1063. }
  1064. break;
  1065. default:
  1066. switch (srcImage.getFormat())
  1067. {
  1068. case Image::ARGB:
  1069. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1070. else { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1071. break;
  1072. case Image::RGB:
  1073. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1074. else { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1075. break;
  1076. default:
  1077. if (repeatPattern) { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, true> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1078. else { TransformedImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, false> r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); }
  1079. break;
  1080. }
  1081. break;
  1082. }
  1083. }
  1084. //==============================================================================
  1085. void blittedRenderImage (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData,
  1086. const Image::BitmapData& srcData, const int alpha, int x, int y, const bool repeatPattern) throw()
  1087. {
  1088. switch (destImage.getFormat())
  1089. {
  1090. case Image::ARGB:
  1091. switch (srcImage.getFormat())
  1092. {
  1093. case Image::ARGB:
  1094. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelARGB, PixelARGB, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1095. else { ImageFillEdgeTableRenderer <PixelARGB, PixelARGB, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1096. break;
  1097. case Image::RGB:
  1098. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelARGB, PixelRGB, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1099. else { ImageFillEdgeTableRenderer <PixelARGB, PixelRGB, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1100. break;
  1101. default:
  1102. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1103. else { ImageFillEdgeTableRenderer <PixelARGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1104. break;
  1105. }
  1106. break;
  1107. case Image::RGB:
  1108. switch (srcImage.getFormat())
  1109. {
  1110. case Image::ARGB:
  1111. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelRGB, PixelARGB, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1112. else { ImageFillEdgeTableRenderer <PixelRGB, PixelARGB, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1113. break;
  1114. case Image::RGB:
  1115. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelRGB, PixelRGB, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1116. else { ImageFillEdgeTableRenderer <PixelRGB, PixelRGB, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1117. break;
  1118. default:
  1119. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1120. else { ImageFillEdgeTableRenderer <PixelRGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1121. break;
  1122. }
  1123. break;
  1124. default:
  1125. switch (srcImage.getFormat())
  1126. {
  1127. case Image::ARGB:
  1128. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1129. else { ImageFillEdgeTableRenderer <PixelAlpha, PixelARGB, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1130. break;
  1131. case Image::RGB:
  1132. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1133. else { ImageFillEdgeTableRenderer <PixelAlpha, PixelRGB, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1134. break;
  1135. default:
  1136. if (repeatPattern) { ImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, true> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1137. else { ImageFillEdgeTableRenderer <PixelAlpha, PixelAlpha, false> r (destData, srcData, alpha, x, y); et.iterate (r); }
  1138. break;
  1139. }
  1140. break;
  1141. }
  1142. }
  1143. };
  1144. //==============================================================================
  1145. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_)
  1146. : image (image_)
  1147. {
  1148. currentState = new LLGCSavedState (image_.getBounds(), 0, 0, Font(),
  1149. FillType(), Graphics::mediumResamplingQuality);
  1150. }
  1151. LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer()
  1152. {
  1153. }
  1154. bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const
  1155. {
  1156. return false;
  1157. }
  1158. //==============================================================================
  1159. void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y)
  1160. {
  1161. currentState->xOffset += x;
  1162. currentState->yOffset += y;
  1163. }
  1164. bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle<int>& r)
  1165. {
  1166. return currentState->clipToRectangle (r);
  1167. }
  1168. bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion)
  1169. {
  1170. return currentState->clipToRectangleList (clipRegion);
  1171. }
  1172. void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle<int>& r)
  1173. {
  1174. currentState->excludeClipRectangle (r);
  1175. }
  1176. void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform)
  1177. {
  1178. currentState->clipToPath (path, transform);
  1179. }
  1180. void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform)
  1181. {
  1182. currentState->clipToImageAlpha (sourceImage, srcClip, transform);
  1183. }
  1184. bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle<int>& r)
  1185. {
  1186. return currentState->edgeTable->edgeTable.getMaximumBounds()
  1187. .intersects (r.translated (currentState->xOffset, currentState->yOffset));
  1188. }
  1189. const Rectangle<int> LowLevelGraphicsSoftwareRenderer::getClipBounds() const
  1190. {
  1191. return currentState->edgeTable->edgeTable.getMaximumBounds().translated (-currentState->xOffset, -currentState->yOffset);
  1192. }
  1193. bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const
  1194. {
  1195. return currentState->edgeTable->edgeTable.isEmpty();
  1196. }
  1197. //==============================================================================
  1198. void LowLevelGraphicsSoftwareRenderer::saveState()
  1199. {
  1200. stateStack.add (new LLGCSavedState (*currentState));
  1201. }
  1202. void LowLevelGraphicsSoftwareRenderer::restoreState()
  1203. {
  1204. LLGCSavedState* const top = stateStack.getLast();
  1205. if (top != 0)
  1206. {
  1207. currentState = top;
  1208. stateStack.removeLast (1, false);
  1209. }
  1210. else
  1211. {
  1212. jassertfalse // trying to pop with an empty stack!
  1213. }
  1214. }
  1215. //==============================================================================
  1216. void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType)
  1217. {
  1218. currentState->fillType = fillType;
  1219. }
  1220. void LowLevelGraphicsSoftwareRenderer::setOpacity (float newOpacity)
  1221. {
  1222. currentState->fillType.setOpacity (newOpacity);
  1223. }
  1224. void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality)
  1225. {
  1226. currentState->interpolationQuality = quality;
  1227. }
  1228. //==============================================================================
  1229. void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle<int>& r, const bool replaceExistingContents)
  1230. {
  1231. const Rectangle<int>& totalClip = currentState->edgeTable->edgeTable.getMaximumBounds();
  1232. const Rectangle<int> clipped (totalClip.getIntersection (r.translated (currentState->xOffset, currentState->yOffset)));
  1233. if (! clipped.isEmpty())
  1234. {
  1235. EdgeTable et (clipped);
  1236. currentState->fillEdgeTable (image, et, replaceExistingContents);
  1237. }
  1238. }
  1239. void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform)
  1240. {
  1241. EdgeTable et (currentState->edgeTable->edgeTable.getMaximumBounds(),
  1242. path, transform.translated ((float) currentState->xOffset,
  1243. (float) currentState->yOffset));
  1244. currentState->fillEdgeTable (image, et);
  1245. }
  1246. void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle<int>& srcClip,
  1247. const AffineTransform& transform, const bool fillEntireClipAsTiles)
  1248. {
  1249. jassert (sourceImage.getBounds().contains (srcClip));
  1250. currentState->renderImage (image, sourceImage, srcClip, transform,
  1251. fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0);
  1252. }
  1253. //==============================================================================
  1254. void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2)
  1255. {
  1256. Path p;
  1257. p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f);
  1258. fillPath (p, AffineTransform::identity);
  1259. }
  1260. void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom)
  1261. {
  1262. if (bottom > top)
  1263. {
  1264. EdgeTable et ((float) (x + currentState->xOffset), (float) (top + currentState->yOffset), 1.0f, (float) (bottom - top));
  1265. currentState->fillEdgeTable (image, et);
  1266. }
  1267. }
  1268. void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right)
  1269. {
  1270. if (right > left)
  1271. {
  1272. EdgeTable et ((float) (left + currentState->xOffset), (float) (y + currentState->yOffset),
  1273. (float) (right - left), 1.0f);
  1274. currentState->fillEdgeTable (image, et);
  1275. }
  1276. }
  1277. //==============================================================================
  1278. class GlyphCache : private DeletedAtShutdown
  1279. {
  1280. public:
  1281. GlyphCache() throw()
  1282. : accessCounter (0), hits (0), misses (0)
  1283. {
  1284. for (int i = 120; --i >= 0;)
  1285. glyphs.add (new CachedGlyph());
  1286. }
  1287. ~GlyphCache() throw()
  1288. {
  1289. clearSingletonInstance();
  1290. }
  1291. juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache);
  1292. //==============================================================================
  1293. void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) throw()
  1294. {
  1295. ++accessCounter;
  1296. int oldestCounter = std::numeric_limits<int>::max();
  1297. CachedGlyph* oldest = 0;
  1298. for (int i = glyphs.size(); --i >= 0;)
  1299. {
  1300. CachedGlyph* const glyph = glyphs.getUnchecked (i);
  1301. if (glyph->glyph == glyphNumber && glyph->font == font)
  1302. {
  1303. ++hits;
  1304. glyph->lastAccessCount = accessCounter;
  1305. glyph->draw (state, image, x, y);
  1306. return;
  1307. }
  1308. if (glyph->lastAccessCount <= oldestCounter)
  1309. {
  1310. oldestCounter = glyph->lastAccessCount;
  1311. oldest = glyph;
  1312. }
  1313. }
  1314. if (hits + ++misses > (glyphs.size() << 4))
  1315. {
  1316. if (misses * 2 > hits)
  1317. {
  1318. for (int i = 32; --i >= 0;)
  1319. glyphs.add (new CachedGlyph());
  1320. }
  1321. hits = misses = 0;
  1322. oldest = glyphs.getLast();
  1323. }
  1324. jassert (oldest != 0);
  1325. oldest->lastAccessCount = accessCounter;
  1326. oldest->generate (font, glyphNumber);
  1327. oldest->draw (state, image, x, y);
  1328. }
  1329. //==============================================================================
  1330. class CachedGlyph
  1331. {
  1332. public:
  1333. CachedGlyph() : glyph (0), lastAccessCount (0) {}
  1334. ~CachedGlyph() {}
  1335. void draw (LLGCSavedState& state, Image& image, const float x, const float y) const throw()
  1336. {
  1337. if (edgeTable != 0)
  1338. {
  1339. EdgeTable et (*edgeTable);
  1340. et.translate (x, roundToInt (y));
  1341. state.fillEdgeTable (image, et, false);
  1342. }
  1343. }
  1344. void generate (const Font& newFont, const int glyphNumber) throw()
  1345. {
  1346. font = newFont;
  1347. glyph = glyphNumber;
  1348. edgeTable = 0;
  1349. Path glyphPath;
  1350. font.getTypeface()->getOutlineForGlyph (glyphNumber, glyphPath);
  1351. if (! glyphPath.isEmpty())
  1352. {
  1353. const float fontHeight = font.getHeight();
  1354. const AffineTransform transform (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
  1355. .translated (0.0f, -0.5f));
  1356. edgeTable = new EdgeTable (glyphPath.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
  1357. glyphPath, transform);
  1358. }
  1359. }
  1360. int glyph, lastAccessCount;
  1361. Font font;
  1362. //==============================================================================
  1363. juce_UseDebuggingNewOperator
  1364. private:
  1365. ScopedPointer <EdgeTable> edgeTable;
  1366. CachedGlyph (const CachedGlyph&);
  1367. CachedGlyph& operator= (const CachedGlyph&);
  1368. };
  1369. //==============================================================================
  1370. juce_UseDebuggingNewOperator
  1371. private:
  1372. OwnedArray <CachedGlyph> glyphs;
  1373. int accessCounter, hits, misses;
  1374. GlyphCache (const GlyphCache&);
  1375. GlyphCache& operator= (const GlyphCache&);
  1376. };
  1377. juce_ImplementSingleton_SingleThreaded (GlyphCache);
  1378. void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont)
  1379. {
  1380. currentState->font = newFont;
  1381. }
  1382. const Font LowLevelGraphicsSoftwareRenderer::getFont()
  1383. {
  1384. return currentState->font;
  1385. }
  1386. void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
  1387. {
  1388. Font& f = currentState->font;
  1389. if (transform.isOnlyTranslation())
  1390. {
  1391. GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber,
  1392. transform.getTranslationX() + (float) currentState->xOffset,
  1393. transform.getTranslationY() + (float) currentState->yOffset);
  1394. }
  1395. else
  1396. {
  1397. Path p;
  1398. f.getTypeface()->getOutlineForGlyph (glyphNumber, p);
  1399. fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()).followedBy (transform));
  1400. }
  1401. }
  1402. #if JUCE_MSVC
  1403. #pragma warning (pop)
  1404. #endif
  1405. END_JUCE_NAMESPACE