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.

405 lines
14KB

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