gfxFT2Fonts.cpp (8051B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #if defined(MOZ_WIDGET_GTK) 7 # include "gfxPlatformGtk.h" 8 # define gfxToolkitPlatform gfxPlatformGtk 9 #elif defined(XP_WIN) 10 # include "gfxWindowsPlatform.h" 11 # define gfxToolkitPlatform gfxWindowsPlatform 12 #elif defined(ANDROID) 13 # include "gfxAndroidPlatform.h" 14 # define gfxToolkitPlatform gfxAndroidPlatform 15 #endif 16 17 #include "gfxTypes.h" 18 #include "gfxFT2Fonts.h" 19 #include "gfxFT2FontBase.h" 20 #include "gfxFT2Utils.h" 21 #include "gfxFT2FontList.h" 22 #include "gfxTextRun.h" 23 #include <locale.h> 24 #include "nsGkAtoms.h" 25 #include "nsTArray.h" 26 #include "nsCRT.h" 27 #include "nsXULAppAPI.h" 28 29 #include "mozilla/Logging.h" 30 #include "prinit.h" 31 32 #include "mozilla/MemoryReporting.h" 33 #include "mozilla/Preferences.h" 34 #include "mozilla/gfx/2D.h" 35 36 using namespace mozilla; 37 using namespace mozilla::gfx; 38 39 /** 40 * gfxFT2Font 41 */ 42 43 bool gfxFT2Font::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText, 44 uint32_t aOffset, uint32_t aLength, Script aScript, 45 nsAtom* aLanguage, bool aVertical, 46 RoundingFlags aRounding, 47 gfxShapedText* aShapedText) { 48 if (!gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript, 49 aLanguage, aVertical, aRounding, aShapedText)) { 50 // harfbuzz must have failed(?!), just render raw glyphs 51 AddRange(aText, aOffset, aLength, aShapedText); 52 PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical, 53 aShapedText); 54 } 55 56 return true; 57 } 58 59 void gfxFT2Font::AddRange(const char16_t* aText, uint32_t aOffset, 60 uint32_t aLength, gfxShapedText* aShapedText) { 61 typedef gfxShapedText::CompressedGlyph CompressedGlyph; 62 63 const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit(); 64 // we'll pass this in/figure it out dynamically, but at this point there can 65 // be only one face. 66 gfxFT2LockedFace faceLock(this); 67 FT_Face face = faceLock.get(); 68 69 CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs(); 70 71 const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr; 72 73 FT_UInt spaceGlyph = GetSpaceGlyph(); 74 75 for (uint32_t i = 0; i < aLength; i++, aOffset++) { 76 char16_t ch = aText[i]; 77 78 if (ch == 0) { 79 // treat this null byte as a missing glyph, don't create a glyph for it 80 aShapedText->SetMissingGlyph(aOffset, 0, this); 81 continue; 82 } 83 84 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(ch), "Invalid char detected"); 85 86 if (cgdNext) { 87 cgd = cgdNext; 88 cgdNext = nullptr; 89 } else { 90 cgd = GetGlyphDataForChar(face, ch); 91 } 92 93 FT_UInt gid = cgd->glyphIndex; 94 int32_t advance = 0; 95 96 if (gid == 0) { 97 advance = -1; // trigger the missing glyphs case below 98 } else { 99 // find next character and its glyph -- in case they exist 100 // and exist in the current font face -- to compute kerning 101 char16_t chNext = 0; 102 FT_UInt gidNext = 0; 103 FT_Pos lsbDeltaNext = 0; 104 105 if (FT_HAS_KERNING(face) && i + 1 < aLength) { 106 chNext = aText[i + 1]; 107 if (chNext != 0) { 108 cgdNext = GetGlyphDataForChar(face, chNext); 109 gidNext = cgdNext->glyphIndex; 110 if (gidNext && gidNext != spaceGlyph) 111 lsbDeltaNext = cgdNext->lsbDelta; 112 } 113 } 114 115 advance = cgd->xAdvance; 116 117 // now add kerning to the current glyph's advance 118 if (chNext && gidNext) { 119 FT_Vector kerning; 120 kerning.x = 0; 121 FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning); 122 advance += kerning.x; 123 if (cgd->rsbDelta - lsbDeltaNext >= 32) { 124 advance -= 64; 125 } else if (cgd->rsbDelta - lsbDeltaNext < -32) { 126 advance += 64; 127 } 128 } 129 130 // convert 26.6 fixed point to app units 131 // round rather than truncate to nearest pixel 132 // because these advances are often scaled 133 advance = ((advance * appUnitsPerDevUnit + 32) >> 6); 134 } 135 136 if (advance >= 0 && CompressedGlyph::IsSimpleAdvance(advance) && 137 CompressedGlyph::IsSimpleGlyphID(gid)) { 138 charGlyphs[aOffset].SetSimpleGlyph(advance, gid); 139 } else if (gid == 0) { 140 // gid = 0 only happens when the glyph is missing from the font 141 aShapedText->SetMissingGlyph(aOffset, ch, this); 142 } else { 143 gfxTextRun::DetailedGlyph details; 144 details.mGlyphID = gid; 145 NS_ASSERTION(details.mGlyphID == gid, 146 "Seriously weird glyph ID detected!"); 147 details.mAdvance = advance; 148 aShapedText->SetDetailedGlyphs(aOffset, 1, &details); 149 } 150 } 151 } 152 153 gfxFT2Font::gfxFT2Font(const RefPtr<UnscaledFontFreeType>& aUnscaledFont, 154 RefPtr<mozilla::gfx::SharedFTFace>&& aFTFace, 155 FT2FontEntry* aFontEntry, const gfxFontStyle* aFontStyle, 156 int aLoadFlags) 157 : gfxFT2FontBase(aUnscaledFont, std::move(aFTFace), aFontEntry, aFontStyle, 158 aLoadFlags, aFontStyle->NeedsSyntheticBold(aFontEntry)), 159 mCharGlyphCache(32) { 160 NS_ASSERTION(mFontEntry, 161 "Unable to find font entry for font. Something is whack."); 162 InitMetrics(); 163 } 164 165 gfxFT2Font::~gfxFT2Font() {} 166 167 already_AddRefed<ScaledFont> gfxFT2Font::GetScaledFont( 168 const TextRunDrawParams& aRunParams) { 169 if (ScaledFont* scaledFont = mAzureScaledFont) { 170 return do_AddRef(scaledFont); 171 } 172 173 RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForFreeTypeFont( 174 GetUnscaledFont(), GetAdjustedSize(), mFTFace, 175 GetStyle()->NeedsSyntheticBold(GetFontEntry())); 176 if (!newScaledFont) { 177 return nullptr; 178 } 179 180 InitializeScaledFont(newScaledFont); 181 182 if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) { 183 newScaledFont.forget().leak(); 184 } 185 ScaledFont* scaledFont = mAzureScaledFont; 186 return do_AddRef(scaledFont); 187 } 188 189 bool gfxFT2Font::ShouldHintMetrics() const { 190 return !gfxPlatform::GetPlatform()->RequiresLinearZoom(); 191 } 192 193 void gfxFT2Font::FillGlyphDataForChar(FT_Face face, uint32_t ch, 194 CachedGlyphData* gd) { 195 if (!face->charmap || (face->charmap->encoding != FT_ENCODING_UNICODE && 196 face->charmap->encoding != FT_ENCODING_MS_SYMBOL)) { 197 if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE) && 198 FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_MS_SYMBOL)) { 199 NS_WARNING("failed to select Unicode or symbol charmap!"); 200 } 201 } 202 FT_UInt gid = FT_Get_Char_Index(face, ch); 203 204 if (gid == 0) { 205 // this font doesn't support this char! 206 NS_ASSERTION(gid != 0, 207 "We don't have a glyph, but font indicated that it supported " 208 "this char in tables?"); 209 gd->glyphIndex = 0; 210 return; 211 } 212 213 FT_Error err = Factory::LoadFTGlyph(face, gid, mFTLoadFlags); 214 215 if (err) { 216 // hmm, this is weird, we failed to load a glyph that we had? 217 NS_WARNING("Failed to load glyph that we got from Get_Char_index"); 218 219 gd->glyphIndex = 0; 220 return; 221 } 222 223 gd->glyphIndex = gid; 224 gd->lsbDelta = face->glyph->lsb_delta; 225 gd->rsbDelta = face->glyph->rsb_delta; 226 gd->xAdvance = face->glyph->advance.x; 227 if (gd->xAdvance) { 228 gd->xAdvance += GetEmboldenStrength(face).x; 229 } 230 } 231 232 void gfxFT2Font::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 233 FontCacheSizes* aSizes) const { 234 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 235 aSizes->mFontInstances += 236 mCharGlyphCache.ShallowSizeOfExcludingThis(aMallocSizeOf); 237 } 238 239 void gfxFT2Font::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 240 FontCacheSizes* aSizes) const { 241 aSizes->mFontInstances += aMallocSizeOf(this); 242 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 243 }