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.

533 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. // (This file gets included by juce_mac_NativeCode.mm, rather than being
  24. // compiled on its own).
  25. #ifdef JUCE_INCLUDED_FILE
  26. //==============================================================================
  27. static OSStatus pascal CubicMoveTo (const Float32Point *pt,
  28. void* callBackDataPtr)
  29. {
  30. Path* const p = (Path*) callBackDataPtr;
  31. p->startNewSubPath (pt->x, pt->y);
  32. return noErr;
  33. }
  34. static OSStatus pascal CubicLineTo (const Float32Point *pt,
  35. void* callBackDataPtr)
  36. {
  37. Path* const p = (Path*) callBackDataPtr;
  38. p->lineTo (pt->x, pt->y);
  39. return noErr;
  40. }
  41. static OSStatus pascal CubicCurveTo (const Float32Point *pt1,
  42. const Float32Point *pt2,
  43. const Float32Point *pt3,
  44. void* callBackDataPtr)
  45. {
  46. Path* const p = (Path*) callBackDataPtr;
  47. p->cubicTo (pt1->x, pt1->y,
  48. pt2->x, pt2->y,
  49. pt3->x, pt3->y);
  50. return noErr;
  51. }
  52. static OSStatus pascal CubicClosePath (void* callBackDataPtr)
  53. {
  54. Path* const p = (Path*) callBackDataPtr;
  55. p->closeSubPath();
  56. return noErr;
  57. }
  58. //==============================================================================
  59. class ATSFontHelper
  60. {
  61. ATSUFontID fontId;
  62. ATSUStyle style;
  63. ATSCubicMoveToUPP moveToProc;
  64. ATSCubicLineToUPP lineToProc;
  65. ATSCubicCurveToUPP curveToProc;
  66. ATSCubicClosePathUPP closePathProc;
  67. float totalSize, ascent;
  68. TextToUnicodeInfo encodingInfo;
  69. public:
  70. String name;
  71. bool isBold, isItalic;
  72. float fontSize;
  73. int refCount;
  74. ATSFontHelper (const String& name_,
  75. const bool bold_,
  76. const bool italic_,
  77. const float size_)
  78. : fontId (0),
  79. name (name_),
  80. isBold (bold_),
  81. isItalic (italic_),
  82. fontSize (size_),
  83. refCount (1)
  84. {
  85. const char* const nameUtf8 = name_.toUTF8();
  86. ATSUFindFontFromName (const_cast <char*> (nameUtf8),
  87. strlen (nameUtf8),
  88. kFontFullName,
  89. kFontNoPlatformCode,
  90. kFontNoScriptCode,
  91. kFontNoLanguageCode,
  92. &fontId);
  93. ATSUCreateStyle (&style);
  94. ATSUAttributeTag attTypes[] = { kATSUFontTag,
  95. kATSUQDBoldfaceTag,
  96. kATSUQDItalicTag,
  97. kATSUSizeTag };
  98. ByteCount attSizes[] = { sizeof (ATSUFontID),
  99. sizeof (Boolean),
  100. sizeof (Boolean),
  101. sizeof (Fixed) };
  102. Boolean bold = bold_, italic = italic_;
  103. Fixed size = X2Fix (size_);
  104. ATSUAttributeValuePtr attValues[] = { &fontId,
  105. &bold,
  106. &italic,
  107. &size };
  108. ATSUSetAttributes (style, 4, attTypes, attSizes, attValues);
  109. moveToProc = NewATSCubicMoveToUPP (CubicMoveTo);
  110. lineToProc = NewATSCubicLineToUPP (CubicLineTo);
  111. curveToProc = NewATSCubicCurveToUPP (CubicCurveTo);
  112. closePathProc = NewATSCubicClosePathUPP (CubicClosePath);
  113. ascent = 0.0f;
  114. float kern, descent = 0.0f;
  115. getPathAndKerning (T('N'), T('O'), 0, kern, &ascent, &descent);
  116. totalSize = ascent + descent;
  117. }
  118. ~ATSFontHelper()
  119. {
  120. ATSUDisposeStyle (style);
  121. DisposeATSCubicMoveToUPP (moveToProc);
  122. DisposeATSCubicLineToUPP (lineToProc);
  123. DisposeATSCubicCurveToUPP (curveToProc);
  124. DisposeATSCubicClosePathUPP (closePathProc);
  125. }
  126. bool getPathAndKerning (const juce_wchar char1,
  127. const juce_wchar char2,
  128. Path* path,
  129. float& kerning,
  130. float* ascent,
  131. float* descent)
  132. {
  133. bool ok = false;
  134. UniChar buffer[4];
  135. buffer[0] = T(' ');
  136. buffer[1] = char1;
  137. buffer[2] = char2;
  138. buffer[3] = 0;
  139. UniCharCount count = kATSUToTextEnd;
  140. ATSUTextLayout layout;
  141. OSStatus err = ATSUCreateTextLayoutWithTextPtr (buffer,
  142. 0,
  143. 2,
  144. 2,
  145. 1,
  146. &count,
  147. &style,
  148. &layout);
  149. if (err == noErr)
  150. {
  151. ATSUSetTransientFontMatching (layout, true);
  152. ATSLayoutRecord* layoutRecords;
  153. ItemCount numRecords;
  154. Fixed* deltaYs;
  155. ItemCount numDeltaYs;
  156. ATSUDirectGetLayoutDataArrayPtrFromTextLayout (layout,
  157. 0,
  158. kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
  159. (void**) &layoutRecords,
  160. &numRecords);
  161. ATSUDirectGetLayoutDataArrayPtrFromTextLayout (layout,
  162. 0,
  163. kATSUDirectDataBaselineDeltaFixedArray,
  164. (void**) &deltaYs,
  165. &numDeltaYs);
  166. if (numRecords > 2)
  167. {
  168. kerning = (float) (Fix2X (layoutRecords[2].realPos)
  169. - Fix2X (layoutRecords[1].realPos));
  170. if (ascent != 0)
  171. {
  172. ATSUTextMeasurement asc;
  173. ByteCount actualSize;
  174. ATSUGetLineControl (layout,
  175. 0,
  176. kATSULineAscentTag,
  177. sizeof (ATSUTextMeasurement),
  178. &asc,
  179. &actualSize);
  180. *ascent = (float) Fix2X (asc);
  181. }
  182. if (descent != 0)
  183. {
  184. ATSUTextMeasurement desc;
  185. ByteCount actualSize;
  186. ATSUGetLineControl (layout,
  187. 0,
  188. kATSULineDescentTag,
  189. sizeof (ATSUTextMeasurement),
  190. &desc,
  191. &actualSize);
  192. *descent = (float) Fix2X (desc);
  193. }
  194. if (path != 0)
  195. {
  196. OSStatus callbackResult;
  197. ok = (ATSUGlyphGetCubicPaths (style,
  198. layoutRecords[1].glyphID,
  199. moveToProc,
  200. lineToProc,
  201. curveToProc,
  202. closePathProc,
  203. (void*) path,
  204. &callbackResult) == noErr);
  205. if (numDeltaYs > 0 && ok)
  206. {
  207. const float dy = (float) Fix2X (deltaYs[1]);
  208. path->applyTransform (AffineTransform::translation (0.0f, dy));
  209. }
  210. }
  211. else
  212. {
  213. ok = true;
  214. }
  215. }
  216. if (deltaYs != 0)
  217. ATSUDirectReleaseLayoutDataArrayPtr (0, kATSUDirectDataBaselineDeltaFixedArray,
  218. (void**) &deltaYs);
  219. if (layoutRecords != 0)
  220. ATSUDirectReleaseLayoutDataArrayPtr (0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
  221. (void**) &layoutRecords);
  222. ATSUDisposeTextLayout (layout);
  223. }
  224. return kerning;
  225. }
  226. float getAscent()
  227. {
  228. return ascent;
  229. }
  230. float getTotalHeight()
  231. {
  232. return totalSize;
  233. }
  234. juce_wchar getDefaultChar()
  235. {
  236. return 0;
  237. }
  238. };
  239. //==============================================================================
  240. class ATSFontHelperCache : public Timer,
  241. public DeletedAtShutdown
  242. {
  243. VoidArray cache;
  244. public:
  245. ATSFontHelperCache()
  246. {
  247. }
  248. ~ATSFontHelperCache()
  249. {
  250. for (int i = cache.size(); --i >= 0;)
  251. {
  252. ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i);
  253. delete f;
  254. }
  255. clearSingletonInstance();
  256. }
  257. ATSFontHelper* getFont (const String& name,
  258. const bool bold,
  259. const bool italic,
  260. const float size = 1024)
  261. {
  262. for (int i = cache.size(); --i >= 0;)
  263. {
  264. ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i);
  265. if (f->name == name
  266. && f->isBold == bold
  267. && f->isItalic == italic
  268. && f->fontSize == size)
  269. {
  270. f->refCount++;
  271. return f;
  272. }
  273. }
  274. ATSFontHelper* const f = new ATSFontHelper (name, bold, italic, size);
  275. cache.add (f);
  276. return f;
  277. }
  278. void releaseFont (ATSFontHelper* f)
  279. {
  280. for (int i = cache.size(); --i >= 0;)
  281. {
  282. ATSFontHelper* const f2 = (ATSFontHelper*) cache.getUnchecked(i);
  283. if (f == f2)
  284. {
  285. f->refCount--;
  286. if (f->refCount == 0)
  287. startTimer (5000);
  288. break;
  289. }
  290. }
  291. }
  292. void timerCallback()
  293. {
  294. stopTimer();
  295. for (int i = cache.size(); --i >= 0;)
  296. {
  297. ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i);
  298. if (f->refCount == 0)
  299. {
  300. cache.remove (i);
  301. delete f;
  302. }
  303. }
  304. if (cache.size() == 0)
  305. delete this;
  306. }
  307. juce_DeclareSingleton_SingleThreaded_Minimal (ATSFontHelperCache)
  308. };
  309. juce_ImplementSingleton_SingleThreaded (ATSFontHelperCache)
  310. //==============================================================================
  311. void Typeface::initialiseTypefaceCharacteristics (const String& fontName,
  312. bool bold,
  313. bool italic,
  314. bool addAllGlyphsToFont) throw()
  315. {
  316. // This method is only safe to be called from the normal UI thread..
  317. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  318. ATSFontHelper* const helper = ATSFontHelperCache::getInstance()
  319. ->getFont (fontName, bold, italic);
  320. clear();
  321. setAscent (helper->getAscent() / helper->getTotalHeight());
  322. setName (fontName);
  323. setDefaultCharacter (helper->getDefaultChar());
  324. setBold (bold);
  325. setItalic (italic);
  326. if (addAllGlyphsToFont)
  327. {
  328. //xxx
  329. jassertfalse
  330. }
  331. ATSFontHelperCache::getInstance()->releaseFont (helper);
  332. }
  333. bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw()
  334. {
  335. // This method is only safe to be called from the normal UI thread..
  336. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  337. ATSFontHelper* const helper = ATSFontHelperCache::getInstance()
  338. ->getFont (getName(), isBold(), isItalic());
  339. Path path;
  340. float width;
  341. bool foundOne = false;
  342. if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0))
  343. {
  344. path.applyTransform (AffineTransform::scale (1.0f / helper->getTotalHeight(),
  345. 1.0f / helper->getTotalHeight()));
  346. addGlyph (character, path, width / helper->getTotalHeight());
  347. for (int i = 0; i < glyphs.size(); ++i)
  348. {
  349. const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i);
  350. float kerning;
  351. if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0))
  352. {
  353. kerning = (kerning - width) / helper->getTotalHeight();
  354. if (kerning != 0)
  355. addKerningPair (character, g->getCharacter(), kerning);
  356. }
  357. if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0))
  358. {
  359. kerning = kerning / helper->getTotalHeight() - g->width;
  360. if (kerning != 0)
  361. addKerningPair (g->getCharacter(), character, kerning);
  362. }
  363. }
  364. foundOne = true;
  365. }
  366. ATSFontHelperCache::getInstance()->releaseFont (helper);
  367. return foundOne;
  368. }
  369. const StringArray Font::findAllTypefaceNames() throw()
  370. {
  371. StringArray names;
  372. ATSFontIterator iter;
  373. if (ATSFontIteratorCreate (kATSFontContextGlobal,
  374. 0,
  375. 0,
  376. kATSOptionFlagsRestrictedScope,
  377. &iter) == noErr)
  378. {
  379. ATSFontRef font;
  380. while (ATSFontIteratorNext (iter, &font) == noErr)
  381. {
  382. CFStringRef name;
  383. if (ATSFontGetName (font,
  384. kATSOptionFlagsDefault,
  385. &name) == noErr)
  386. {
  387. const String nm (PlatformUtilities::cfStringToJuceString (name));
  388. if (nm.isNotEmpty())
  389. names.add (nm);
  390. CFRelease (name);
  391. }
  392. }
  393. ATSFontIteratorRelease (&iter);
  394. }
  395. // Use some totuous logic to eliminate bold/italic versions of fonts that we've already got
  396. // a plain version of. This is only necessary because of Carbon's total lack of support
  397. // for dealing with font families...
  398. for (int j = names.size(); --j >= 0;)
  399. {
  400. const char* const endings[] = { " bold", " italic", " bold italic", " bolditalic",
  401. " oblque", " bold oblique", " boldoblique" };
  402. for (int i = 0; i < numElementsInArray (endings); ++i)
  403. {
  404. const String ending (endings[i]);
  405. if (names[j].endsWithIgnoreCase (ending))
  406. {
  407. const String root (names[j].dropLastCharacters (ending.length()).trimEnd());
  408. if (names.contains (root)
  409. || names.contains (root + T(" plain"), true))
  410. {
  411. names.remove (j);
  412. break;
  413. }
  414. }
  415. }
  416. }
  417. names.sort (true);
  418. return names;
  419. }
  420. void Font::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw()
  421. {
  422. defaultSans = "Lucida Grande";
  423. defaultSerif = "Times New Roman";
  424. defaultFixed = "Monaco";
  425. }
  426. #endif