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.

1598 lines
58KB

  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. forcedinline void blendLine (PixelType* dest, const PixelARGB& colour, int width) const throw()
  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. const 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 p3 = l.getPointAlongLine (0.0, 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 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 = roundDoubleToInt ((numEntries << (int) numScaleBits) / (double) (y2 - y1));
  173. start = roundDoubleToInt (y1 * scale);
  174. }
  175. else if (horizontal)
  176. {
  177. scale = roundDoubleToInt ((numEntries << (int) numScaleBits) / (double) (x2 - x1));
  178. start = roundDoubleToInt (x1 * scale);
  179. }
  180. else
  181. {
  182. grad = (y2 - y1) / (double) (x1 - x2);
  183. yTerm = y1 - x1 / grad;
  184. scale = roundDoubleToInt ((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 = roundDoubleToInt ((y - yTerm) * grad);
  194. }
  195. forcedinline 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. const 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 dx = gradient.x1 - gradient.x2;
  224. const float dy = gradient.y1 - gradient.y2;
  225. maxDist = dx * dx + dy * dy;
  226. invScale = numEntries / sqrt (maxDist);
  227. jassert (roundDoubleToInt (sqrt (maxDist) * invScale) <= numEntries);
  228. }
  229. forcedinline void setY (const int y) throw()
  230. {
  231. dy = y - gy1;
  232. dy *= dy;
  233. }
  234. forcedinline 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 : roundDoubleToInt (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. const 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. forcedinline 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, roundDoubleToInt (sqrt (x) * invScale))];
  277. }
  278. private:
  279. double tM10, tM00, lineYM01, lineYM11;
  280. const AffineTransform inverseTransform;
  281. TransformedRadialGradientPixelGenerator (const TransformedRadialGradientPixelGenerator&);
  282. const 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. const 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* sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y - yOffset);
  399. uint8* mask = (uint8*) (sourceLineStart + 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. const 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. {
  445. }
  446. forcedinline void setEdgeTableYPos (const int newY) throw()
  447. {
  448. y = newY;
  449. linePixels = (DestPixelType*) destData.getLinePointer (newY);
  450. }
  451. forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) throw()
  452. {
  453. alphaLevel *= extraAlpha;
  454. alphaLevel >>= 8;
  455. SrcPixelType p;
  456. generate (&p, x, 1);
  457. linePixels[x].blend (p, alphaLevel);
  458. }
  459. forcedinline void handleEdgeTableLine (const int x, int width, int alphaLevel) throw()
  460. {
  461. SrcPixelType* span = (SrcPixelType*) alloca (sizeof (SrcPixelType) * width);
  462. generate (span, x, width);
  463. DestPixelType* dest = linePixels + x;
  464. alphaLevel *= extraAlpha;
  465. alphaLevel >>= 8;
  466. if (alphaLevel < 0xfe)
  467. {
  468. do
  469. {
  470. dest++ ->blend (*span++, alphaLevel);
  471. } while (--width > 0);
  472. }
  473. else
  474. {
  475. do
  476. {
  477. dest++ ->blend (*span++);
  478. } while (--width > 0);
  479. }
  480. }
  481. void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) throw()
  482. {
  483. uint8* mask = (uint8*) alloca (sizeof (SrcPixelType) * width);
  484. y = y_;
  485. generate ((SrcPixelType*) mask, x, width);
  486. if (sizeof (SrcPixelType) == sizeof (PixelARGB))
  487. mask += PixelARGB::indexA;
  488. et.clipLineToMask (x, y_, mask, sizeof (SrcPixelType), width);
  489. }
  490. private:
  491. //==============================================================================
  492. void generate (PixelARGB* dest, const int x, int numPixels) throw()
  493. {
  494. this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels);
  495. do
  496. {
  497. int hiResX, hiResY;
  498. this->interpolator.next (hiResX, hiResY);
  499. hiResX += pixelOffsetInt;
  500. hiResY += pixelOffsetInt;
  501. int loResX = hiResX >> 8;
  502. int loResY = hiResY >> 8;
  503. if (repeatPattern)
  504. {
  505. loResX = safeModulo (loResX, srcData.width);
  506. loResY = safeModulo (loResY, srcData.height);
  507. }
  508. if (betterQuality
  509. && ((unsigned int) loResX) < (unsigned int) maxX
  510. && ((unsigned int) loResY) < (unsigned int) maxY)
  511. {
  512. uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 };
  513. hiResX &= 255;
  514. hiResY &= 255;
  515. const uint8* src = this->srcData.getPixelPointer (loResX, loResY);
  516. uint32 weight = (256 - hiResX) * (256 - hiResY);
  517. c[0] += weight * src[0];
  518. c[1] += weight * src[1];
  519. c[2] += weight * src[2];
  520. c[3] += weight * src[3];
  521. weight = hiResX * (256 - hiResY);
  522. c[0] += weight * src[4];
  523. c[1] += weight * src[5];
  524. c[2] += weight * src[6];
  525. c[3] += weight * src[7];
  526. src += this->srcData.lineStride;
  527. weight = (256 - hiResX) * hiResY;
  528. c[0] += weight * src[0];
  529. c[1] += weight * src[1];
  530. c[2] += weight * src[2];
  531. c[3] += weight * src[3];
  532. weight = hiResX * hiResY;
  533. c[0] += weight * src[4];
  534. c[1] += weight * src[5];
  535. c[2] += weight * src[6];
  536. c[3] += weight * src[7];
  537. dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16),
  538. (uint8) (c[PixelARGB::indexR] >> 16),
  539. (uint8) (c[PixelARGB::indexG] >> 16),
  540. (uint8) (c[PixelARGB::indexB] >> 16));
  541. }
  542. else
  543. {
  544. if (! repeatPattern)
  545. {
  546. // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable
  547. if (loResX < 0) loResX = 0;
  548. if (loResY < 0) loResY = 0;
  549. if (loResX > maxX) loResX = maxX;
  550. if (loResY > maxY) loResY = maxY;
  551. }
  552. dest->set (*(const PixelARGB*) this->srcData.getPixelPointer (loResX, loResY));
  553. }
  554. ++dest;
  555. } while (--numPixels > 0);
  556. }
  557. void generate (PixelRGB* dest, const int x, int numPixels) throw()
  558. {
  559. this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels);
  560. do
  561. {
  562. int hiResX, hiResY;
  563. this->interpolator.next (hiResX, hiResY);
  564. hiResX += pixelOffsetInt;
  565. hiResY += pixelOffsetInt;
  566. int loResX = hiResX >> 8;
  567. int loResY = hiResY >> 8;
  568. if (repeatPattern)
  569. {
  570. loResX = safeModulo (loResX, srcData.width);
  571. loResY = safeModulo (loResY, srcData.height);
  572. }
  573. if (betterQuality
  574. && ((unsigned int) loResX) < (unsigned int) maxX
  575. && ((unsigned int) loResY) < (unsigned int) maxY)
  576. {
  577. uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 };
  578. hiResX &= 255;
  579. hiResY &= 255;
  580. const uint8* src = this->srcData.getPixelPointer (loResX, loResY);
  581. unsigned int weight = (256 - hiResX) * (256 - hiResY);
  582. c[0] += weight * src[0];
  583. c[1] += weight * src[1];
  584. c[2] += weight * src[2];
  585. weight = hiResX * (256 - hiResY);
  586. c[0] += weight * src[3];
  587. c[1] += weight * src[4];
  588. c[2] += weight * src[5];
  589. src += this->srcData.lineStride;
  590. weight = (256 - hiResX) * hiResY;
  591. c[0] += weight * src[0];
  592. c[1] += weight * src[1];
  593. c[2] += weight * src[2];
  594. weight = hiResX * hiResY;
  595. c[0] += weight * src[3];
  596. c[1] += weight * src[4];
  597. c[2] += weight * src[5];
  598. dest->setARGB ((uint8) 255,
  599. (uint8) (c[PixelRGB::indexR] >> 16),
  600. (uint8) (c[PixelRGB::indexG] >> 16),
  601. (uint8) (c[PixelRGB::indexB] >> 16));
  602. }
  603. else
  604. {
  605. if (! repeatPattern)
  606. {
  607. // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable
  608. if (loResX < 0) loResX = 0;
  609. if (loResY < 0) loResY = 0;
  610. if (loResX > maxX) loResX = maxX;
  611. if (loResY > maxY) loResY = maxY;
  612. }
  613. dest->set (*(const PixelRGB*) this->srcData.getPixelPointer (loResX, loResY));
  614. }
  615. ++dest;
  616. } while (--numPixels > 0);
  617. }
  618. void generate (PixelAlpha* dest, const int x, int numPixels) throw()
  619. {
  620. this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels);
  621. do
  622. {
  623. int hiResX, hiResY;
  624. this->interpolator.next (hiResX, hiResY);
  625. hiResX += pixelOffsetInt;
  626. hiResY += pixelOffsetInt;
  627. int loResX = hiResX >> 8;
  628. int loResY = hiResY >> 8;
  629. if (repeatPattern)
  630. {
  631. loResX = safeModulo (loResX, srcData.width);
  632. loResY = safeModulo (loResY, srcData.height);
  633. }
  634. if (betterQuality
  635. && ((unsigned int) loResX) < (unsigned int) maxX
  636. && ((unsigned int) loResY) < (unsigned int) maxY)
  637. {
  638. hiResX &= 255;
  639. hiResY &= 255;
  640. const uint8* src = this->srcData.getPixelPointer (loResX, loResY);
  641. uint32 c = 256 * 128;
  642. c += src[0] * ((256 - hiResX) * (256 - hiResY));
  643. c += src[1] * (hiResX * (256 - hiResY));
  644. src += this->srcData.lineStride;
  645. c += src[0] * ((256 - hiResX) * hiResY);
  646. c += src[1] * (hiResX * hiResY);
  647. *((uint8*) dest) = (uint8) c;
  648. }
  649. else
  650. {
  651. if (! repeatPattern)
  652. {
  653. // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable
  654. if (loResX < 0) loResX = 0;
  655. if (loResY < 0) loResY = 0;
  656. if (loResX > maxX) loResX = maxX;
  657. if (loResY > maxY) loResY = maxY;
  658. }
  659. *((uint8*) dest) = *(this->srcData.getPixelPointer (loResX, loResY));
  660. }
  661. ++dest;
  662. } while (--numPixels > 0);
  663. }
  664. //==============================================================================
  665. class TransformedImageSpanInterpolator
  666. {
  667. public:
  668. TransformedImageSpanInterpolator (const AffineTransform& transform) throw()
  669. : inverseTransform (transform.inverted())
  670. {}
  671. void setStartOfLine (float x, float y, const int numPixels) throw()
  672. {
  673. float x1 = x, y1 = y;
  674. inverseTransform.transformPoint (x1, y1);
  675. x += numPixels;
  676. inverseTransform.transformPoint (x, y);
  677. xBresenham.set ((int) (x1 * 256.0f), (int) (x * 256.0f), numPixels);
  678. yBresenham.set ((int) (y1 * 256.0f), (int) (y * 256.0f), numPixels);
  679. }
  680. void next (int& x, int& y) throw()
  681. {
  682. x = xBresenham.n;
  683. xBresenham.stepToNext();
  684. y = yBresenham.n;
  685. yBresenham.stepToNext();
  686. }
  687. private:
  688. class BresenhamInterpolator
  689. {
  690. public:
  691. BresenhamInterpolator() throw() {}
  692. void set (const int n1, const int n2, const int numSteps_) throw()
  693. {
  694. numSteps = jmax (1, numSteps_);
  695. step = (n2 - n1) / numSteps;
  696. remainder = modulo = (n2 - n1) % numSteps;
  697. n = n1;
  698. if (modulo <= 0)
  699. {
  700. modulo += numSteps;
  701. remainder += numSteps;
  702. --step;
  703. }
  704. modulo -= numSteps;
  705. }
  706. forcedinline void stepToNext() throw()
  707. {
  708. modulo += remainder;
  709. n += step;
  710. if (modulo > 0)
  711. {
  712. modulo -= numSteps;
  713. ++n;
  714. }
  715. }
  716. int n;
  717. private:
  718. int numSteps, step, modulo, remainder;
  719. };
  720. const AffineTransform inverseTransform;
  721. BresenhamInterpolator xBresenham, yBresenham;
  722. TransformedImageSpanInterpolator (const TransformedImageSpanInterpolator&);
  723. const TransformedImageSpanInterpolator& operator= (const TransformedImageSpanInterpolator&);
  724. };
  725. //==============================================================================
  726. TransformedImageSpanInterpolator interpolator;
  727. const Image::BitmapData& destData;
  728. const Image::BitmapData& srcData;
  729. const int extraAlpha;
  730. const bool betterQuality;
  731. const float pixelOffset;
  732. const int pixelOffsetInt, maxX, maxY;
  733. int y;
  734. DestPixelType* linePixels;
  735. TransformedImageFillEdgeTableRenderer (const TransformedImageFillEdgeTableRenderer&);
  736. const TransformedImageFillEdgeTableRenderer& operator= (const TransformedImageFillEdgeTableRenderer&);
  737. };
  738. //==============================================================================
  739. class LLGCSavedState
  740. {
  741. public:
  742. LLGCSavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_,
  743. const Font& font_, const FillType& fillType_,
  744. const Graphics::ResamplingQuality interpolationQuality_) throw()
  745. : edgeTable (new EdgeTableHolder (EdgeTable (clip_))),
  746. xOffset (xOffset_), yOffset (yOffset_),
  747. font (font_), fillType (fillType_),
  748. interpolationQuality (interpolationQuality_)
  749. {
  750. }
  751. LLGCSavedState (const LLGCSavedState& other) throw()
  752. : edgeTable (other.edgeTable), xOffset (other.xOffset),
  753. yOffset (other.yOffset), font (other.font),
  754. fillType (other.fillType), interpolationQuality (other.interpolationQuality)
  755. {
  756. }
  757. ~LLGCSavedState() throw()
  758. {
  759. }
  760. bool clipToRectangle (const Rectangle& r) throw()
  761. {
  762. dupeEdgeTableIfMultiplyReferenced();
  763. edgeTable->edgeTable.clipToRectangle (r.translated (xOffset, yOffset));
  764. return ! edgeTable->edgeTable.isEmpty();
  765. }
  766. bool clipToRectangleList (const RectangleList& r) throw()
  767. {
  768. dupeEdgeTableIfMultiplyReferenced();
  769. RectangleList temp (r);
  770. temp.offsetAll (xOffset, yOffset);
  771. RectangleList totalArea (edgeTable->edgeTable.getMaximumBounds());
  772. totalArea.subtract (temp);
  773. for (RectangleList::Iterator i (totalArea); i.next();)
  774. edgeTable->edgeTable.excludeRectangle (*i.getRectangle());
  775. return ! edgeTable->edgeTable.isEmpty();
  776. }
  777. bool excludeClipRectangle (const Rectangle& r) throw()
  778. {
  779. dupeEdgeTableIfMultiplyReferenced();
  780. edgeTable->edgeTable.excludeRectangle (r.translated (xOffset, yOffset));
  781. return ! edgeTable->edgeTable.isEmpty();
  782. }
  783. void clipToPath (const Path& p, const AffineTransform& transform) throw()
  784. {
  785. dupeEdgeTableIfMultiplyReferenced();
  786. EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform.translated ((float) xOffset, (float) yOffset));
  787. edgeTable->edgeTable.clipToEdgeTable (et);
  788. }
  789. //==============================================================================
  790. void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw()
  791. {
  792. et.clipToEdgeTable (edgeTable->edgeTable);
  793. Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true);
  794. if (fillType.isGradient())
  795. {
  796. jassert (! replaceContents); // that option is just for solid colours
  797. ColourGradient g2 (*(fillType.gradient));
  798. g2.multiplyOpacity (fillType.getOpacity());
  799. AffineTransform transform (fillType.transform.translated ((float) xOffset, (float) yOffset));
  800. const bool isIdentity = transform.isOnlyTranslation();
  801. if (isIdentity)
  802. {
  803. // If our translation doesn't involve any distortion, we can speed it up..
  804. transform.transformPoint (g2.x1, g2.y1);
  805. transform.transformPoint (g2.x2, g2.y2);
  806. transform = AffineTransform::identity;
  807. }
  808. int numLookupEntries;
  809. PixelARGB* const lookupTable = g2.createLookupTable (transform, numLookupEntries);
  810. jassert (numLookupEntries > 0);
  811. switch (image.getFormat())
  812. {
  813. case Image::ARGB: renderGradient <PixelARGB> (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity); break;
  814. case Image::RGB: renderGradient <PixelRGB> (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity); break;
  815. default: renderGradient <PixelAlpha> (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity); break;
  816. }
  817. juce_free (lookupTable);
  818. }
  819. else if (fillType.isTiledImage())
  820. {
  821. renderImage (image, *(fillType.image), Rectangle (0, 0, fillType.image->getWidth(), fillType.image->getHeight()),
  822. fillType.transform, &et);
  823. }
  824. else
  825. {
  826. const PixelARGB fillColour (fillType.colour.getPixelARGB());
  827. switch (image.getFormat())
  828. {
  829. case Image::ARGB: renderSolidFill2 <PixelARGB> (et, destData, fillColour, replaceContents); break;
  830. case Image::RGB: renderSolidFill2 <PixelRGB> (et, destData, fillColour, replaceContents); break;
  831. default: renderSolidFill2 <PixelAlpha> (et, destData, fillColour, replaceContents); break;
  832. }
  833. }
  834. }
  835. //==============================================================================
  836. void renderImage (Image& destImage, const Image& sourceImage, const Rectangle& srcClip,
  837. const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw()
  838. {
  839. const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset));
  840. jassert (Rectangle (0, 0, sourceImage.getWidth(), sourceImage.getHeight()).contains (srcClip));
  841. const Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true);
  842. const Image::BitmapData srcData (sourceImage, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight());
  843. const int alpha = fillType.colour.getAlpha();
  844. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  845. if (transform.isOnlyTranslation())
  846. {
  847. // If our translation doesn't involve any distortion, just use a simple blit..
  848. const int tx = (int) (transform.getTranslationX() * 256.0f);
  849. const int ty = (int) (transform.getTranslationY() * 256.0f);
  850. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  851. {
  852. const Rectangle srcRect (srcClip.translated ((tx + 128) >> 8, (ty + 128) >> 8));
  853. if (tiledFillClipRegion != 0)
  854. {
  855. blittedRenderImage3 <true> (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, srcRect.getX(), srcRect.getY());
  856. }
  857. else
  858. {
  859. EdgeTable et (srcRect.getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight())));
  860. et.clipToEdgeTable (edgeTable->edgeTable);
  861. if (! et.isEmpty())
  862. blittedRenderImage3 <false> (sourceImage, destImage, et, destData, srcData, alpha, srcRect.getX(), srcRect.getY());
  863. }
  864. return;
  865. }
  866. }
  867. if (transform.isSingularity())
  868. return;
  869. if (tiledFillClipRegion != 0)
  870. {
  871. transformedRenderImage3 <true> (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality);
  872. }
  873. else
  874. {
  875. Path p;
  876. p.addRectangle (srcClip);
  877. EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform);
  878. et.clipToEdgeTable (edgeTable->edgeTable);
  879. if (! et.isEmpty())
  880. transformedRenderImage3 <false> (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality);
  881. }
  882. }
  883. //==============================================================================
  884. void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& t) throw()
  885. {
  886. if (! image.hasAlphaChannel())
  887. {
  888. Path p;
  889. p.addRectangle (srcClip);
  890. clipToPath (p, t);
  891. return;
  892. }
  893. dupeEdgeTableIfMultiplyReferenced();
  894. const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset));
  895. const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight());
  896. const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality);
  897. EdgeTable& et = edgeTable->edgeTable;
  898. if (transform.isOnlyTranslation())
  899. {
  900. // If our translation doesn't involve any distortion, just use a simple blit..
  901. const int tx = (int) (transform.getTranslationX() * 256.0f);
  902. const int ty = (int) (transform.getTranslationY() * 256.0f);
  903. if ((! betterQuality) || ((tx | ty) & 224) == 0)
  904. {
  905. const int imageX = ((tx + 128) >> 8);
  906. const int imageY = ((ty + 128) >> 8);
  907. if (image.getFormat() == Image::ARGB)
  908. straightClipImage <PixelARGB> (et, srcData, imageX, imageY);
  909. else
  910. straightClipImage <PixelAlpha> (et, srcData, imageX, imageY);
  911. return;
  912. }
  913. }
  914. if (transform.isSingularity())
  915. {
  916. et.clipToRectangle (Rectangle());
  917. return;
  918. }
  919. {
  920. Path p;
  921. p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height);
  922. EdgeTable et2 (et.getMaximumBounds(), p, transform);
  923. et.clipToEdgeTable (et2);
  924. }
  925. if (! et.isEmpty())
  926. {
  927. if (image.getFormat() == Image::ARGB)
  928. transformedClipImage <PixelARGB> (et, srcData, transform, betterQuality);
  929. else
  930. transformedClipImage <PixelAlpha> (et, srcData, transform, betterQuality);
  931. }
  932. }
  933. template <class SrcPixelType>
  934. void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality) throw()
  935. {
  936. TransformedImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, transform, 255, betterQuality);
  937. for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y)
  938. renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(),
  939. et.getMaximumBounds().getWidth());
  940. }
  941. template <class SrcPixelType>
  942. void straightClipImage (EdgeTable& et, const Image::BitmapData& srcData, int imageX, int imageY) throw()
  943. {
  944. Rectangle r (imageX, imageY, srcData.width, srcData.height);
  945. et.clipToRectangle (r);
  946. ImageFillEdgeTableRenderer <SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, 255, imageX, imageY);
  947. for (int y = 0; y < r.getHeight(); ++y)
  948. renderer.clipEdgeTableLine (et, r.getX(), y + r.getY(), r.getWidth());
  949. }
  950. //==============================================================================
  951. class EdgeTableHolder : public ReferenceCountedObject
  952. {
  953. public:
  954. EdgeTableHolder (const EdgeTable& e) throw() : edgeTable (e) {}
  955. EdgeTable edgeTable;
  956. };
  957. ReferenceCountedObjectPtr<EdgeTableHolder> edgeTable;
  958. int xOffset, yOffset;
  959. Font font;
  960. FillType fillType;
  961. Graphics::ResamplingQuality interpolationQuality;
  962. private:
  963. const LLGCSavedState& operator= (const LLGCSavedState&);
  964. void dupeEdgeTableIfMultiplyReferenced() throw()
  965. {
  966. if (edgeTable->getReferenceCount() > 1)
  967. edgeTable = new EdgeTableHolder (edgeTable->edgeTable);
  968. }
  969. //==============================================================================
  970. template <class DestPixelType>
  971. void renderGradient (EdgeTable& et, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform,
  972. const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity) throw()
  973. {
  974. jassert (destData.pixelStride == sizeof (DestPixelType));
  975. if (g.isRadial)
  976. {
  977. if (isIdentity)
  978. {
  979. GradientEdgeTableRenderer <DestPixelType, RadialGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  980. et.iterate (renderer);
  981. }
  982. else
  983. {
  984. GradientEdgeTableRenderer <DestPixelType, TransformedRadialGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  985. et.iterate (renderer);
  986. }
  987. }
  988. else
  989. {
  990. GradientEdgeTableRenderer <DestPixelType, LinearGradientPixelGenerator> renderer (destData, g, transform, lookupTable, numLookupEntries);
  991. et.iterate (renderer);
  992. }
  993. }
  994. //==============================================================================
  995. template <class DestPixelType, bool replaceContents>
  996. void renderSolidFill1 (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour) throw()
  997. {
  998. jassert (destData.pixelStride == sizeof (DestPixelType));
  999. SolidColourEdgeTableRenderer <DestPixelType, replaceContents> renderer (destData, fillColour);
  1000. et.iterate (renderer);
  1001. }
  1002. template <class DestPixelType>
  1003. void renderSolidFill2 (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents) throw()
  1004. {
  1005. if (replaceContents)
  1006. renderSolidFill1 <DestPixelType, true> (et, destData, fillColour);
  1007. else
  1008. renderSolidFill1 <DestPixelType, false> (et, destData, fillColour);
  1009. }
  1010. //==============================================================================
  1011. template <class SrcPixelType, class DestPixelType, bool repeatPattern>
  1012. void transformedRenderImage1 (const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData,
  1013. const int alpha, const AffineTransform& transform, const bool betterQuality) throw()
  1014. {
  1015. TransformedImageFillEdgeTableRenderer <DestPixelType, SrcPixelType, repeatPattern> renderer (destData, srcData, transform, alpha, betterQuality);
  1016. et.iterate (renderer);
  1017. }
  1018. template <class SrcPixelType, bool repeatPattern>
  1019. void transformedRenderImage2 (Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData,
  1020. const int alpha, const AffineTransform& transform, const bool betterQuality) throw()
  1021. {
  1022. switch (destImage.getFormat())
  1023. {
  1024. case Image::ARGB: transformedRenderImage1 <SrcPixelType, PixelARGB, repeatPattern> (et, destData, srcData, alpha, transform, betterQuality); break;
  1025. case Image::RGB: transformedRenderImage1 <SrcPixelType, PixelRGB, repeatPattern> (et, destData, srcData, alpha, transform, betterQuality); break;
  1026. default: transformedRenderImage1 <SrcPixelType, PixelAlpha, repeatPattern> (et, destData, srcData, alpha, transform, betterQuality); break;
  1027. }
  1028. }
  1029. template <bool repeatPattern>
  1030. void transformedRenderImage3 (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData,
  1031. const int alpha, const AffineTransform& transform, const bool betterQuality) throw()
  1032. {
  1033. switch (srcImage.getFormat())
  1034. {
  1035. case Image::ARGB: transformedRenderImage2 <PixelARGB, repeatPattern> (destImage, et, destData, srcData, alpha, transform, betterQuality); break;
  1036. case Image::RGB: transformedRenderImage2 <PixelRGB, repeatPattern> (destImage, et, destData, srcData, alpha, transform, betterQuality); break;
  1037. default: transformedRenderImage2 <PixelAlpha, repeatPattern> (destImage, et, destData, srcData, alpha, transform, betterQuality); break;
  1038. }
  1039. }
  1040. //==============================================================================
  1041. template <class SrcPixelType, class DestPixelType, bool repeatPattern>
  1042. void blittedRenderImage1 (const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData,
  1043. const int alpha, int x, int y) throw()
  1044. {
  1045. ImageFillEdgeTableRenderer <DestPixelType, SrcPixelType, repeatPattern> renderer (destData, srcData, alpha, x, y);
  1046. et.iterate (renderer);
  1047. }
  1048. template <class SrcPixelType, bool repeatPattern>
  1049. void blittedRenderImage2 (Image& destImage, const EdgeTable& et, const Image::BitmapData& destData,
  1050. const Image::BitmapData& srcData, const int alpha, int x, int y) throw()
  1051. {
  1052. switch (destImage.getFormat())
  1053. {
  1054. case Image::ARGB: blittedRenderImage1 <SrcPixelType, PixelARGB, repeatPattern> (et, destData, srcData, alpha, x, y); break;
  1055. case Image::RGB: blittedRenderImage1 <SrcPixelType, PixelRGB, repeatPattern> (et, destData, srcData, alpha, x, y); break;
  1056. default: blittedRenderImage1 <SrcPixelType, PixelAlpha, repeatPattern> (et, destData, srcData, alpha, x, y); break;
  1057. }
  1058. }
  1059. template <bool repeatPattern>
  1060. void blittedRenderImage3 (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData,
  1061. const Image::BitmapData& srcData, const int alpha, int x, int y) throw()
  1062. {
  1063. switch (srcImage.getFormat())
  1064. {
  1065. case Image::ARGB: blittedRenderImage2 <PixelARGB, repeatPattern> (destImage, et, destData, srcData, alpha, x, y); break;
  1066. case Image::RGB: blittedRenderImage2 <PixelRGB, repeatPattern> (destImage, et, destData, srcData, alpha, x, y); break;
  1067. default: blittedRenderImage2 <PixelAlpha, repeatPattern> (destImage, et, destData, srcData, alpha, x, y); break;
  1068. }
  1069. }
  1070. };
  1071. //==============================================================================
  1072. LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_)
  1073. : image (image_),
  1074. stateStack (20)
  1075. {
  1076. currentState = new LLGCSavedState (Rectangle (0, 0, image_.getWidth(), image_.getHeight()),
  1077. 0, 0, Font(), FillType(), Graphics::mediumResamplingQuality);
  1078. }
  1079. LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer()
  1080. {
  1081. delete currentState;
  1082. }
  1083. bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const
  1084. {
  1085. return false;
  1086. }
  1087. //==============================================================================
  1088. void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y)
  1089. {
  1090. currentState->xOffset += x;
  1091. currentState->yOffset += y;
  1092. }
  1093. bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r)
  1094. {
  1095. return currentState->clipToRectangle (r);
  1096. }
  1097. bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion)
  1098. {
  1099. return currentState->clipToRectangleList (clipRegion);
  1100. }
  1101. void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle& r)
  1102. {
  1103. currentState->excludeClipRectangle (r);
  1104. }
  1105. void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform)
  1106. {
  1107. currentState->clipToPath (path, transform);
  1108. }
  1109. void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform)
  1110. {
  1111. currentState->clipToImageAlpha (sourceImage, srcClip, transform);
  1112. }
  1113. bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle& r)
  1114. {
  1115. return currentState->edgeTable->edgeTable.getMaximumBounds()
  1116. .intersects (r.translated (currentState->xOffset, currentState->yOffset));
  1117. }
  1118. const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const
  1119. {
  1120. return currentState->edgeTable->edgeTable.getMaximumBounds().translated (-currentState->xOffset, -currentState->yOffset);
  1121. }
  1122. bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const
  1123. {
  1124. return currentState->edgeTable->edgeTable.isEmpty();
  1125. }
  1126. //==============================================================================
  1127. void LowLevelGraphicsSoftwareRenderer::saveState()
  1128. {
  1129. stateStack.add (new LLGCSavedState (*currentState));
  1130. }
  1131. void LowLevelGraphicsSoftwareRenderer::restoreState()
  1132. {
  1133. LLGCSavedState* const top = stateStack.getLast();
  1134. if (top != 0)
  1135. {
  1136. delete currentState;
  1137. currentState = top;
  1138. stateStack.removeLast (1, false);
  1139. }
  1140. else
  1141. {
  1142. jassertfalse // trying to pop with an empty stack!
  1143. }
  1144. }
  1145. //==============================================================================
  1146. void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType)
  1147. {
  1148. currentState->fillType = fillType;
  1149. }
  1150. void LowLevelGraphicsSoftwareRenderer::setOpacity (float newOpacity)
  1151. {
  1152. currentState->fillType.setOpacity (newOpacity);
  1153. }
  1154. void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality)
  1155. {
  1156. currentState->interpolationQuality = quality;
  1157. }
  1158. //==============================================================================
  1159. void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents)
  1160. {
  1161. const Rectangle& totalClip = currentState->edgeTable->edgeTable.getMaximumBounds();
  1162. const Rectangle clipped (totalClip.getIntersection (r.translated (currentState->xOffset, currentState->yOffset)));
  1163. if (! clipped.isEmpty())
  1164. {
  1165. EdgeTable et (clipped);
  1166. currentState->fillEdgeTable (image, et, replaceExistingContents);
  1167. }
  1168. }
  1169. void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform)
  1170. {
  1171. EdgeTable et (currentState->edgeTable->edgeTable.getMaximumBounds(),
  1172. path, transform.translated ((float) currentState->xOffset,
  1173. (float) currentState->yOffset));
  1174. currentState->fillEdgeTable (image, et);
  1175. }
  1176. void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip,
  1177. const AffineTransform& transform, const bool fillEntireClipAsTiles)
  1178. {
  1179. currentState->renderImage (image, sourceImage, srcClip, transform,
  1180. fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0);
  1181. }
  1182. //==============================================================================
  1183. void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2)
  1184. {
  1185. Path p;
  1186. p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f);
  1187. fillPath (p, AffineTransform::identity);
  1188. }
  1189. void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom)
  1190. {
  1191. if (bottom > top)
  1192. {
  1193. EdgeTable et ((float) (x + currentState->xOffset), (float) (top + currentState->yOffset), 1.0f, (float) (bottom - top));
  1194. currentState->fillEdgeTable (image, et);
  1195. }
  1196. }
  1197. void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right)
  1198. {
  1199. if (right > left)
  1200. {
  1201. EdgeTable et ((float) (left + currentState->xOffset), (float) (y + currentState->yOffset),
  1202. (float) (right - left), 1.0f);
  1203. currentState->fillEdgeTable (image, et);
  1204. }
  1205. }
  1206. //==============================================================================
  1207. class GlyphCache : private DeletedAtShutdown
  1208. {
  1209. public:
  1210. GlyphCache() throw()
  1211. : accessCounter (0), hits (0), misses (0)
  1212. {
  1213. for (int i = 120; --i >= 0;)
  1214. glyphs.add (new CachedGlyph());
  1215. }
  1216. ~GlyphCache() throw()
  1217. {
  1218. clearSingletonInstance();
  1219. }
  1220. juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache);
  1221. //==============================================================================
  1222. void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) throw()
  1223. {
  1224. ++accessCounter;
  1225. int oldestCounter = INT_MAX;
  1226. CachedGlyph* oldest = 0;
  1227. for (int i = glyphs.size(); --i >= 0;)
  1228. {
  1229. CachedGlyph* const glyph = glyphs.getUnchecked (i);
  1230. if (glyph->glyph == glyphNumber && glyph->font == font)
  1231. {
  1232. ++hits;
  1233. glyph->lastAccessCount = accessCounter;
  1234. glyph->draw (state, image, x, y);
  1235. return;
  1236. }
  1237. if (glyph->lastAccessCount <= oldestCounter)
  1238. {
  1239. oldestCounter = glyph->lastAccessCount;
  1240. oldest = glyph;
  1241. }
  1242. }
  1243. if (hits + ++misses > (glyphs.size() << 4))
  1244. {
  1245. if (misses * 2 > hits)
  1246. {
  1247. for (int i = 32; --i >= 0;)
  1248. glyphs.add (new CachedGlyph());
  1249. }
  1250. hits = misses = 0;
  1251. oldest = glyphs.getLast();
  1252. }
  1253. jassert (oldest != 0);
  1254. oldest->lastAccessCount = accessCounter;
  1255. oldest->generate (font, glyphNumber);
  1256. oldest->draw (state, image, x, y);
  1257. }
  1258. //==============================================================================
  1259. class CachedGlyph
  1260. {
  1261. public:
  1262. CachedGlyph() throw() : glyph (0), lastAccessCount (0), edgeTable (0) {}
  1263. ~CachedGlyph() throw() { delete edgeTable; }
  1264. void draw (LLGCSavedState& state, Image& image, const float x, const float y) const throw()
  1265. {
  1266. if (edgeTable != 0)
  1267. {
  1268. EdgeTable et (*edgeTable);
  1269. et.translate (x, roundFloatToInt (y));
  1270. state.fillEdgeTable (image, et, false);
  1271. }
  1272. }
  1273. void generate (const Font& newFont, const int glyphNumber) throw()
  1274. {
  1275. font = newFont;
  1276. glyph = glyphNumber;
  1277. deleteAndZero (edgeTable);
  1278. Path glyphPath;
  1279. font.getTypeface()->getOutlineForGlyph (glyphNumber, glyphPath);
  1280. if (! glyphPath.isEmpty())
  1281. {
  1282. const float fontHeight = font.getHeight();
  1283. const AffineTransform transform (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight));
  1284. float px, py, pw, ph;
  1285. glyphPath.getBoundsTransformed (transform.translated (0.0f, -0.5f), px, py, pw, ph);
  1286. Rectangle clip ((int) floorf (px), (int) floorf (py),
  1287. roundFloatToInt (pw) + 2, roundFloatToInt (ph) + 2);
  1288. edgeTable = new EdgeTable (clip, glyphPath, transform);
  1289. }
  1290. }
  1291. int glyph, lastAccessCount;
  1292. Font font;
  1293. //==============================================================================
  1294. juce_UseDebuggingNewOperator
  1295. private:
  1296. EdgeTable* edgeTable;
  1297. CachedGlyph (const CachedGlyph&);
  1298. const CachedGlyph& operator= (const CachedGlyph&);
  1299. };
  1300. //==============================================================================
  1301. juce_UseDebuggingNewOperator
  1302. private:
  1303. OwnedArray <CachedGlyph> glyphs;
  1304. int accessCounter, hits, misses;
  1305. GlyphCache (const GlyphCache&);
  1306. const GlyphCache& operator= (const GlyphCache&);
  1307. };
  1308. juce_ImplementSingleton_SingleThreaded (GlyphCache);
  1309. void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont)
  1310. {
  1311. currentState->font = newFont;
  1312. }
  1313. const Font LowLevelGraphicsSoftwareRenderer::getFont()
  1314. {
  1315. return currentState->font;
  1316. }
  1317. void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
  1318. {
  1319. Font& f = currentState->font;
  1320. if (transform.isOnlyTranslation())
  1321. {
  1322. GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber,
  1323. transform.getTranslationX() + (float) currentState->xOffset,
  1324. transform.getTranslationY() + (float) currentState->yOffset);
  1325. }
  1326. else
  1327. {
  1328. Path p;
  1329. f.getTypeface()->getOutlineForGlyph (glyphNumber, p);
  1330. fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()).followedBy (transform));
  1331. }
  1332. }
  1333. #if JUCE_MSVC
  1334. #pragma warning (pop)
  1335. #endif
  1336. END_JUCE_NAMESPACE