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.

434 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. #ifndef __JUCE_RENDERINGHELPERS_JUCEHEADER__
  19. #define __JUCE_RENDERINGHELPERS_JUCEHEADER__
  20. namespace RenderingHelpers
  21. {
  22. //==============================================================================
  23. /** Holds either a simple integer translation, or an affine transform.
  24. */
  25. class TranslationOrTransform
  26. {
  27. public:
  28. TranslationOrTransform (int xOffset_, int yOffset_) noexcept
  29. : xOffset (xOffset_), yOffset (yOffset_), isOnlyTranslated (true)
  30. {
  31. }
  32. TranslationOrTransform (const TranslationOrTransform& other) noexcept
  33. : complexTransform (other.complexTransform),
  34. xOffset (other.xOffset), yOffset (other.yOffset),
  35. isOnlyTranslated (other.isOnlyTranslated)
  36. {
  37. }
  38. AffineTransform getTransform() const noexcept
  39. {
  40. return isOnlyTranslated ? AffineTransform::translation ((float) xOffset, (float) yOffset)
  41. : complexTransform;
  42. }
  43. AffineTransform getTransformWith (const AffineTransform& userTransform) const noexcept
  44. {
  45. return isOnlyTranslated ? userTransform.translated ((float) xOffset, (float) yOffset)
  46. : userTransform.followedBy (complexTransform);
  47. }
  48. void setOrigin (const int x, const int y) noexcept
  49. {
  50. if (isOnlyTranslated)
  51. {
  52. xOffset += x;
  53. yOffset += y;
  54. }
  55. else
  56. {
  57. complexTransform = AffineTransform::translation ((float) x, (float) y)
  58. .followedBy (complexTransform);
  59. }
  60. }
  61. void addTransform (const AffineTransform& t) noexcept
  62. {
  63. if (isOnlyTranslated
  64. && t.isOnlyTranslation()
  65. && isIntegerTranslation (t))
  66. {
  67. xOffset += (int) t.getTranslationX();
  68. yOffset += (int) t.getTranslationY();
  69. }
  70. else
  71. {
  72. complexTransform = getTransformWith (t);
  73. isOnlyTranslated = false;
  74. }
  75. }
  76. float getScaleFactor() const noexcept
  77. {
  78. return isOnlyTranslated ? 1.0f : complexTransform.getScaleFactor();
  79. }
  80. void moveOriginInDeviceSpace (const int dx, const int dy) noexcept
  81. {
  82. if (isOnlyTranslated)
  83. {
  84. xOffset += dx;
  85. yOffset += dy;
  86. }
  87. else
  88. {
  89. complexTransform = complexTransform.translated ((float) dx, (float) dy);
  90. }
  91. }
  92. template <typename Type>
  93. Rectangle<Type> translated (const Rectangle<Type>& r) const noexcept
  94. {
  95. jassert (isOnlyTranslated);
  96. return r.translated (static_cast <Type> (xOffset),
  97. static_cast <Type> (yOffset));
  98. }
  99. Rectangle<int> deviceSpaceToUserSpace (const Rectangle<int>& r) const noexcept
  100. {
  101. return isOnlyTranslated ? r.translated (-xOffset, -yOffset)
  102. : r.toFloat().transformed (complexTransform.inverted()).getSmallestIntegerContainer();
  103. }
  104. AffineTransform complexTransform;
  105. int xOffset, yOffset;
  106. bool isOnlyTranslated;
  107. private:
  108. static inline bool isIntegerTranslation (const AffineTransform& t) noexcept
  109. {
  110. const int tx = (int) (t.getTranslationX() * 256.0f);
  111. const int ty = (int) (t.getTranslationY() * 256.0f);
  112. return ((tx | ty) & 0xf8) == 0;
  113. }
  114. };
  115. //==============================================================================
  116. template <class CachedGlyphType, class RenderTargetType>
  117. class GlyphCache : private DeletedAtShutdown
  118. {
  119. public:
  120. GlyphCache()
  121. {
  122. addNewGlyphSlots (120);
  123. }
  124. ~GlyphCache()
  125. {
  126. getSingletonPointer() = nullptr;
  127. }
  128. static GlyphCache& getInstance()
  129. {
  130. GlyphCache*& g = getSingletonPointer();
  131. if (g == nullptr)
  132. g = new GlyphCache();
  133. return *g;
  134. }
  135. //==============================================================================
  136. void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, float x, float y)
  137. {
  138. ++accessCounter;
  139. CachedGlyphType* glyph = nullptr;
  140. const ScopedReadLock srl (lock);
  141. for (int i = glyphs.size(); --i >= 0;)
  142. {
  143. CachedGlyphType* const g = glyphs.getUnchecked (i);
  144. if (g->glyph == glyphNumber && g->font == font)
  145. {
  146. glyph = g;
  147. ++hits;
  148. break;
  149. }
  150. }
  151. if (glyph == nullptr)
  152. {
  153. ++misses;
  154. const ScopedWriteLock swl (lock);
  155. if (hits.value + misses.value > glyphs.size() * 16)
  156. {
  157. if (misses.value * 2 > hits.value)
  158. addNewGlyphSlots (32);
  159. hits.set (0);
  160. misses.set (0);
  161. glyph = glyphs.getLast();
  162. }
  163. else
  164. {
  165. glyph = findLeastRecentlyUsedGlyph();
  166. }
  167. jassert (glyph != nullptr);
  168. glyph->generate (font, glyphNumber);
  169. }
  170. glyph->lastAccessCount = accessCounter.value;
  171. glyph->draw (target, x, y);
  172. }
  173. private:
  174. friend class OwnedArray <CachedGlyphType>;
  175. OwnedArray <CachedGlyphType> glyphs;
  176. Atomic<int> accessCounter, hits, misses;
  177. ReadWriteLock lock;
  178. void addNewGlyphSlots (int num)
  179. {
  180. while (--num >= 0)
  181. glyphs.add (new CachedGlyphType());
  182. }
  183. CachedGlyphType* findLeastRecentlyUsedGlyph() const noexcept
  184. {
  185. CachedGlyphType* oldest = glyphs.getLast();
  186. int oldestCounter = oldest->lastAccessCount;
  187. for (int i = glyphs.size() - 1; --i >= 0;)
  188. {
  189. CachedGlyphType* const glyph = glyphs.getUnchecked(i);
  190. if (glyph->lastAccessCount <= oldestCounter)
  191. {
  192. oldestCounter = glyph->lastAccessCount;
  193. oldest = glyph;
  194. }
  195. }
  196. return oldest;
  197. }
  198. static GlyphCache*& getSingletonPointer() noexcept
  199. {
  200. static GlyphCache* g = nullptr;
  201. return g;
  202. }
  203. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache);
  204. };
  205. //==============================================================================
  206. template <class RendererType>
  207. class CachedGlyphEdgeTable
  208. {
  209. public:
  210. CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {}
  211. void draw (RendererType& state, float x, const float y) const
  212. {
  213. if (snapToIntegerCoordinate)
  214. x = std::floor (x + 0.5f);
  215. if (edgeTable != nullptr)
  216. state.fillEdgeTable (*edgeTable, x, roundToInt (y));
  217. }
  218. void generate (const Font& newFont, const int glyphNumber)
  219. {
  220. font = newFont;
  221. Typeface* const typeface = newFont.getTypeface();
  222. snapToIntegerCoordinate = typeface->isHinted();
  223. glyph = glyphNumber;
  224. const float fontHeight = font.getHeight();
  225. edgeTable = typeface->getEdgeTableForGlyph (glyphNumber,
  226. AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
  227. #if JUCE_MAC || JUCE_IOS
  228. .translated (0.0f, -0.5f)
  229. #endif
  230. );
  231. }
  232. Font font;
  233. int glyph, lastAccessCount;
  234. bool snapToIntegerCoordinate;
  235. private:
  236. ScopedPointer <EdgeTable> edgeTable;
  237. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable);
  238. };
  239. //==============================================================================
  240. template <class StateObjectType>
  241. class SavedStateStack
  242. {
  243. public:
  244. SavedStateStack (StateObjectType* const initialState) noexcept
  245. : currentState (initialState)
  246. {}
  247. inline StateObjectType* operator->() const noexcept { return currentState; }
  248. inline StateObjectType& operator*() const noexcept { return *currentState; }
  249. void save()
  250. {
  251. stack.add (new StateObjectType (*currentState));
  252. }
  253. void restore()
  254. {
  255. StateObjectType* const top = stack.getLast();
  256. if (top != nullptr)
  257. {
  258. currentState = top;
  259. stack.removeLast (1, false);
  260. }
  261. else
  262. {
  263. jassertfalse; // trying to pop with an empty stack!
  264. }
  265. }
  266. void beginTransparencyLayer (float opacity)
  267. {
  268. save();
  269. currentState = currentState->beginTransparencyLayer (opacity);
  270. }
  271. void endTransparencyLayer()
  272. {
  273. const ScopedPointer<StateObjectType> finishedTransparencyLayer (currentState);
  274. restore();
  275. currentState->endTransparencyLayer (*finishedTransparencyLayer);
  276. }
  277. private:
  278. ScopedPointer<StateObjectType> currentState;
  279. OwnedArray<StateObjectType> stack;
  280. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedStateStack);
  281. };
  282. //==============================================================================
  283. // Calculates the alpha values and positions for rendering the edges of a non-pixel
  284. // aligned rectangle.
  285. struct FloatRectangleRasterisingInfo
  286. {
  287. FloatRectangleRasterisingInfo (const Rectangle<float>& area)
  288. : left (roundToInt (256.0f * area.getX())),
  289. top (roundToInt (256.0f * area.getY())),
  290. right (roundToInt (256.0f * area.getRight())),
  291. bottom (roundToInt (256.0f * area.getBottom()))
  292. {
  293. if ((top >> 8) == (bottom >> 8))
  294. {
  295. topAlpha = bottom - top;
  296. bottomAlpha = 0;
  297. totalTop = top >> 8;
  298. totalBottom = bottom = top = totalTop + 1;
  299. }
  300. else
  301. {
  302. if ((top & 255) == 0)
  303. {
  304. topAlpha = 0;
  305. top = totalTop = (top >> 8);
  306. }
  307. else
  308. {
  309. topAlpha = 255 - (top & 255);
  310. totalTop = (top >> 8);
  311. top = totalTop + 1;
  312. }
  313. bottomAlpha = bottom & 255;
  314. bottom >>= 8;
  315. totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0);
  316. }
  317. if ((left >> 8) == (right >> 8))
  318. {
  319. leftAlpha = right - left;
  320. rightAlpha = 0;
  321. totalLeft = (left >> 8);
  322. totalRight = right = left = totalLeft + 1;
  323. }
  324. else
  325. {
  326. if ((left & 255) == 0)
  327. {
  328. leftAlpha = 0;
  329. left = totalLeft = (left >> 8);
  330. }
  331. else
  332. {
  333. leftAlpha = 255 - (left & 255);
  334. totalLeft = (left >> 8);
  335. left = totalLeft + 1;
  336. }
  337. rightAlpha = right & 255;
  338. right >>= 8;
  339. totalRight = right + (rightAlpha != 0 ? 1 : 0);
  340. }
  341. }
  342. template <class Callback>
  343. void iterate (Callback& callback) const
  344. {
  345. if (topAlpha != 0) callback (totalLeft, totalTop, totalRight - totalLeft, 1, topAlpha);
  346. if (bottomAlpha != 0) callback (totalLeft, bottom, totalRight - totalLeft, 1, bottomAlpha);
  347. if (leftAlpha != 0) callback (totalLeft, totalTop, 1, totalBottom - totalTop, leftAlpha);
  348. if (rightAlpha != 0) callback (right, totalTop, 1, totalBottom - totalTop, rightAlpha);
  349. callback (left, top, right - left, bottom - top, 255);
  350. }
  351. inline bool isOnePixelWide() const noexcept { return right - left == 1 && leftAlpha + rightAlpha == 0; }
  352. inline int getTopLeftCornerAlpha() const noexcept { return (topAlpha * leftAlpha) >> 8; }
  353. inline int getTopRightCornerAlpha() const noexcept { return (topAlpha * rightAlpha) >> 8; }
  354. inline int getBottomLeftCornerAlpha() const noexcept { return (bottomAlpha * leftAlpha) >> 8; }
  355. inline int getBottomRightCornerAlpha() const noexcept { return (bottomAlpha * rightAlpha) >> 8; }
  356. //==============================================================================
  357. int left, top, right, bottom; // bounds of the solid central area, excluding anti-aliased edges
  358. int totalTop, totalLeft, totalBottom, totalRight; // bounds of the total area, including edges
  359. int topAlpha, leftAlpha, bottomAlpha, rightAlpha; // alpha of each anti-aliased edge
  360. };
  361. }
  362. #endif // __JUCE_RENDERINGHELPERS_JUCEHEADER__