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.

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