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.

728 lines
20KB

  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. namespace FontValues
  19. {
  20. static float limitFontHeight (const float height) noexcept
  21. {
  22. return jlimit (0.1f, 10000.0f, height);
  23. }
  24. const float defaultFontHeight = 14.0f;
  25. String fallbackFont;
  26. String fallbackFontStyle;
  27. }
  28. typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&);
  29. GetTypefaceForFont juce_getTypefaceForFont = nullptr;
  30. //==============================================================================
  31. class TypefaceCache : private DeletedAtShutdown
  32. {
  33. public:
  34. TypefaceCache() : counter (0)
  35. {
  36. setSize (10);
  37. }
  38. ~TypefaceCache()
  39. {
  40. clearSingletonInstance();
  41. }
  42. juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache);
  43. void setSize (const int numToCache)
  44. {
  45. faces.clear();
  46. faces.insertMultiple (-1, CachedFace(), numToCache);
  47. }
  48. void clear()
  49. {
  50. setSize (faces.size());
  51. defaultFace = nullptr;
  52. }
  53. Typeface::Ptr findTypefaceFor (const Font& font)
  54. {
  55. const String faceName (font.getTypefaceName());
  56. const String faceStyle (font.getTypefaceStyle());
  57. jassert (faceName.isNotEmpty());
  58. for (int i = faces.size(); --i >= 0;)
  59. {
  60. CachedFace& face = faces.getReference(i);
  61. if (face.typefaceName == faceName
  62. && face.typefaceStyle == faceStyle
  63. && face.typeface != nullptr
  64. && face.typeface->isSuitableForFont (font))
  65. {
  66. face.lastUsageCount = ++counter;
  67. return face.typeface;
  68. }
  69. }
  70. int replaceIndex = 0;
  71. size_t bestLastUsageCount = std::numeric_limits<size_t>::max();
  72. for (int i = faces.size(); --i >= 0;)
  73. {
  74. const size_t lu = faces.getReference(i).lastUsageCount;
  75. if (bestLastUsageCount > lu)
  76. {
  77. bestLastUsageCount = lu;
  78. replaceIndex = i;
  79. }
  80. }
  81. CachedFace& face = faces.getReference (replaceIndex);
  82. face.typefaceName = faceName;
  83. face.typefaceStyle = faceStyle;
  84. face.lastUsageCount = ++counter;
  85. if (juce_getTypefaceForFont == nullptr)
  86. face.typeface = Font::getDefaultTypefaceForFont (font);
  87. else
  88. face.typeface = juce_getTypefaceForFont (font);
  89. jassert (face.typeface != nullptr); // the look and feel must return a typeface!
  90. if (defaultFace == nullptr && font == Font())
  91. defaultFace = face.typeface;
  92. return face.typeface;
  93. }
  94. Typeface::Ptr getDefaultTypeface() const noexcept
  95. {
  96. return defaultFace;
  97. }
  98. private:
  99. struct CachedFace
  100. {
  101. CachedFace() noexcept : lastUsageCount (0) {}
  102. // Although it seems a bit wacky to store the name here, it's because it may be a
  103. // placeholder rather than a real one, e.g. "<Sans-Serif>" vs the actual typeface name.
  104. // Since the typeface itself doesn't know that it may have this alias, the name under
  105. // which it was fetched needs to be stored separately.
  106. String typefaceName;
  107. String typefaceStyle;
  108. size_t lastUsageCount;
  109. Typeface::Ptr typeface;
  110. };
  111. Array <CachedFace> faces;
  112. Typeface::Ptr defaultFace;
  113. size_t counter;
  114. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache)
  115. };
  116. juce_ImplementSingleton_SingleThreaded (TypefaceCache)
  117. void Typeface::setTypefaceCacheSize (int numFontsToCache)
  118. {
  119. TypefaceCache::getInstance()->setSize (numFontsToCache);
  120. }
  121. void Typeface::clearTypefaceCache()
  122. {
  123. TypefaceCache::getInstance()->clear();
  124. }
  125. //==============================================================================
  126. class Font::SharedFontInternal : public ReferenceCountedObject
  127. {
  128. public:
  129. SharedFontInternal() noexcept
  130. : typefaceName (Font::getDefaultSansSerifFontName()),
  131. typefaceStyle (Font::getDefaultStyle()),
  132. height (FontValues::defaultFontHeight),
  133. horizontalScale (1.0f),
  134. kerning (0),
  135. ascent (0),
  136. underline (false),
  137. typeface (TypefaceCache::getInstance()->getDefaultTypeface())
  138. {
  139. }
  140. SharedFontInternal (const String& style, const float fontHeight,
  141. const bool isUnderlined) noexcept
  142. : typefaceName (Font::getDefaultSansSerifFontName()),
  143. typefaceStyle (style),
  144. height (fontHeight),
  145. horizontalScale (1.0f),
  146. kerning (0),
  147. ascent (0),
  148. underline (isUnderlined),
  149. typeface (nullptr)
  150. {
  151. }
  152. SharedFontInternal (const String& name, const String& style,
  153. const float fontHeight, const bool isUnderlined) noexcept
  154. : typefaceName (name),
  155. typefaceStyle (style),
  156. height (fontHeight),
  157. horizontalScale (1.0f),
  158. kerning (0),
  159. ascent (0),
  160. underline (isUnderlined),
  161. typeface (nullptr)
  162. {
  163. if (typefaceName.isEmpty())
  164. typefaceName = Font::getDefaultSansSerifFontName();
  165. }
  166. explicit SharedFontInternal (const Typeface::Ptr& face) noexcept
  167. : typefaceName (face->getName()),
  168. typefaceStyle (face->getStyle()),
  169. height (FontValues::defaultFontHeight),
  170. horizontalScale (1.0f),
  171. kerning (0),
  172. ascent (0),
  173. underline (false),
  174. typeface (face)
  175. {
  176. jassert (typefaceName.isNotEmpty());
  177. }
  178. SharedFontInternal (const SharedFontInternal& other) noexcept
  179. : typefaceName (other.typefaceName),
  180. typefaceStyle (other.typefaceStyle),
  181. height (other.height),
  182. horizontalScale (other.horizontalScale),
  183. kerning (other.kerning),
  184. ascent (other.ascent),
  185. underline (other.underline),
  186. typeface (other.typeface)
  187. {
  188. }
  189. bool operator== (const SharedFontInternal& other) const noexcept
  190. {
  191. return height == other.height
  192. && underline == other.underline
  193. && horizontalScale == other.horizontalScale
  194. && kerning == other.kerning
  195. && typefaceName == other.typefaceName
  196. && typefaceStyle == other.typefaceStyle;
  197. }
  198. String typefaceName, typefaceStyle;
  199. float height, horizontalScale, kerning, ascent;
  200. bool underline;
  201. Typeface::Ptr typeface;
  202. };
  203. //==============================================================================
  204. Font::Font()
  205. : font (new SharedFontInternal())
  206. {
  207. }
  208. Font::Font (const float fontHeight, const int styleFlags)
  209. : font (new SharedFontInternal (FontStyleHelpers::getStyleName (styleFlags),
  210. FontValues::limitFontHeight (fontHeight),
  211. (styleFlags & underlined) != 0))
  212. {
  213. }
  214. Font::Font (const String& typefaceName, const float fontHeight, const int styleFlags)
  215. : font (new SharedFontInternal (typefaceName,
  216. FontStyleHelpers::getStyleName (styleFlags),
  217. FontValues::limitFontHeight (fontHeight),
  218. (styleFlags & underlined) != 0))
  219. {
  220. }
  221. Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight)
  222. : font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight), false))
  223. {
  224. }
  225. Font::Font (const Typeface::Ptr& typeface)
  226. : font (new SharedFontInternal (typeface))
  227. {
  228. }
  229. Font::Font (const Font& other) noexcept
  230. : font (other.font)
  231. {
  232. }
  233. Font& Font::operator= (const Font& other) noexcept
  234. {
  235. font = other.font;
  236. return *this;
  237. }
  238. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  239. Font::Font (Font&& other) noexcept
  240. : font (static_cast <ReferenceCountedObjectPtr <SharedFontInternal>&&> (other.font))
  241. {
  242. }
  243. Font& Font::operator= (Font&& other) noexcept
  244. {
  245. font = static_cast <ReferenceCountedObjectPtr <SharedFontInternal>&&> (other.font);
  246. return *this;
  247. }
  248. #endif
  249. Font::~Font() noexcept
  250. {
  251. }
  252. bool Font::operator== (const Font& other) const noexcept
  253. {
  254. return font == other.font
  255. || *font == *other.font;
  256. }
  257. bool Font::operator!= (const Font& other) const noexcept
  258. {
  259. return ! operator== (other);
  260. }
  261. void Font::dupeInternalIfShared()
  262. {
  263. if (font->getReferenceCount() > 1)
  264. font = new SharedFontInternal (*font);
  265. }
  266. void Font::checkTypefaceSuitability()
  267. {
  268. if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this))
  269. font->typeface = nullptr;
  270. }
  271. //==============================================================================
  272. const String& Font::getDefaultSansSerifFontName()
  273. {
  274. static const String name ("<Sans-Serif>");
  275. return name;
  276. }
  277. const String& Font::getDefaultSerifFontName()
  278. {
  279. static const String name ("<Serif>");
  280. return name;
  281. }
  282. const String& Font::getDefaultMonospacedFontName()
  283. {
  284. static const String name ("<Monospaced>");
  285. return name;
  286. }
  287. const String& Font::getDefaultStyle()
  288. {
  289. static const String style ("<Regular>");
  290. return style;
  291. }
  292. const String& Font::getTypefaceName() const noexcept
  293. {
  294. return font->typefaceName;
  295. }
  296. void Font::setTypefaceName (const String& faceName)
  297. {
  298. if (faceName != font->typefaceName)
  299. {
  300. jassert (faceName.isNotEmpty());
  301. dupeInternalIfShared();
  302. font->typefaceName = faceName;
  303. font->typeface = nullptr;
  304. font->ascent = 0;
  305. }
  306. }
  307. const String& Font::getTypefaceStyle() const noexcept
  308. {
  309. return font->typefaceStyle;
  310. }
  311. void Font::setTypefaceStyle (const String& typefaceStyle)
  312. {
  313. if (typefaceStyle != font->typefaceStyle)
  314. {
  315. dupeInternalIfShared();
  316. font->typefaceStyle = typefaceStyle;
  317. font->typeface = nullptr;
  318. font->ascent = 0;
  319. }
  320. }
  321. Font Font::withTypefaceStyle (const String& newStyle) const
  322. {
  323. Font f (*this);
  324. f.setTypefaceStyle (newStyle);
  325. return f;
  326. }
  327. StringArray Font::getAvailableStyles() const
  328. {
  329. return findAllTypefaceStyles (getTypeface()->getName());
  330. }
  331. Typeface* Font::getTypeface() const
  332. {
  333. if (font->typeface == nullptr)
  334. font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this);
  335. return font->typeface;
  336. }
  337. //==============================================================================
  338. const String& Font::getFallbackFontName()
  339. {
  340. return FontValues::fallbackFont;
  341. }
  342. void Font::setFallbackFontName (const String& name)
  343. {
  344. FontValues::fallbackFont = name;
  345. #if JUCE_MAC || JUCE_IOS
  346. jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
  347. #endif
  348. }
  349. const String& Font::getFallbackFontStyle()
  350. {
  351. return FontValues::fallbackFontStyle;
  352. }
  353. void Font::setFallbackFontStyle (const String& style)
  354. {
  355. FontValues::fallbackFontStyle = style;
  356. #if JUCE_MAC || JUCE_IOS
  357. jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
  358. #endif
  359. }
  360. //==============================================================================
  361. float Font::getHeight() const noexcept
  362. {
  363. return font->height;
  364. }
  365. Font Font::withHeight (const float newHeight) const
  366. {
  367. Font f (*this);
  368. f.setHeight (newHeight);
  369. return f;
  370. }
  371. Font Font::withPointHeight (float heightInPoints) const
  372. {
  373. Font f (*this);
  374. f.setHeight (heightInPoints / getTypeface()->getHeightToPointsFactor());
  375. return f;
  376. }
  377. void Font::setHeight (float newHeight)
  378. {
  379. newHeight = FontValues::limitFontHeight (newHeight);
  380. if (font->height != newHeight)
  381. {
  382. dupeInternalIfShared();
  383. font->height = newHeight;
  384. checkTypefaceSuitability();
  385. }
  386. }
  387. void Font::setHeightWithoutChangingWidth (float newHeight)
  388. {
  389. newHeight = FontValues::limitFontHeight (newHeight);
  390. if (font->height != newHeight)
  391. {
  392. dupeInternalIfShared();
  393. font->horizontalScale *= (font->height / newHeight);
  394. font->height = newHeight;
  395. checkTypefaceSuitability();
  396. }
  397. }
  398. int Font::getStyleFlags() const noexcept
  399. {
  400. int styleFlags = font->underline ? underlined : plain;
  401. if (isBold()) styleFlags |= bold;
  402. if (isItalic()) styleFlags |= italic;
  403. return styleFlags;
  404. }
  405. Font Font::withStyle (const int newFlags) const
  406. {
  407. Font f (*this);
  408. f.setStyleFlags (newFlags);
  409. return f;
  410. }
  411. void Font::setStyleFlags (const int newFlags)
  412. {
  413. if (getStyleFlags() != newFlags)
  414. {
  415. dupeInternalIfShared();
  416. font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags);
  417. font->underline = (newFlags & underlined) != 0;
  418. font->typeface = nullptr;
  419. font->ascent = 0;
  420. }
  421. }
  422. void Font::setSizeAndStyle (float newHeight,
  423. const int newStyleFlags,
  424. const float newHorizontalScale,
  425. const float newKerningAmount)
  426. {
  427. newHeight = FontValues::limitFontHeight (newHeight);
  428. if (font->height != newHeight
  429. || font->horizontalScale != newHorizontalScale
  430. || font->kerning != newKerningAmount)
  431. {
  432. dupeInternalIfShared();
  433. font->height = newHeight;
  434. font->horizontalScale = newHorizontalScale;
  435. font->kerning = newKerningAmount;
  436. checkTypefaceSuitability();
  437. }
  438. setStyleFlags (newStyleFlags);
  439. }
  440. void Font::setSizeAndStyle (float newHeight,
  441. const String& newStyle,
  442. const float newHorizontalScale,
  443. const float newKerningAmount)
  444. {
  445. newHeight = FontValues::limitFontHeight (newHeight);
  446. if (font->height != newHeight
  447. || font->horizontalScale != newHorizontalScale
  448. || font->kerning != newKerningAmount)
  449. {
  450. dupeInternalIfShared();
  451. font->height = newHeight;
  452. font->horizontalScale = newHorizontalScale;
  453. font->kerning = newKerningAmount;
  454. checkTypefaceSuitability();
  455. }
  456. setTypefaceStyle (newStyle);
  457. }
  458. float Font::getHorizontalScale() const noexcept
  459. {
  460. return font->horizontalScale;
  461. }
  462. Font Font::withHorizontalScale (const float newHorizontalScale) const
  463. {
  464. Font f (*this);
  465. f.setHorizontalScale (newHorizontalScale);
  466. return f;
  467. }
  468. void Font::setHorizontalScale (const float scaleFactor)
  469. {
  470. dupeInternalIfShared();
  471. font->horizontalScale = scaleFactor;
  472. checkTypefaceSuitability();
  473. }
  474. float Font::getExtraKerningFactor() const noexcept
  475. {
  476. return font->kerning;
  477. }
  478. Font Font::withExtraKerningFactor (const float extraKerning) const
  479. {
  480. Font f (*this);
  481. f.setExtraKerningFactor (extraKerning);
  482. return f;
  483. }
  484. void Font::setExtraKerningFactor (const float extraKerning)
  485. {
  486. dupeInternalIfShared();
  487. font->kerning = extraKerning;
  488. checkTypefaceSuitability();
  489. }
  490. Font Font::boldened() const { return withStyle (getStyleFlags() | bold); }
  491. Font Font::italicised() const { return withStyle (getStyleFlags() | italic); }
  492. bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->typefaceStyle); }
  493. bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->typefaceStyle); }
  494. void Font::setBold (const bool shouldBeBold)
  495. {
  496. const int flags = getStyleFlags();
  497. setStyleFlags (shouldBeBold ? (flags | bold)
  498. : (flags & ~bold));
  499. }
  500. void Font::setItalic (const bool shouldBeItalic)
  501. {
  502. const int flags = getStyleFlags();
  503. setStyleFlags (shouldBeItalic ? (flags | italic)
  504. : (flags & ~italic));
  505. }
  506. void Font::setUnderline (const bool shouldBeUnderlined)
  507. {
  508. dupeInternalIfShared();
  509. font->underline = shouldBeUnderlined;
  510. checkTypefaceSuitability();
  511. }
  512. bool Font::isUnderlined() const noexcept
  513. {
  514. return font->underline;
  515. }
  516. float Font::getAscent() const
  517. {
  518. if (font->ascent == 0)
  519. font->ascent = getTypeface()->getAscent();
  520. return font->height * font->ascent;
  521. }
  522. float Font::getDescent() const
  523. {
  524. return font->height - getAscent();
  525. }
  526. int Font::getStringWidth (const String& text) const
  527. {
  528. return roundToInt (getStringWidthFloat (text));
  529. }
  530. float Font::getStringWidthFloat (const String& text) const
  531. {
  532. float w = getTypeface()->getStringWidth (text);
  533. if (font->kerning != 0)
  534. w += font->kerning * text.length();
  535. return w * font->height * font->horizontalScale;
  536. }
  537. void Font::getGlyphPositions (const String& text, Array <int>& glyphs, Array <float>& xOffsets) const
  538. {
  539. getTypeface()->getGlyphPositions (text, glyphs, xOffsets);
  540. const int num = xOffsets.size();
  541. if (num > 0)
  542. {
  543. const float scale = font->height * font->horizontalScale;
  544. float* const x = xOffsets.getRawDataPointer();
  545. if (font->kerning != 0)
  546. {
  547. for (int i = 0; i < num; ++i)
  548. x[i] = (x[i] + i * font->kerning) * scale;
  549. }
  550. else
  551. {
  552. for (int i = 0; i < num; ++i)
  553. x[i] *= scale;
  554. }
  555. }
  556. }
  557. void Font::findFonts (Array<Font>& destArray)
  558. {
  559. const StringArray names (findAllTypefaceNames());
  560. for (int i = 0; i < names.size(); ++i)
  561. {
  562. const StringArray styles (findAllTypefaceStyles (names[i]));
  563. String style ("Regular");
  564. if (! styles.contains (style, true))
  565. style = styles[0];
  566. destArray.add (Font (names[i], style, FontValues::defaultFontHeight));
  567. }
  568. }
  569. //==============================================================================
  570. String Font::toString() const
  571. {
  572. String s;
  573. if (getTypefaceName() != getDefaultSansSerifFontName())
  574. s << getTypefaceName() << "; ";
  575. s << String (getHeight(), 1);
  576. if (getTypefaceStyle() != getDefaultStyle())
  577. s << ' ' << getTypefaceStyle();
  578. return s;
  579. }
  580. Font Font::fromString (const String& fontDescription)
  581. {
  582. const int separator = fontDescription.indexOfChar (';');
  583. String name;
  584. if (separator > 0)
  585. name = fontDescription.substring (0, separator).trim();
  586. if (name.isEmpty())
  587. name = getDefaultSansSerifFontName();
  588. String sizeAndStyle (fontDescription.substring (separator + 1).trimStart());
  589. float height = sizeAndStyle.getFloatValue();
  590. if (height <= 0)
  591. height = 10.0f;
  592. const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false));
  593. return Font (name, style, height);
  594. }