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