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.

367 lines
12KB

  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) dx);
  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. : accessCounter (0), hits (0), misses (0)
  122. {
  123. addNewGlyphSlots (120);
  124. }
  125. ~GlyphCache()
  126. {
  127. getSingletonPointer() = nullptr;
  128. }
  129. static GlyphCache& getInstance()
  130. {
  131. GlyphCache*& g = getSingletonPointer();
  132. if (g == nullptr)
  133. g = new GlyphCache();
  134. return *g;
  135. }
  136. //==============================================================================
  137. void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, float x, float y)
  138. {
  139. ++accessCounter;
  140. int oldestCounter = std::numeric_limits<int>::max();
  141. CachedGlyphType* oldest = nullptr;
  142. for (int i = glyphs.size(); --i >= 0;)
  143. {
  144. CachedGlyphType* const glyph = glyphs.getUnchecked (i);
  145. if (glyph->glyph == glyphNumber && glyph->font == font)
  146. {
  147. ++hits;
  148. glyph->lastAccessCount = accessCounter;
  149. glyph->draw (target, x, y);
  150. return;
  151. }
  152. if (glyph->lastAccessCount <= oldestCounter)
  153. {
  154. oldestCounter = glyph->lastAccessCount;
  155. oldest = glyph;
  156. }
  157. }
  158. if (hits + ++misses > (glyphs.size() << 4))
  159. {
  160. if (misses * 2 > hits)
  161. addNewGlyphSlots (32);
  162. hits = misses = 0;
  163. oldest = glyphs.getLast();
  164. }
  165. jassert (oldest != nullptr);
  166. oldest->lastAccessCount = accessCounter;
  167. oldest->generate (font, glyphNumber);
  168. oldest->draw (target, x, y);
  169. }
  170. private:
  171. friend class OwnedArray <CachedGlyphType>;
  172. OwnedArray <CachedGlyphType> glyphs;
  173. int accessCounter, hits, misses;
  174. void addNewGlyphSlots (int num)
  175. {
  176. while (--num >= 0)
  177. glyphs.add (new CachedGlyphType());
  178. }
  179. static GlyphCache*& getSingletonPointer() noexcept
  180. {
  181. static GlyphCache* g = nullptr;
  182. return g;
  183. }
  184. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache);
  185. };
  186. //==============================================================================
  187. template <class StateObjectType>
  188. class SavedStateStack
  189. {
  190. public:
  191. SavedStateStack (StateObjectType* const initialState) noexcept
  192. : currentState (initialState)
  193. {}
  194. inline StateObjectType* operator->() const noexcept { return currentState; }
  195. inline StateObjectType& operator*() const noexcept { return *currentState; }
  196. void save()
  197. {
  198. stack.add (new StateObjectType (*currentState));
  199. }
  200. void restore()
  201. {
  202. StateObjectType* const top = stack.getLast();
  203. if (top != nullptr)
  204. {
  205. currentState = top;
  206. stack.removeLast (1, false);
  207. }
  208. else
  209. {
  210. jassertfalse; // trying to pop with an empty stack!
  211. }
  212. }
  213. void beginTransparencyLayer (float opacity)
  214. {
  215. save();
  216. currentState = currentState->beginTransparencyLayer (opacity);
  217. }
  218. void endTransparencyLayer()
  219. {
  220. const ScopedPointer<StateObjectType> finishedTransparencyLayer (currentState);
  221. restore();
  222. currentState->endTransparencyLayer (*finishedTransparencyLayer);
  223. }
  224. private:
  225. ScopedPointer<StateObjectType> currentState;
  226. OwnedArray<StateObjectType> stack;
  227. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedStateStack);
  228. };
  229. //==============================================================================
  230. // Calculates the alpha values and positions for rendering the edges of a non-pixel
  231. // aligned rectangle.
  232. struct FloatRectangleRasterisingInfo
  233. {
  234. FloatRectangleRasterisingInfo (const Rectangle<float>& area)
  235. : left (roundToInt (256.0f * area.getX())),
  236. top (roundToInt (256.0f * area.getY())),
  237. right (roundToInt (256.0f * area.getRight())),
  238. bottom (roundToInt (256.0f * area.getBottom()))
  239. {
  240. if ((top >> 8) == (bottom >> 8))
  241. {
  242. topAlpha = bottom - top;
  243. bottomAlpha = 0;
  244. totalTop = top >> 8;
  245. totalBottom = bottom = top = totalTop + 1;
  246. }
  247. else
  248. {
  249. if ((top & 255) == 0)
  250. {
  251. topAlpha = 0;
  252. top = totalTop = (top >> 8);
  253. }
  254. else
  255. {
  256. topAlpha = 255 - (top & 255);
  257. totalTop = (top >> 8);
  258. top = totalTop + 1;
  259. }
  260. bottomAlpha = bottom & 255;
  261. bottom >>= 8;
  262. totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0);
  263. }
  264. if ((left >> 8) == (right >> 8))
  265. {
  266. leftAlpha = right - left;
  267. rightAlpha = 0;
  268. totalLeft = (left >> 8);
  269. totalRight = right = left = totalLeft + 1;
  270. }
  271. else
  272. {
  273. if ((left & 255) == 0)
  274. {
  275. leftAlpha = 0;
  276. left = totalLeft = (left >> 8);
  277. }
  278. else
  279. {
  280. leftAlpha = 255 - (left & 255);
  281. totalLeft = (left >> 8);
  282. left = totalLeft + 1;
  283. }
  284. rightAlpha = right & 255;
  285. right >>= 8;
  286. totalRight = right + (rightAlpha != 0 ? 1 : 0);
  287. }
  288. }
  289. template <class Callback>
  290. void iterate (Callback& callback) const
  291. {
  292. if (topAlpha != 0) callback (totalLeft, totalTop, totalRight - totalLeft, 1, topAlpha);
  293. if (bottomAlpha != 0) callback (totalLeft, bottom, totalRight - totalLeft, 1, bottomAlpha);
  294. if (leftAlpha != 0) callback (totalLeft, totalTop, 1, totalBottom - totalTop, leftAlpha);
  295. if (rightAlpha != 0) callback (right, totalTop, 1, totalBottom - totalTop, rightAlpha);
  296. callback (left, top, right - left, bottom - top, 255);
  297. }
  298. inline bool isOnePixelWide() const noexcept { return right - left == 1 && leftAlpha + rightAlpha == 0; }
  299. inline int getTopLeftCornerAlpha() const noexcept { return (topAlpha * leftAlpha) >> 8; }
  300. inline int getTopRightCornerAlpha() const noexcept { return (topAlpha * rightAlpha) >> 8; }
  301. inline int getBottomLeftCornerAlpha() const noexcept { return (bottomAlpha * leftAlpha) >> 8; }
  302. inline int getBottomRightCornerAlpha() const noexcept { return (bottomAlpha * rightAlpha) >> 8; }
  303. //==============================================================================
  304. int left, top, right, bottom; // bounds of the solid central area, excluding anti-aliased edges
  305. int totalTop, totalLeft, totalBottom, totalRight; // bounds of the total area, including edges
  306. int topAlpha, leftAlpha, bottomAlpha, rightAlpha; // alpha of each anti-aliased edge
  307. };
  308. }
  309. #endif // __JUCE_RENDERINGHELPERS_JUCEHEADER__