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 
ShapeText(DrawTarget * aDrawTarget,const char16_t * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxShapedText * aShapedText)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 
AddRange(const char16_t * aText,uint32_t aOffset,uint32_t aLength,gfxShapedText * aShapedText)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 
gfxFT2Font(const RefPtr<UnscaledFontFreeType> & aUnscaledFont,RefPtr<mozilla::gfx::SharedFTFace> && aFTFace,FT2FontEntry * aFontEntry,const gfxFontStyle * aFontStyle,int aLoadFlags)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 
~gfxFT2Font()165 gfxFT2Font::~gfxFT2Font() {}
166 
GetScaledFont(DrawTarget * aTarget)167 already_AddRefed<ScaledFont> gfxFT2Font::GetScaledFont(DrawTarget* aTarget) {
168   if (!mAzureScaledFont) {
169     mAzureScaledFont = Factory::CreateScaledFontForFreeTypeFont(
170         GetUnscaledFont(), GetAdjustedSize(), mFTFace,
171         GetStyle()->NeedsSyntheticBold(GetFontEntry()));
172     InitializeScaledFont();
173   }
174 
175   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
176   return scaledFont.forget();
177 }
178 
ShouldHintMetrics() const179 bool gfxFT2Font::ShouldHintMetrics() const {
180   return !gfxPlatform::GetPlatform()->RequiresLinearZoom();
181 }
182 
FillGlyphDataForChar(FT_Face face,uint32_t ch,CachedGlyphData * gd)183 void gfxFT2Font::FillGlyphDataForChar(FT_Face face, uint32_t ch,
184                                       CachedGlyphData* gd) {
185   if (!face->charmap || (face->charmap->encoding != FT_ENCODING_UNICODE &&
186                          face->charmap->encoding != FT_ENCODING_MS_SYMBOL)) {
187     if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE) &&
188         FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_MS_SYMBOL)) {
189       NS_WARNING("failed to select Unicode or symbol charmap!");
190     }
191   }
192   FT_UInt gid = FT_Get_Char_Index(face, ch);
193 
194   if (gid == 0) {
195     // this font doesn't support this char!
196     NS_ASSERTION(gid != 0,
197                  "We don't have a glyph, but font indicated that it supported "
198                  "this char in tables?");
199     gd->glyphIndex = 0;
200     return;
201   }
202 
203   FT_Error err = Factory::LoadFTGlyph(face, gid, mFTLoadFlags);
204 
205   if (err) {
206     // hmm, this is weird, we failed to load a glyph that we had?
207     NS_WARNING("Failed to load glyph that we got from Get_Char_index");
208 
209     gd->glyphIndex = 0;
210     return;
211   }
212 
213   gd->glyphIndex = gid;
214   gd->lsbDelta = face->glyph->lsb_delta;
215   gd->rsbDelta = face->glyph->rsb_delta;
216   gd->xAdvance = face->glyph->advance.x;
217   if (gd->xAdvance) {
218     gd->xAdvance += GetEmboldenStrength(face).x;
219   }
220 }
221 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const222 void gfxFT2Font::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
223                                         FontCacheSizes* aSizes) const {
224   gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
225   aSizes->mFontInstances +=
226       mCharGlyphCache.ShallowSizeOfExcludingThis(aMallocSizeOf);
227 }
228 
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const229 void gfxFT2Font::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
230                                         FontCacheSizes* aSizes) const {
231   aSizes->mFontInstances += aMallocSizeOf(this);
232   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
233 }
234