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.

729 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. : ReferenceCountedObject(),
  180. typefaceName (other.typefaceName),
  181. typefaceStyle (other.typefaceStyle),
  182. height (other.height),
  183. horizontalScale (other.horizontalScale),
  184. kerning (other.kerning),
  185. ascent (other.ascent),
  186. underline (other.underline),
  187. typeface (other.typeface)
  188. {
  189. }
  190. bool operator== (const SharedFontInternal& other) const noexcept
  191. {
  192. return height == other.height
  193. && underline == other.underline
  194. && horizontalScale == other.horizontalScale
  195. && kerning == other.kerning
  196. && typefaceName == other.typefaceName
  197. && typefaceStyle == other.typefaceStyle;
  198. }
  199. String typefaceName, typefaceStyle;
  200. float height, horizontalScale, kerning, ascent;
  201. bool underline;
  202. Typeface::Ptr typeface;
  203. };
  204. //==============================================================================
  205. Font::Font()
  206. : font (new SharedFontInternal())
  207. {
  208. }
  209. Font::Font (const float fontHeight, const int styleFlags)
  210. : font (new SharedFontInternal (FontStyleHelpers::getStyleName (styleFlags),
  211. FontValues::limitFontHeight (fontHeight),
  212. (styleFlags & underlined) != 0))
  213. {
  214. }
  215. Font::Font (const String& typefaceName, const float fontHeight, const int styleFlags)
  216. : font (new SharedFontInternal (typefaceName,
  217. FontStyleHelpers::getStyleName (styleFlags),
  218. FontValues::limitFontHeight (fontHeight),
  219. (styleFlags & underlined) != 0))
  220. {
  221. }
  222. Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight)
  223. : font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight), false))
  224. {
  225. }
  226. Font::Font (const Typeface::Ptr& typeface)
  227. : font (new SharedFontInternal (typeface))
  228. {
  229. }
  230. Font::Font (const Font& other) noexcept
  231. : font (other.font)
  232. {
  233. }
  234. Font& Font::operator= (const Font& other) noexcept
  235. {
  236. font = other.font;
  237. return *this;
  238. }
  239. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  240. Font::Font (Font&& other) noexcept
  241. : font (static_cast <ReferenceCountedObjectPtr <SharedFontInternal>&&> (other.font))
  242. {
  243. }
  244. Font& Font::operator= (Font&& other) noexcept
  245. {
  246. font = static_cast <ReferenceCountedObjectPtr <SharedFontInternal>&&> (other.font);
  247. return *this;
  248. }
  249. #endif
  250. Font::~Font() noexcept
  251. {
  252. }
  253. bool Font::operator== (const Font& other) const noexcept
  254. {
  255. return font == other.font
  256. || *font == *other.font;
  257. }
  258. bool Font::operator!= (const Font& other) const noexcept
  259. {
  260. return ! operator== (other);
  261. }
  262. void Font::dupeInternalIfShared()
  263. {
  264. if (font->getReferenceCount() > 1)
  265. font = new SharedFontInternal (*font);
  266. }
  267. void Font::checkTypefaceSuitability()
  268. {
  269. if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this))
  270. font->typeface = nullptr;
  271. }
  272. //==============================================================================
  273. const String& Font::getDefaultSansSerifFontName()
  274. {
  275. static const String name ("<Sans-Serif>");
  276. return name;
  277. }
  278. const String& Font::getDefaultSerifFontName()
  279. {
  280. static const String name ("<Serif>");
  281. return name;
  282. }
  283. const String& Font::getDefaultMonospacedFontName()
  284. {
  285. static const String name ("<Monospaced>");
  286. return name;
  287. }
  288. const String& Font::getDefaultStyle()
  289. {
  290. static const String style ("<Regular>");
  291. return style;
  292. }
  293. const String& Font::getTypefaceName() const noexcept
  294. {
  295. return font->typefaceName;
  296. }
  297. void Font::setTypefaceName (const String& faceName)
  298. {
  299. if (faceName != font->typefaceName)
  300. {
  301. jassert (faceName.isNotEmpty());
  302. dupeInternalIfShared();
  303. font->typefaceName = faceName;
  304. font->typeface = nullptr;
  305. font->ascent = 0;
  306. }
  307. }
  308. const String& Font::getTypefaceStyle() const noexcept
  309. {
  310. return font->typefaceStyle;
  311. }
  312. void Font::setTypefaceStyle (const String& typefaceStyle)
  313. {
  314. if (typefaceStyle != font->typefaceStyle)
  315. {
  316. dupeInternalIfShared();
  317. font->typefaceStyle = typefaceStyle;
  318. font->typeface = nullptr;
  319. font->ascent = 0;
  320. }
  321. }
  322. Font Font::withTypefaceStyle (const String& newStyle) const
  323. {
  324. Font f (*this);
  325. f.setTypefaceStyle (newStyle);
  326. return f;
  327. }
  328. StringArray Font::getAvailableStyles() const
  329. {
  330. return findAllTypefaceStyles (getTypeface()->getName());
  331. }
  332. Typeface* Font::getTypeface() const
  333. {
  334. if (font->typeface == nullptr)
  335. font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this);
  336. return font->typeface;
  337. }
  338. //==============================================================================
  339. const String& Font::getFallbackFontName()
  340. {
  341. return FontValues::fallbackFont;
  342. }
  343. void Font::setFallbackFontName (const String& name)
  344. {
  345. FontValues::fallbackFont = name;
  346. #if JUCE_MAC || JUCE_IOS
  347. jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
  348. #endif
  349. }
  350. const String& Font::getFallbackFontStyle()
  351. {
  352. return FontValues::fallbackFontStyle;
  353. }
  354. void Font::setFallbackFontStyle (const String& style)
  355. {
  356. FontValues::fallbackFontStyle = style;
  357. #if JUCE_MAC || JUCE_IOS
  358. jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
  359. #endif
  360. }
  361. //==============================================================================
  362. float Font::getHeight() const noexcept
  363. {
  364. return font->height;
  365. }
  366. Font Font::withHeight (const float newHeight) const
  367. {
  368. Font f (*this);
  369. f.setHeight (newHeight);
  370. return f;
  371. }
  372. Font Font::withPointHeight (float heightInPoints) const
  373. {
  374. Font f (*this);
  375. f.setHeight (heightInPoints / getTypeface()->getHeightToPointsFactor());
  376. return f;
  377. }
  378. void Font::setHeight (float newHeight)
  379. {
  380. newHeight = FontValues::limitFontHeight (newHeight);
  381. if (font->height != newHeight)
  382. {
  383. dupeInternalIfShared();
  384. font->height = newHeight;
  385. checkTypefaceSuitability();
  386. }
  387. }
  388. void Font::setHeightWithoutChangingWidth (float newHeight)
  389. {
  390. newHeight = FontValues::limitFontHeight (newHeight);
  391. if (font->height != newHeight)
  392. {
  393. dupeInternalIfShared();
  394. font->horizontalScale *= (font->height / newHeight);
  395. font->height = newHeight;
  396. checkTypefaceSuitability();
  397. }
  398. }
  399. int Font::getStyleFlags() const noexcept
  400. {
  401. int styleFlags = font->underline ? underlined : plain;
  402. if (isBold()) styleFlags |= bold;
  403. if (isItalic()) styleFlags |= italic;
  404. return styleFlags;
  405. }
  406. Font Font::withStyle (const int newFlags) const
  407. {
  408. Font f (*this);
  409. f.setStyleFlags (newFlags);
  410. return f;
  411. }
  412. void Font::setStyleFlags (const int newFlags)
  413. {
  414. if (getStyleFlags() != newFlags)
  415. {
  416. dupeInternalIfShared();
  417. font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags);
  418. font->underline = (newFlags & underlined) != 0;
  419. font->typeface = nullptr;
  420. font->ascent = 0;
  421. }
  422. }
  423. void Font::setSizeAndStyle (float newHeight,
  424. const int newStyleFlags,
  425. const float newHorizontalScale,
  426. const float newKerningAmount)
  427. {
  428. newHeight = FontValues::limitFontHeight (newHeight);
  429. if (font->height != newHeight
  430. || font->horizontalScale != newHorizontalScale
  431. || font->kerning != newKerningAmount)
  432. {
  433. dupeInternalIfShared();
  434. font->height = newHeight;
  435. font->horizontalScale = newHorizontalScale;
  436. font->kerning = newKerningAmount;
  437. checkTypefaceSuitability();
  438. }
  439. setStyleFlags (newStyleFlags);
  440. }
  441. void Font::setSizeAndStyle (float newHeight,
  442. const String& newStyle,
  443. const float newHorizontalScale,
  444. const float newKerningAmount)
  445. {
  446. newHeight = FontValues::limitFontHeight (newHeight);
  447. if (font->height != newHeight
  448. || font->horizontalScale != newHorizontalScale
  449. || font->kerning != newKerningAmount)
  450. {
  451. dupeInternalIfShared();
  452. font->height = newHeight;
  453. font->horizontalScale = newHorizontalScale;
  454. font->kerning = newKerningAmount;
  455. checkTypefaceSuitability();
  456. }
  457. setTypefaceStyle (newStyle);
  458. }
  459. float Font::getHorizontalScale() const noexcept
  460. {
  461. return font->horizontalScale;
  462. }
  463. Font Font::withHorizontalScale (const float newHorizontalScale) const
  464. {
  465. Font f (*this);
  466. f.setHorizontalScale (newHorizontalScale);
  467. return f;
  468. }
  469. void Font::setHorizontalScale (const float scaleFactor)
  470. {
  471. dupeInternalIfShared();
  472. font->horizontalScale = scaleFactor;
  473. checkTypefaceSuitability();
  474. }
  475. float Font::getExtraKerningFactor() const noexcept
  476. {
  477. return font->kerning;
  478. }
  479. Font Font::withExtraKerningFactor (const float extraKerning) const
  480. {
  481. Font f (*this);
  482. f.setExtraKerningFactor (extraKerning);
  483. return f;
  484. }
  485. void Font::setExtraKerningFactor (const float extraKerning)
  486. {
  487. dupeInternalIfShared();
  488. font->kerning = extraKerning;
  489. checkTypefaceSuitability();
  490. }
  491. Font Font::boldened() const { return withStyle (getStyleFlags() | bold); }
  492. Font Font::italicised() const { return withStyle (getStyleFlags() | italic); }
  493. bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->typefaceStyle); }
  494. bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->typefaceStyle); }
  495. void Font::setBold (const bool shouldBeBold)
  496. {
  497. const int flags = getStyleFlags();
  498. setStyleFlags (shouldBeBold ? (flags | bold)
  499. : (flags & ~bold));
  500. }
  501. void Font::setItalic (const bool shouldBeItalic)
  502. {
  503. const int flags = getStyleFlags();
  504. setStyleFlags (shouldBeItalic ? (flags | italic)
  505. : (flags & ~italic));
  506. }
  507. void Font::setUnderline (const bool shouldBeUnderlined)
  508. {
  509. dupeInternalIfShared();
  510. font->underline = shouldBeUnderlined;
  511. checkTypefaceSuitability();
  512. }
  513. bool Font::isUnderlined() const noexcept
  514. {
  515. return font->underline;
  516. }
  517. float Font::getAscent() const
  518. {
  519. if (font->ascent == 0)
  520. font->ascent = getTypeface()->getAscent();
  521. return font->height * font->ascent;
  522. }
  523. float Font::getDescent() const
  524. {
  525. return font->height - getAscent();
  526. }
  527. int Font::getStringWidth (const String& text) const
  528. {
  529. return roundToInt (getStringWidthFloat (text));
  530. }
  531. float Font::getStringWidthFloat (const String& text) const
  532. {
  533. float w = getTypeface()->getStringWidth (text);
  534. if (font->kerning != 0)
  535. w += font->kerning * text.length();
  536. return w * font->height * font->horizontalScale;
  537. }
  538. void Font::getGlyphPositions (const String& text, Array <int>& glyphs, Array <float>& xOffsets) const
  539. {
  540. getTypeface()->getGlyphPositions (text, glyphs, xOffsets);
  541. const int num = xOffsets.size();
  542. if (num > 0)
  543. {
  544. const float scale = font->height * font->horizontalScale;
  545. float* const x = xOffsets.getRawDataPointer();
  546. if (font->kerning != 0)
  547. {
  548. for (int i = 0; i < num; ++i)
  549. x[i] = (x[i] + i * font->kerning) * scale;
  550. }
  551. else
  552. {
  553. for (int i = 0; i < num; ++i)
  554. x[i] *= scale;
  555. }
  556. }
  557. }
  558. void Font::findFonts (Array<Font>& destArray)
  559. {
  560. const StringArray names (findAllTypefaceNames());
  561. for (int i = 0; i < names.size(); ++i)
  562. {
  563. const StringArray styles (findAllTypefaceStyles (names[i]));
  564. String style ("Regular");
  565. if (! styles.contains (style, true))
  566. style = styles[0];
  567. destArray.add (Font (names[i], style, FontValues::defaultFontHeight));
  568. }
  569. }
  570. //==============================================================================
  571. String Font::toString() const
  572. {
  573. String s;
  574. if (getTypefaceName() != getDefaultSansSerifFontName())
  575. s << getTypefaceName() << "; ";
  576. s << String (getHeight(), 1);
  577. if (getTypefaceStyle() != getDefaultStyle())
  578. s << ' ' << getTypefaceStyle();
  579. return s;
  580. }
  581. Font Font::fromString (const String& fontDescription)
  582. {
  583. const int separator = fontDescription.indexOfChar (';');
  584. String name;
  585. if (separator > 0)
  586. name = fontDescription.substring (0, separator).trim();
  587. if (name.isEmpty())
  588. name = getDefaultSansSerifFontName();
  589. String sizeAndStyle (fontDescription.substring (separator + 1).trimStart());
  590. float height = sizeAndStyle.getFloatValue();
  591. if (height <= 0)
  592. height = 10.0f;
  593. const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false));
  594. return Font (name, style, height);
  595. }