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.

403 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. struct DefaultFontNames
  18. {
  19. DefaultFontNames()
  20. : defaultSans ("sans"),
  21. defaultSerif ("serif"),
  22. defaultFixed ("monospace"),
  23. defaultFallback ("sans")
  24. {
  25. }
  26. String getRealFontName (const String& faceName) const
  27. {
  28. if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans;
  29. if (faceName == Font::getDefaultSerifFontName()) return defaultSerif;
  30. if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed;
  31. return faceName;
  32. }
  33. String defaultSans, defaultSerif, defaultFixed, defaultFallback;
  34. };
  35. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  36. {
  37. static DefaultFontNames defaultNames;
  38. Font f (font);
  39. f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName()));
  40. return Typeface::createSystemTypefaceFor (f);
  41. }
  42. //==============================================================================
  43. #if JUCE_USE_FREETYPE
  44. StringArray FTTypefaceList::getDefaultFontDirectories()
  45. {
  46. return StringArray ("/system/fonts");
  47. }
  48. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  49. {
  50. return new FreeTypeTypeface (font);
  51. }
  52. void Typeface::scanFolderForFonts (const File& folder)
  53. {
  54. FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName()));
  55. }
  56. StringArray Font::findAllTypefaceNames()
  57. {
  58. return FTTypefaceList::getInstance()->findAllFamilyNames();
  59. }
  60. StringArray Font::findAllTypefaceStyles (const String& family)
  61. {
  62. return FTTypefaceList::getInstance()->findAllTypefaceStyles (family);
  63. }
  64. bool TextLayout::createNativeLayout (const AttributedString&)
  65. {
  66. return false;
  67. }
  68. #else
  69. //==============================================================================
  70. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  71. STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \
  72. STATICMETHOD (createFromFile, "createFromFile", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
  73. DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface");
  74. #undef JNI_CLASS_MEMBERS
  75. //==============================================================================
  76. StringArray Font::findAllTypefaceNames()
  77. {
  78. StringArray results;
  79. Array<File> fonts;
  80. File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, "*.ttf");
  81. for (int i = 0; i < fonts.size(); ++i)
  82. results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension()
  83. .upToLastOccurrenceOf ("-", false, false));
  84. return results;
  85. }
  86. StringArray Font::findAllTypefaceStyles (const String& family)
  87. {
  88. StringArray results ("Regular");
  89. Array<File> fonts;
  90. File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, family + "-*.ttf");
  91. for (int i = 0; i < fonts.size(); ++i)
  92. results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension()
  93. .fromLastOccurrenceOf ("-", false, false));
  94. return results;
  95. }
  96. const float referenceFontSize = 256.0f;
  97. const float referenceFontToUnits = 1.0f / referenceFontSize;
  98. //==============================================================================
  99. class AndroidTypeface : public Typeface
  100. {
  101. public:
  102. AndroidTypeface (const Font& font)
  103. : Typeface (font.getTypefaceName(), font.getTypefaceStyle()),
  104. ascent (0), descent (0), heightToPointsFactor (1.0f)
  105. {
  106. JNIEnv* const env = getEnv();
  107. // First check whether there's an embedded asset with this font name:
  108. typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromAsset,
  109. javaString ("fonts/" + name).get()));
  110. if (typeface.get() == nullptr)
  111. {
  112. const bool isBold = style.contains ("Bold");
  113. const bool isItalic = style.contains ("Italic");
  114. File fontFile (getFontFile (name, style));
  115. if (! fontFile.exists())
  116. fontFile = findFontFile (name, isBold, isItalic);
  117. if (fontFile.exists())
  118. typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
  119. javaString (fontFile.getFullPathName()).get()));
  120. else
  121. typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create,
  122. javaString (getName()).get(),
  123. (isBold ? 1 : 0) + (isItalic ? 2 : 0)));
  124. }
  125. initialise (env);
  126. }
  127. AndroidTypeface (const void* data, size_t size)
  128. : Typeface (String (static_cast<uint64> (reinterpret_cast<uintptr_t> (data))), String())
  129. {
  130. JNIEnv* const env = getEnv();
  131. LocalRef<jbyteArray> bytes (env->NewByteArray ((jsize) size));
  132. env->SetByteArrayRegion (bytes, 0, (jsize) size, (const jbyte*) data);
  133. typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromByteArray, bytes.get()));
  134. initialise (env);
  135. }
  136. void initialise (JNIEnv* const env)
  137. {
  138. rect = GlobalRef (env->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0));
  139. paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality));
  140. const LocalRef<jobject> ignored (paint.callObjectMethod (Paint.setTypeface, typeface.get()));
  141. paint.callVoidMethod (Paint.setTextSize, referenceFontSize);
  142. const float fullAscent = std::abs (paint.callFloatMethod (Paint.ascent));
  143. const float fullDescent = paint.callFloatMethod (Paint.descent);
  144. const float totalHeight = fullAscent + fullDescent;
  145. ascent = fullAscent / totalHeight;
  146. descent = fullDescent / totalHeight;
  147. heightToPointsFactor = referenceFontSize / totalHeight;
  148. }
  149. float getAscent() const override { return ascent; }
  150. float getDescent() const override { return descent; }
  151. float getHeightToPointsFactor() const override { return heightToPointsFactor; }
  152. float getStringWidth (const String& text) override
  153. {
  154. JNIEnv* env = getEnv();
  155. const int numChars = text.length();
  156. jfloatArray widths = env->NewFloatArray (numChars);
  157. const int numDone = paint.callIntMethod (Paint.getTextWidths, javaString (text).get(), widths);
  158. HeapBlock<jfloat> localWidths (static_cast<size_t> (numDone));
  159. env->GetFloatArrayRegion (widths, 0, numDone, localWidths);
  160. env->DeleteLocalRef (widths);
  161. float x = 0;
  162. for (int i = 0; i < numDone; ++i)
  163. x += localWidths[i];
  164. return x * referenceFontToUnits;
  165. }
  166. void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) override
  167. {
  168. JNIEnv* env = getEnv();
  169. const int numChars = text.length();
  170. jfloatArray widths = env->NewFloatArray (numChars);
  171. const int numDone = paint.callIntMethod (Paint.getTextWidths, javaString (text).get(), widths);
  172. HeapBlock<jfloat> localWidths (static_cast<size_t> (numDone));
  173. env->GetFloatArrayRegion (widths, 0, numDone, localWidths);
  174. env->DeleteLocalRef (widths);
  175. String::CharPointerType s (text.getCharPointer());
  176. xOffsets.add (0);
  177. float x = 0;
  178. for (int i = 0; i < numDone; ++i)
  179. {
  180. const float local = localWidths[i];
  181. // Android uses jchar (UTF-16) characters
  182. jchar ch = (jchar) s.getAndAdvance();
  183. // Android has no proper glyph support, so we have to do
  184. // a hacky workaround for ligature detection
  185. #if JUCE_STRING_UTF_TYPE <= 16
  186. static_assert (sizeof (int) >= (sizeof (jchar) * 2), "Unable store two java chars in one glyph");
  187. // if the width of this glyph is zero inside the string but has
  188. // a width on it's own, then it's probably due to ligature
  189. if (local == 0.0f && glyphs.size() > 0 && getStringWidth (String (ch)) > 0.0f)
  190. {
  191. // modify the previous glyph
  192. int& glyphNumber = glyphs.getReference (glyphs.size() - 1);
  193. // make sure this is not a three character ligature
  194. if (glyphNumber < std::numeric_limits<jchar>::max())
  195. {
  196. const unsigned int previousGlyph
  197. = static_cast<unsigned int> (glyphNumber) & ((1U << (sizeof (jchar) * 8U)) - 1U);
  198. const unsigned int thisGlyph
  199. = static_cast<unsigned int> (ch) & ((1U << (sizeof (jchar) * 8U)) - 1U);
  200. glyphNumber = static_cast<int> ((thisGlyph << (sizeof (jchar) * 8U)) | previousGlyph);
  201. ch = 0;
  202. }
  203. }
  204. #endif
  205. glyphs.add ((int) ch);
  206. x += local;
  207. xOffsets.add (x * referenceFontToUnits);
  208. }
  209. }
  210. bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) override
  211. {
  212. return false;
  213. }
  214. EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t, float /*fontHeight*/) override
  215. {
  216. #if JUCE_STRING_UTF_TYPE <= 16
  217. static_assert (sizeof (int) >= (sizeof (jchar) * 2), "Unable store two jni chars in one int");
  218. // glyphNumber of zero is used to indicate that the last character was a ligature
  219. if (glyphNumber == 0) return nullptr;
  220. jchar ch1 = (static_cast<unsigned int> (glyphNumber) >> 0) & ((1U << (sizeof (jchar) * 8U)) - 1U);
  221. jchar ch2 = (static_cast<unsigned int> (glyphNumber) >> (sizeof (jchar) * 8U)) & ((1U << (sizeof (jchar) * 8U)) - 1U);
  222. #else
  223. jchar ch1 = glyphNumber, ch2 = 0;
  224. #endif
  225. JNIEnv* env = getEnv();
  226. jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t));
  227. jintArray maskData = (jintArray) android.activity.callObjectMethod (JuceAppActivity.renderGlyph, ch1, ch2, paint.get(), matrix, rect.get());
  228. env->DeleteLocalRef (matrix);
  229. const int left = env->GetIntField (rect.get(), RectClass.left);
  230. const int top = env->GetIntField (rect.get(), RectClass.top);
  231. const int right = env->GetIntField (rect.get(), RectClass.right);
  232. const int bottom = env->GetIntField (rect.get(), RectClass.bottom);
  233. const Rectangle<int> bounds (left, top, right - left, bottom - top);
  234. EdgeTable* et = nullptr;
  235. if (! bounds.isEmpty())
  236. {
  237. et = new EdgeTable (bounds);
  238. jint* const maskDataElements = env->GetIntArrayElements (maskData, 0);
  239. const jint* mask = maskDataElements;
  240. for (int y = top; y < bottom; ++y)
  241. {
  242. #if JUCE_LITTLE_ENDIAN
  243. const uint8* const lineBytes = ((const uint8*) mask) + 3;
  244. #else
  245. const uint8* const lineBytes = (const uint8*) mask;
  246. #endif
  247. et->clipLineToMask (left, y, lineBytes, 4, bounds.getWidth());
  248. mask += bounds.getWidth();
  249. }
  250. env->ReleaseIntArrayElements (maskData, maskDataElements, 0);
  251. }
  252. env->DeleteLocalRef (maskData);
  253. return et;
  254. }
  255. GlobalRef typeface, paint, rect;
  256. float ascent, descent, heightToPointsFactor;
  257. private:
  258. static File findFontFile (const String& family,
  259. const bool bold, const bool italic)
  260. {
  261. File file;
  262. if (bold || italic)
  263. {
  264. String suffix;
  265. if (bold) suffix = "Bold";
  266. if (italic) suffix << "Italic";
  267. file = getFontFile (family, suffix);
  268. if (file.exists())
  269. return file;
  270. }
  271. file = getFontFile (family, "Regular");
  272. if (! file.exists())
  273. file = getFontFile (family, String());
  274. return file;
  275. }
  276. static File getFontFile (const String& family, const String& style)
  277. {
  278. String path ("/system/fonts/" + family);
  279. if (style.isNotEmpty())
  280. path << '-' << style;
  281. return File (path + ".ttf");
  282. }
  283. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface)
  284. };
  285. //==============================================================================
  286. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  287. {
  288. return new AndroidTypeface (font);
  289. }
  290. Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size)
  291. {
  292. return new AndroidTypeface (data, size);
  293. }
  294. void Typeface::scanFolderForFonts (const File&)
  295. {
  296. jassertfalse; // not available unless using FreeType
  297. }
  298. bool TextLayout::createNativeLayout (const AttributedString&)
  299. {
  300. return false;
  301. }
  302. #endif