Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright 2014 Google Inc. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license that can be | |
| 5 * found in the LICENSE file. | |
| 6 */ | |
| 7 | |
| 8 #include "SkDataTable.h" | |
| 9 #include "SkFontDescriptor.h" | |
| 10 #include "SkFontHost_FreeType_common.h" | |
| 11 #include "SkFontMgr.h" | |
| 12 #include "SkFontStyle.h" | |
| 13 #include "SkMath.h" | |
| 14 #include "SkString.h" | |
| 15 #include "SkStream.h" | |
| 16 #include "SkTDArray.h" | |
| 17 #include "SkThread.h" | |
| 18 #include "SkTypefaceCache.h" | |
| 19 #include "SkOSFile.h" | |
| 20 | |
| 21 #include <fontconfig/fontconfig.h> | |
| 22 | |
| 23 // FC_POSTSCRIPT_NAME was added with b561ff20 which ended up in 2.10.92 | |
| 24 // Ubuntu 12.04 is on 2.8.0, 13.10 is on 2.10.93 | |
| 25 // Debian 7 is on 2.9.0, 8 is on 2.11 | |
| 26 // OpenSUSE 12.2 is on 2.9.0, 12.3 is on 2.10.2, 13.1 2.11.0 | |
| 27 // Fedora 19 is on 2.10.93 | |
| 28 #ifndef FC_POSTSCRIPT_NAME | |
| 29 # define FC_POSTSCRIPT_NAME "postscriptname" | |
| 30 #endif | |
| 31 | |
| 32 #ifdef SK_DEBUG | |
| 33 # include "SkTLS.h" | |
| 34 #endif | |
| 35 | |
| 36 /** Since FontConfig is poorly documented, this gives a high level overview: | |
| 37 * | |
| 38 * FcConfig is a handle to a FontConfig configuration instance. Each 'configura tion' is independent | |
| 39 * from any others which may exist. There exists a default global configuration which is created | |
| 40 * and destroyed by FcInit and FcFini, but this default should not normally be used. | |
| 41 * Instead, one should use FcConfigCreate and FcInit* to have a named local sta te. | |
| 42 * | |
| 43 * FcPatterns are {objectName -> [element]} (maps from object names to a list o f elements). | |
| 44 * Each element is some internal data plus an FcValue which is a variant (a uni on with a type tag). | |
| 45 * Lists of elements are not typed, except by convention. Any collection of FcV alues must be | |
| 46 * assumed to be heterogeneous by the code, but the code need not do anything p articularly | |
| 47 * interesting if the values go against convention. | |
| 48 * | |
| 49 * Somewhat like DirectWrite, FontConfig supports synthetics through FC_EMBOLDE N and FC_MATRIX. | |
| 50 * Like all synthetic information, such information must be passed with the fon t data. | |
| 51 */ | |
| 52 | |
| 53 namespace { | |
| 54 | |
| 55 // Fontconfig is not threadsafe before 2.10.91. Before that, we lock with a glob al mutex. | |
| 56 // See http://skbug.com/1497 for background. | |
| 57 SK_DECLARE_STATIC_MUTEX(gFCMutex); | |
| 58 | |
| 59 #ifdef SK_DEBUG | |
|
mtklein
2014/07/22 15:48:44
What's the idea here? So that we can test the loc
bungeman-skia
2014/07/22 22:09:57
Yes, I can't test the old stuff on my new machine,
| |
| 60 void *CreateThreadFcLocked() { return SkNEW(bool); } | |
|
mtklein
2014/07/22 15:48:44
* with void?
bungeman-skia
2014/07/22 22:09:58
I tried that, but you aren't allowed to muck with
| |
| 61 void DeleteThreadFcLocked(void* v) { SkDELETE(reinterpret_cast<bool*>(v)); } | |
|
mtklein
2014/07/22 15:48:43
I think you can get away with a simple static_cast
bungeman-skia
2014/07/22 22:09:58
A prvalue of type pointer to void (possibly cv-qua
| |
| 62 # define THREAD_FC_LOCKED \ | |
| 63 reinterpret_cast<bool*>(SkTLS::Get(CreateThreadFcLocked, DeleteThreadFcL ocked)) | |
| 64 #endif | |
| 65 | |
| 66 struct FCLocker { | |
| 67 // Assume FcGetVersion() has always been thread safe. | |
| 68 | |
| 69 FCLocker() { | |
| 70 if (FcGetVersion() < 21091) { | |
| 71 gFCMutex.acquire(); | |
| 72 fUnlock = true; | |
| 73 } else { | |
| 74 SkDEBUGCODE(bool* threadLocked = THREAD_FC_LOCKED); | |
| 75 SkASSERT(0 == *threadLocked); | |
|
mtklein
2014/07/22 15:48:43
0 -> false?
bungeman-skia
2014/07/22 22:09:58
Done.
| |
| 76 SkDEBUGCODE(*threadLocked = true); | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 ~FCLocker() { | |
| 81 if (FcGetVersion() < 21091) { | |
| 82 if (fUnlock) { | |
|
mtklein
2014/07/22 15:48:44
Do FcGetVersion() < 21091 and fUnlock ever evaluat
bungeman-skia
2014/07/22 22:09:58
Hmmm... yes, that does fall out of changing everyt
| |
| 83 gFCMutex.release(); | |
| 84 } | |
| 85 } else { | |
| 86 SkDEBUGCODE(bool* threadLocked = THREAD_FC_LOCKED); | |
| 87 SkASSERT(1 == *threadLocked); | |
|
mtklein
2014/07/22 15:48:44
1 -> true?
bungeman-skia
2014/07/22 22:09:59
Done.
| |
| 88 SkDEBUGCODE(*threadLocked = false); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 static void AssertHeld() { SkDEBUGCODE( | |
| 93 if (FcGetVersion() < 21091) { | |
| 94 gFCMutex.assertHeld(); | |
| 95 } else { | |
| 96 SkASSERT(true == *THREAD_FC_LOCKED); | |
| 97 } | |
| 98 ) } | |
| 99 | |
| 100 private: | |
| 101 bool fUnlock; | |
| 102 }; | |
| 103 | |
| 104 } // namespace | |
| 105 | |
| 106 template<typename T, void (*P)(T*)> void FcTDestroy(T* t) { | |
| 107 FCLocker::AssertHeld(); | |
| 108 P(t); | |
| 109 } | |
| 110 typedef SkAutoTCallVProc<FcConfig, FcTDestroy<FcConfig, FcConfigDestroy> > SkAut oFcConfig; | |
|
mtklein
2014/07/22 15:48:44
OCD these guys into columns?
bungeman-skia
2014/07/22 22:09:58
Your OCD is making fun of my OCD. When this sort o
| |
| 111 typedef SkAutoTCallVProc<FcFontSet, FcTDestroy<FcFontSet, FcFontSetDestroy> > Sk AutoFcFontSet; | |
| 112 typedef SkAutoTCallVProc<FcLangSet, FcTDestroy<FcLangSet, FcLangSetDestroy> > Sk AutoFcLangSet; | |
| 113 typedef SkAutoTCallVProc<FcObjectSet, FcTDestroy<FcObjectSet, FcObjectSetDestroy > > SkAutoFcObjectSet; | |
| 114 typedef SkAutoTCallVProc<FcPattern, FcTDestroy<FcPattern, FcPatternDestroy> > Sk AutoFcPattern; | |
| 115 | |
| 116 static int get_int(FcPattern* pattern, const char object[], int missing) { | |
| 117 int value; | |
| 118 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) { | |
|
mtklein
2014/07/22 15:48:44
It makes me so sad these don't take a const FcPatt
bungeman-skia
2014/07/22 22:09:58
Yeah, COM does the same thing, where 'const' is ne
| |
| 119 return missing; | |
| 120 } | |
| 121 return value; | |
| 122 } | |
| 123 | |
| 124 static const char* get_string(FcPattern* pattern, const char object[], const cha r* missing = "") { | |
| 125 FcChar8* value; | |
| 126 if (FcPatternGetString(pattern, object, 0, &value) != FcResultMatch) { | |
| 127 return missing; | |
| 128 } | |
| 129 return (const char*)value; | |
| 130 } | |
| 131 | |
| 132 enum is_weak_return { | |
|
mtklein
2014/07/22 15:48:43
Funky type name and constant values for Skia?
bungeman-skia
2014/07/22 22:09:57
This is a cpp file!!!
Eh, why not.
| |
| 133 isWeak, isStrong, noId | |
| 134 }; | |
| 135 /** Ideally there would exist a call like | |
| 136 * FcResult FcPatternIsWeak(pattern, object, id, FcBool* isWeak); | |
| 137 * | |
| 138 * However, there is no such call and as of Fc 2.11.0 even FcPatternEquals igno res the weak bit. | |
| 139 * Currently, the only reliable way of finding the weak bit is by its effect on matching. | |
| 140 * The weak bit only affects the matching of FC_FAMILY and FC_POSTSCRIPT_NAME o bject values. | |
| 141 * A element with the weak bit is scored after FC_LANG, without the weak bit is scored before. | |
| 142 * Note that the weak bit is stored on the element, not on the value it holds. | |
| 143 */ | |
| 144 static is_weak_return is_weak(FcPattern* pattern, const char object[], int id) { | |
| 145 FCLocker::AssertHeld(); | |
| 146 | |
| 147 FcResult result; | |
| 148 | |
| 149 // Create a copy of the pattern with only the value 'pattern'['object'['id'] ] in it. | |
| 150 // Internally, FontConfig pattern objects are linked lists, so faster to rem ove from head. | |
| 151 SkAutoFcObjectSet requestedObjectOnly(FcObjectSetBuild(object, NULL)); | |
| 152 SkAutoFcPattern minimal(FcPatternFilter(pattern, requestedObjectOnly)); | |
| 153 FcBool hasId = true; | |
| 154 for (int i = 0; hasId && i < id; ++i) { | |
| 155 hasId = FcPatternRemove(minimal, object, 0); | |
| 156 } | |
| 157 if (!hasId) { | |
| 158 return noId; | |
| 159 } | |
| 160 FcValue value; | |
| 161 result = FcPatternGet(minimal, object, 0, &value); | |
| 162 if (result != FcResultMatch) { | |
| 163 return noId; | |
| 164 } | |
| 165 while (hasId) { | |
| 166 hasId = FcPatternRemove(minimal, object, 1); | |
| 167 } | |
| 168 | |
| 169 // Create a font set with two patterns. | |
| 170 // 1. the same 'object' as minimal and a lang object with only 'nomatchlang' . | |
| 171 // 2. a different 'object' from minimal and a lang object with only 'matchla ng'. | |
| 172 SkAutoFcFontSet fontSet(FcFontSetCreate()); | |
| 173 | |
| 174 SkAutoFcLangSet strongLangSet(FcLangSetCreate()); | |
| 175 FcLangSetAdd(strongLangSet, (const FcChar8*)"nomatchlang"); | |
| 176 SkAutoFcPattern strong(FcPatternDuplicate(minimal)); | |
| 177 FcPatternAddLangSet(strong, FC_LANG, strongLangSet); | |
| 178 | |
| 179 SkAutoFcLangSet weakLangSet(FcLangSetCreate()); | |
| 180 FcLangSetAdd(weakLangSet, (const FcChar8*)"matchlang"); | |
| 181 SkAutoFcPattern weak(FcPatternCreate()); | |
| 182 FcPatternAddString(weak, object, (const FcChar8*)"nomatchstring"); | |
| 183 FcPatternAddLangSet(weak, FC_LANG, weakLangSet); | |
| 184 | |
| 185 FcFontSetAdd(fontSet, strong.detach()); | |
| 186 FcFontSetAdd(fontSet, weak.detach()); | |
| 187 | |
| 188 // Add 'matchlang' to the copy of the pattern. | |
| 189 FcPatternAddLangSet(minimal, FC_LANG, weakLangSet); | |
| 190 | |
| 191 // Run a match against the copy of the pattern. | |
| 192 // If the 'id' was weak, then we should match the pattern with 'matchlang'. | |
| 193 // If the 'id' was strong, then we should match the pattern with 'nomatchlan g'. | |
| 194 | |
| 195 // Note that this config is only used for FcFontRenderPrepare, which we don' t even want. | |
| 196 // However, there appears to be no way to match/sort without it. | |
| 197 SkAutoFcConfig config(FcConfigCreate()); | |
| 198 SkAutoFcPattern match(FcFontSetMatch(config, &fontSet, 1, minimal, &result)) ; | |
| 199 | |
| 200 FcLangSet* matchLangSet; | |
| 201 FcPatternGetLangSet(match, FC_LANG, 0, &matchLangSet); | |
| 202 return FcLangEqual == FcLangSetHasLang(matchLangSet, (const FcChar8*)"matchl ang") | |
| 203 ? isWeak : isStrong; | |
| 204 } | |
| 205 | |
| 206 /** Removes weak elements from either FC_FAMILY or FC_POSTSCRIPT_NAME objects in the property. | |
| 207 * This can be quite expensive, and should not be used more than once per font lookup. | |
| 208 * This removes all of the weak elements after the last strong element. | |
| 209 */ | |
| 210 static void remove_weak(FcPattern* pattern, const char object[]) { | |
| 211 FCLocker::AssertHeld(); | |
| 212 | |
| 213 SkAutoFcObjectSet requestedObjectOnly(FcObjectSetBuild(object, NULL)); | |
| 214 SkAutoFcPattern minimal(FcPatternFilter(pattern, requestedObjectOnly)); | |
| 215 | |
| 216 int lastStrongId = -1; | |
| 217 int numIds; | |
| 218 is_weak_return result; | |
| 219 for (int id = 0; ; ++id) { | |
| 220 result = is_weak(minimal, object, 0); | |
| 221 if (noId == result) { | |
| 222 numIds = id; | |
| 223 break; | |
| 224 } | |
| 225 if (isStrong == result) { | |
| 226 lastStrongId = id; | |
| 227 } | |
| 228 SkAssertResult(FcPatternRemove(minimal, object, 0)); | |
| 229 } | |
| 230 | |
| 231 // If they were all weak, then leave the pattern alone. | |
| 232 if (lastStrongId < 0) { | |
| 233 return; | |
| 234 } | |
| 235 | |
| 236 // Remove everything after the last strong. | |
| 237 for (int id = lastStrongId + 1; id < numIds; ++id) { | |
| 238 SkAssertResult(FcPatternRemove(pattern, object, lastStrongId + 1)); | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 static int map_range(SkFixed value, | |
| 243 SkFixed old_min, SkFixed old_max, | |
| 244 SkFixed new_min, SkFixed new_max) | |
| 245 { | |
| 246 SkASSERT(old_min < old_max); | |
| 247 SkASSERT(new_min <= new_max); | |
| 248 return new_min + SkMulDiv(value - old_min, new_max - new_min, | |
| 249 old_max - old_min); | |
| 250 } | |
| 251 | |
| 252 static int ave(SkFixed a, SkFixed b) { | |
| 253 return SkFixedAve(a, b); | |
| 254 } | |
| 255 | |
| 256 struct MapRanges { | |
| 257 SkFixed old_val; | |
| 258 SkFixed new_val; | |
| 259 }; | |
| 260 | |
| 261 static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int range sCount) { | |
| 262 // -Inf to [0] | |
| 263 if (val < ranges[0].old_val) | |
|
mtklein
2014/07/22 15:48:43
{} around all these ifs?
bungeman-skia
2014/07/22 22:09:57
Done.
| |
| 264 return ranges[0].new_val; | |
| 265 | |
| 266 // Linear from [i] to ave([i], [i+1]), then from ave([i], [i+1]) to [i+1] | |
| 267 for (int i = 0; i < rangesCount - 1; ++i) { | |
|
mtklein
2014/07/22 15:48:43
Is it ever worth doing some o(N) search here?
bungeman-skia
2014/07/22 22:09:59
Probably not.
| |
| 268 if (val < ave(ranges[i].old_val, ranges[i+1].old_val)) | |
| 269 return map_range(val, ranges[i].old_val, ave(ranges[i].old_val, rang es[i+1].old_val), | |
| 270 ranges[i].new_val, ave(ranges[i].new_val, rang es[i+1].new_val)); | |
| 271 if (val < ranges[i+1].old_val) | |
| 272 return map_range(val, ave(ranges[i].old_val, ranges[i+1].old_val), r anges[i+1].old_val, | |
| 273 ave(ranges[i].new_val, ranges[i+1].new_val), r anges[i+1].new_val); | |
| 274 } | |
| 275 | |
| 276 // From [n] to +Inf | |
| 277 // if (fcweight < Inf) | |
| 278 return ranges[rangesCount-1].new_val; | |
| 279 } | |
| 280 | |
| 281 static int map_ranges(int val, MapRanges const ranges[], int rangesCount) { | |
| 282 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesC ount)); | |
| 283 } | |
| 284 | |
| 285 template<int n> struct SkTFixed { | |
| 286 SK_COMPILE_ASSERT(-32768 <= n && n <= 32767, SkTFixed_n_not_in_range); | |
| 287 static const SkFixed value = static_cast<SkFixed>(n << 16); | |
| 288 }; | |
| 289 | |
| 290 static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) { | |
| 291 typedef SkFontStyle SkFS; | |
| 292 | |
| 293 static const MapRanges weightRanges[] = { | |
| 294 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value } , | |
|
mtklein
2014/07/22 15:48:43
Line all these guys up too?
bungeman-skia
2014/07/22 22:09:58
Generally, I'm against monospace ascii art, but si
| |
| 295 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weig ht>::value }, | |
| 296 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value }, | |
| 297 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::va lue }, | |
| 298 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::val ue }, | |
| 299 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>: :value }, | |
| 300 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value } , | |
| 301 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight >::value }, | |
| 302 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value }, | |
| 303 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<1000>::value }, | |
| 304 }; | |
| 305 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR), | |
| 306 weightRanges, SK_ARRAY_COUNT(weightRanges)); | |
| 307 | |
| 308 static const MapRanges widthRanges[] = { | |
| 309 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondens ed_Width>::value }, | |
| 310 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondens ed_Width>::value }, | |
| 311 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>: :value }, | |
| 312 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed _Width>::value }, | |
| 313 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value }, | |
| 314 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_W idth>::value }, | |
| 315 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::v alue }, | |
| 316 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded _Width>::value }, | |
| 317 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltaExpanded_ Width>::value }, | |
| 318 }; | |
| 319 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL), | |
| 320 widthRanges, SK_ARRAY_COUNT(widthRanges)); | |
| 321 | |
| 322 SkFS::Slant slant = get_int(pattern, FC_SLANT, FC_SLANT_ROMAN) > 0 | |
| 323 ? SkFS::kItalic_Slant | |
| 324 : SkFS::kUpright_Slant; | |
| 325 | |
| 326 return SkFontStyle(weight, width, slant); | |
| 327 } | |
| 328 | |
| 329 static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) { | |
| 330 FCLocker::AssertHeld(); | |
| 331 | |
| 332 typedef SkFontStyle SkFS; | |
| 333 | |
| 334 static const MapRanges weightRanges[] = { | |
| 335 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value } , | |
| 336 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIG HT>::value }, | |
| 337 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value }, | |
| 338 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::va lue }, | |
| 339 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::val ue }, | |
| 340 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>: :value }, | |
| 341 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value } , | |
| 342 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD >::value }, | |
| 343 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value }, | |
| 344 { SkTFixed<1000>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value }, | |
| 345 }; | |
| 346 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightR anges)); | |
| 347 | |
| 348 static const MapRanges widthRanges[] = { | |
| 349 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRAC ONDENSED>::value }, | |
| 350 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRAC ONDENSED>::value }, | |
| 351 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>: :value }, | |
| 352 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICON DENSED>::value }, | |
| 353 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value }, | |
| 354 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPA NDED>::value }, | |
| 355 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::v alue }, | |
| 356 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEX PANDED>::value }, | |
| 357 { SkTFixed<SkFS::kUltaExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXP ANDED>::value }, | |
| 358 }; | |
| 359 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRange s)); | |
| 360 | |
| 361 FcPatternAddInteger(pattern, FC_WEIGHT, weight); | |
| 362 FcPatternAddInteger(pattern, FC_WIDTH, width); | |
| 363 FcPatternAddInteger(pattern, FC_SLANT, style.isItalic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); | |
| 364 } | |
| 365 | |
| 366 static SkTypeface::Style sktypefacestyle_from_fcpattern(FcPattern* pattern) { | |
| 367 int fcweight = get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR); | |
| 368 int fcslant = get_int(pattern, FC_SLANT, FC_SLANT_ROMAN); | |
| 369 return (SkTypeface::Style)((fcweight >= FC_WEIGHT_BOLD ? SkTypeface::kBold : 0) | | |
| 370 (fcslant > FC_SLANT_ROMAN ? SkTypeface::kItalic : 0)); | |
| 371 } | |
| 372 | |
| 373 class SkTypeface_stream : public SkTypeface_FreeType { | |
| 374 public: | |
| 375 /** @param stream does not ownership of the reference, does take ownership o f the stream. */ | |
|
mtklein
2014/07/22 15:48:43
Awkward? Taking ownership of the stream sounds li
bungeman-skia
2014/07/22 22:09:58
Haha, 'will take a ref' sounds like it takes owner
| |
| 376 SkTypeface_stream(SkTypeface::Style style, bool fixedWidth, int ttcIndex, Sk StreamAsset* stream) | |
| 377 : INHERITED(style, SkTypefaceCache::NewFontID(), fixedWidth) | |
| 378 , fStream(SkRef(stream)) | |
| 379 , fIndex(ttcIndex) | |
| 380 { }; | |
| 381 | |
| 382 virtual void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) co nst SK_OVERRIDE { | |
| 383 desc->setStyle(this->style()); | |
| 384 *serialize = true; | |
| 385 } | |
| 386 | |
| 387 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE { | |
| 388 *ttcIndex = fIndex; | |
| 389 return fStream->duplicate(); | |
| 390 } | |
| 391 | |
| 392 private: | |
| 393 SkAutoTUnref<SkStreamAsset> fStream; | |
| 394 int fIndex; | |
| 395 | |
| 396 typedef SkTypeface_FreeType INHERITED; | |
| 397 }; | |
| 398 | |
| 399 class SkTypeface_fontconfig : public SkTypeface_FreeType { | |
| 400 public: | |
| 401 /** @param pattern takes ownership of the reference. */ | |
| 402 static SkTypeface_fontconfig* Create(FcPattern* pattern) { | |
| 403 return SkNEW_ARGS(SkTypeface_fontconfig, (pattern)); | |
| 404 } | |
| 405 mutable SkAutoFcPattern fPattern; | |
|
mtklein
2014/07/22 15:48:44
public, mutable, and sometimes we need to hold a g
bungeman-skia
2014/07/22 22:09:58
Yes, I could put it in a global linked list. I'd m
| |
| 406 | |
| 407 virtual void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) co nst SK_OVERRIDE { | |
| 408 FCLocker lock; | |
| 409 desc->setFamilyName(get_string(fPattern, FC_FAMILY)); | |
| 410 desc->setFontFileName(get_string(fPattern, FC_FILE)); | |
| 411 desc->setFullName(get_string(fPattern, FC_FULLNAME)); | |
| 412 desc->setPostscriptName(get_string(fPattern, FC_POSTSCRIPT_NAME)); | |
| 413 desc->setStyle(this->style()); | |
| 414 *serialize = false; | |
| 415 } | |
| 416 | |
| 417 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE { | |
| 418 FCLocker lock; | |
| 419 *ttcIndex = get_int(fPattern, FC_INDEX, 0); | |
| 420 return SkStream::NewFromFile(get_string(fPattern, FC_FILE)); | |
| 421 } | |
| 422 | |
| 423 virtual ~SkTypeface_fontconfig() { | |
| 424 // Hold the lock while unrefing the pattern. | |
| 425 FCLocker lock; | |
| 426 fPattern.reset(); | |
| 427 } | |
| 428 | |
| 429 private: | |
| 430 /** @param pattern takes ownership of the reference. */ | |
| 431 SkTypeface_fontconfig(FcPattern* pattern) | |
| 432 : INHERITED(sktypefacestyle_from_fcpattern(pattern), | |
| 433 SkTypefaceCache::NewFontID(), | |
| 434 FC_PROPORTIONAL != get_int(pattern, FC_SPACING, FC_PROPORTIO NAL)) | |
| 435 , fPattern(pattern) | |
| 436 { }; | |
| 437 | |
| 438 typedef SkTypeface_FreeType INHERITED; | |
| 439 }; | |
| 440 | |
| 441 class SkFontMgr_fontconfig : public SkFontMgr { | |
| 442 mutable SkAutoFcConfig fFC; | |
|
mtklein
2014/07/22 15:48:43
Ditto?
bungeman-skia
2014/07/22 22:09:58
What? This one's already private, what do you have
| |
| 443 SkAutoTUnref<SkDataTable> fFamilyNames; | |
| 444 | |
| 445 class StyleSet : public SkFontStyleSet { | |
| 446 public: | |
| 447 /** @param parent does not take ownership of the reference. | |
| 448 * @param fontSet takes ownership of the reference. | |
| 449 */ | |
| 450 StyleSet(const SkFontMgr_fontconfig* parent, FcFontSet* fontSet) | |
| 451 : fFontMgr(SkRef(parent)), fFontSet(fontSet) | |
| 452 { } | |
| 453 | |
| 454 virtual ~StyleSet() { | |
| 455 // Hold the lock while unrefing the font set. | |
| 456 FCLocker lock; | |
| 457 fFontSet.reset(); | |
| 458 } | |
| 459 | |
| 460 virtual int count() SK_OVERRIDE { return fFontSet->nfont; } | |
| 461 | |
| 462 virtual void getStyle(int index, SkFontStyle* style, SkString* styleName ) SK_OVERRIDE { | |
| 463 if (index < 0 || fFontSet->nfont <= index) { | |
| 464 return; | |
| 465 } | |
| 466 | |
| 467 FCLocker lock; | |
| 468 if (style) { | |
| 469 *style = skfontstyle_from_fcpattern(fFontSet->fonts[index]); | |
| 470 } | |
| 471 if (styleName) { | |
| 472 *styleName = get_string(fFontSet->fonts[index], FC_STYLE); | |
| 473 } | |
| 474 } | |
| 475 | |
| 476 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE { | |
| 477 FCLocker lock; | |
| 478 | |
| 479 FcPattern* match = fFontSet->fonts[index]; | |
| 480 return fFontMgr->createTypefaceFromFcPattern(match); | |
| 481 } | |
| 482 | |
| 483 virtual SkTypeface* matchStyle(const SkFontStyle& style) SK_OVERRIDE { | |
| 484 FCLocker lock; | |
| 485 | |
| 486 SkAutoFcPattern pattern(FcPatternCreate()); | |
| 487 if (NULL == pattern) { | |
| 488 return NULL; | |
| 489 } | |
| 490 | |
| 491 fcpattern_from_skfontstyle(style, pattern); | |
| 492 FcConfigSubstitute(fFontMgr->fFC, pattern, FcMatchPattern); | |
| 493 FcDefaultSubstitute(pattern); | |
| 494 | |
| 495 FcResult result; | |
| 496 SkAutoFcPattern match(FcFontSetMatch(fFontMgr->fFC, &fFontSet, 1, pa ttern, &result)); | |
| 497 if (NULL == match) { | |
| 498 return NULL; | |
| 499 } | |
| 500 | |
| 501 return fFontMgr->createTypefaceFromFcPattern(match); | |
| 502 } | |
| 503 | |
| 504 private: | |
| 505 SkAutoTUnref<const SkFontMgr_fontconfig> fFontMgr; | |
| 506 SkAutoFcFontSet fFontSet; | |
| 507 }; | |
| 508 | |
| 509 static bool find_name(const SkTDArray<const char*>& list, const char* str) { | |
|
mtklein
2014/07/22 15:48:43
FindName, etc?
bungeman-skia
2014/07/22 22:09:58
Why do we name private static methods of classes (
| |
| 510 int count = list.count(); | |
| 511 for (int i = 0; i < count; ++i) { | |
| 512 if (!strcmp(list[i], str)) { | |
| 513 return true; | |
| 514 } | |
| 515 } | |
| 516 return false; | |
| 517 } | |
| 518 | |
| 519 SkDataTable* get_family_names() { | |
|
mtklein
2014/07/22 15:48:43
getFamilyNames?
bungeman-skia
2014/07/22 22:09:59
Acknowledged.
| |
| 520 FCLocker lock; | |
| 521 | |
| 522 SkTDArray<const char*> names; | |
| 523 SkTDArray<size_t> sizes; | |
| 524 | |
| 525 static const FcSetName fcNameSet[] = { FcSetSystem, FcSetApplication }; | |
| 526 for (int setIndex = 0; setIndex < (int)SK_ARRAY_COUNT(fcNameSet); ++setI ndex) { | |
| 527 // Return value of FcConfigGetFonts must not be destroyed. | |
| 528 FcFontSet* allFonts(FcConfigGetFonts(fFC, fcNameSet[setIndex])); | |
| 529 if (NULL == allFonts) { | |
| 530 continue; | |
| 531 } | |
| 532 | |
| 533 for (int fontIndex = 0; fontIndex < allFonts->nfont; ++fontIndex) { | |
| 534 FcPattern* current = allFonts->fonts[fontIndex]; | |
| 535 for (int id = 0; ; ++id) { | |
| 536 FcChar8* fcFamilyName; | |
| 537 FcResult result = FcPatternGetString(current, FC_FAMILY, id, &fcFamilyName); | |
| 538 if (FcResultNoId == result) { | |
| 539 break; | |
| 540 } | |
| 541 if (FcResultMatch != result) { | |
| 542 continue; | |
| 543 } | |
| 544 const char* familyName = reinterpret_cast<const char*>(fcFam ilyName); | |
|
mtklein
2014/07/22 15:48:44
also probably can static_cast?
bungeman-skia
2014/07/22 22:09:58
Nope.
| |
| 545 if (familyName && !find_name(names, familyName)) { | |
| 546 *names.append() = familyName; | |
| 547 *sizes.append() = strlen(familyName) + 1; | |
| 548 } | |
| 549 } | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 return SkDataTable::NewCopyArrays((void *const *const)names.begin(), | |
|
mtklein
2014/07/22 15:48:43
woah. Is that, a const pointer to a const pointer
bungeman-skia
2014/07/22 22:09:57
Done.
| |
| 554 sizes.begin(), names.count()); | |
| 555 } | |
| 556 | |
| 557 static bool find_by_fc_pattern(SkTypeface* cached, SkTypeface::Style, void* ctx) { | |
| 558 SkTypeface_fontconfig* cshFace = reinterpret_cast<SkTypeface_fontconfig* >(cached); | |
|
mtklein
2014/07/22 15:48:44
Also should both safely static_cast, no?
bungeman-skia
2014/07/22 22:09:57
Done.
| |
| 559 FcPattern* ctxPattern = reinterpret_cast<FcPattern*>(ctx); | |
| 560 return FcTrue == FcPatternEqual(cshFace->fPattern, ctxPattern); | |
| 561 } | |
| 562 | |
| 563 mutable SkMutex fTFCacheMutex; | |
|
mtklein
2014/07/22 15:48:43
Can you break some of the bodies of these methods
bungeman-skia
2014/07/22 22:09:57
It did end up with ~350 lines in this block. Maybe
| |
| 564 mutable SkTypefaceCache fTFCache; | |
| 565 /** Creates a typeface using a typeface cache. | |
| 566 * @param pattern a complete pattern from FcFontRenderPrepare. | |
| 567 */ | |
| 568 SkTypeface* createTypefaceFromFcPattern(FcPattern* pattern) const { | |
| 569 FCLocker::AssertHeld(); | |
| 570 SkAutoMutexAcquire ama(fTFCacheMutex); | |
| 571 SkTypeface* face = fTFCache.findByProcAndRef(find_by_fc_pattern, pattern ); | |
| 572 if (NULL == face) { | |
| 573 FcPatternReference(pattern); | |
| 574 face = SkTypeface_fontconfig::Create(pattern); | |
| 575 if (face) { | |
| 576 fTFCache.add(face, SkTypeface::kNormal, true); | |
| 577 } | |
| 578 } | |
| 579 return face; | |
| 580 } | |
| 581 | |
| 582 public: | |
| 583 SkFontMgr_fontconfig() | |
| 584 : fFC(FcInitLoadConfigAndFonts()) | |
| 585 , fFamilyNames(get_family_names()) { } | |
| 586 | |
| 587 /** Takes control of the reference to 'config'. */ | |
| 588 explicit SkFontMgr_fontconfig(FcConfig* config) | |
| 589 : fFC(config) | |
| 590 , fFamilyNames(get_family_names()) { } | |
| 591 | |
| 592 virtual ~SkFontMgr_fontconfig() { | |
| 593 // Hold the lock while unrefing the config. | |
| 594 FCLocker lock; | |
| 595 fFC.reset(); | |
| 596 } | |
| 597 | |
| 598 protected: | |
| 599 virtual int onCountFamilies() const SK_OVERRIDE { | |
|
mtklein
2014/07/22 15:48:44
Does anything else derive from this? If not, migh
bungeman-skia
2014/07/22 22:09:58
We could, I suppose. Seems odd since others don't
| |
| 600 return fFamilyNames->count(); | |
| 601 } | |
| 602 | |
| 603 virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERR IDE { | |
| 604 familyName->set(fFamilyNames->atStr(index)); | |
| 605 } | |
| 606 | |
| 607 virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE { | |
| 608 return this->onMatchFamily(fFamilyNames->atStr(index)); | |
| 609 } | |
| 610 | |
| 611 /** True if any string object value in the font is the same | |
| 612 * as a string object value in the pattern. | |
|
mtklein
2014/07/22 15:48:44
<3 alignment <3
bungeman-skia
2014/07/22 22:09:58
Heh, you're all over vertical alignment above, but
| |
| 613 */ | |
| 614 static bool any_matching(FcPattern* font, FcPattern* pattern, const char* ob ject) { | |
| 615 FcChar8* fontString; | |
| 616 FcChar8* patternString; | |
| 617 FcResult result; | |
| 618 // Set an arbitrary limit on the number of pattern object values to cons ider. | |
|
mtklein
2014/07/22 15:48:44
Why?
bungeman-skia
2014/07/22 22:09:59
To prevent N*M from getting too big. Eventually th
| |
| 619 static const int maxId = 16; | |
| 620 for (int patternId = 0; patternId < maxId; ++patternId) { | |
| 621 result = FcPatternGetString(pattern, object, patternId, &patternStri ng); | |
| 622 if (FcResultNoId == result) { | |
| 623 break; | |
| 624 } | |
| 625 if (FcResultMatch != result) { | |
| 626 continue; | |
| 627 } | |
| 628 for (int fontId = 0; fontId < maxId; ++fontId) { | |
| 629 result = FcPatternGetString(font, object, fontId, &fontString); | |
| 630 if (FcResultNoId == result) { | |
| 631 break; | |
| 632 } | |
| 633 if (FcResultMatch != result) { | |
| 634 continue; | |
| 635 } | |
| 636 if (0 == FcStrCmpIgnoreCase(patternString, fontString)) { | |
| 637 return true; | |
| 638 } | |
| 639 } | |
| 640 } | |
| 641 return false; | |
| 642 } | |
| 643 | |
| 644 static bool valid_pattern(FcPattern* pattern) { | |
| 645 // FontConfig can return fonts which are unreadable. | |
| 646 const char* filename = get_string(pattern, FC_FILE, NULL); | |
| 647 if (NULL == filename) { | |
| 648 return false; | |
| 649 } | |
| 650 return sk_exists(filename, kRead_SkFILE_Flag); | |
| 651 } | |
| 652 | |
| 653 static bool font_matches(FcPattern* font, FcPattern* pattern) { | |
| 654 return valid_pattern(font) && any_matching(font, pattern, FC_FAMILY); | |
| 655 } | |
| 656 | |
| 657 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVER RIDE { | |
| 658 FCLocker lock; | |
| 659 | |
| 660 SkAutoFcPattern pattern(FcPatternCreate()); | |
| 661 if (NULL == pattern) { | |
| 662 return NULL; | |
| 663 } | |
| 664 | |
| 665 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); | |
| 666 FcConfigSubstitute(fFC, pattern, FcMatchPattern); | |
| 667 FcDefaultSubstitute(pattern); | |
| 668 | |
| 669 FcPattern* matchPattern; | |
| 670 SkAutoFcPattern strongPattern(NULL); | |
| 671 if (familyName) { | |
| 672 strongPattern.reset(FcPatternDuplicate(pattern)); | |
| 673 remove_weak(strongPattern, FC_FAMILY); | |
| 674 matchPattern = strongPattern; | |
| 675 } else { | |
| 676 matchPattern = pattern; | |
| 677 } | |
| 678 | |
| 679 SkAutoFcFontSet matches(FcFontSetCreate()); | |
| 680 // TODO: Some families have 'duplicates' due to symbolic links. | |
| 681 // The patterns are exactly the same except for the FC_FILE. | |
| 682 // It should be possible to collapse these patterns by normalizing. | |
| 683 static const FcSetName fcNameSet[] = { FcSetSystem, FcSetApplication }; | |
| 684 for (int setIndex = 0; setIndex < (int)SK_ARRAY_COUNT(fcNameSet); ++setI ndex) { | |
| 685 // Return value of FcConfigGetFonts must not be destroyed. | |
| 686 FcFontSet* allFonts(FcConfigGetFonts(fFC, fcNameSet[setIndex])); | |
| 687 if (NULL == allFonts) { | |
| 688 continue; | |
| 689 } | |
| 690 | |
| 691 for (int fontIndex = 0; fontIndex < allFonts->nfont; ++fontIndex) { | |
| 692 if (font_matches(allFonts->fonts[fontIndex], matchPattern)) { | |
| 693 FcFontSetAdd(matches, | |
| 694 FcFontRenderPrepare(fFC, pattern, allFonts->fon ts[fontIndex])); | |
| 695 } | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 return SkNEW_ARGS(StyleSet, (this, matches.detach())); | |
| 700 } | |
| 701 | |
| 702 virtual SkTypeface* onMatchFamilyStyle(const char familyName[], | |
| 703 const SkFontStyle& style) const SK_OV ERRIDE | |
| 704 { | |
| 705 FCLocker lock; | |
| 706 | |
| 707 SkAutoFcPattern pattern(FcPatternCreate()); | |
| 708 if (NULL == pattern) { | |
| 709 return NULL; | |
| 710 } | |
| 711 | |
| 712 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); | |
| 713 fcpattern_from_skfontstyle(style, pattern); | |
| 714 FcConfigSubstitute(fFC, pattern, FcMatchPattern); | |
| 715 FcDefaultSubstitute(pattern); | |
| 716 | |
| 717 // We really want to match strong (prefered) and same (acceptable) only here. | |
| 718 // If a family name was specified, assume that any weak matches after th e last strong match | |
| 719 // are weak (default) and ignore them. | |
| 720 // The reason for is that after substitution the pattern for 'sans-serif ' looks like | |
| 721 // "wwwwwwwwwwwwwwswww" where there are many weak but preferred names, f ollowed by defaults. | |
| 722 // So it is possible to have weakly matching but preferred names. | |
| 723 // In aliases, bindings are weak by default, so this is easy and common. | |
| 724 // If no family name was specified, we'll probably only get weak matches , but that's ok. | |
| 725 FcPattern* matchPattern; | |
| 726 SkAutoFcPattern strongPattern(NULL); | |
| 727 if (familyName) { | |
| 728 strongPattern.reset(FcPatternDuplicate(pattern)); | |
| 729 remove_weak(strongPattern, FC_FAMILY); | |
| 730 matchPattern = strongPattern; | |
| 731 } else { | |
| 732 matchPattern = pattern; | |
| 733 } | |
| 734 | |
| 735 FcResult result; | |
| 736 SkAutoFcPattern match(FcFontMatch(fFC, pattern, &result)); | |
| 737 if (NULL == match || !font_matches(match, matchPattern)) { | |
| 738 return NULL; | |
| 739 } | |
| 740 | |
| 741 return createTypefaceFromFcPattern(match); | |
| 742 } | |
| 743 | |
| 744 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* typeface, | |
| 745 const SkFontStyle& style) const SK_OVER RIDE | |
| 746 { | |
| 747 //TODO: should the SkTypeface_fontconfig know its family? | |
| 748 const SkTypeface_fontconfig* fcTypeface = | |
| 749 reinterpret_cast<const SkTypeface_fontconfig*>(typeface); | |
|
mtklein
2014/07/22 15:48:44
static_cast?
bungeman-skia
2014/07/22 22:09:58
Done.
| |
| 750 return this->matchFamilyStyle(get_string(fcTypeface->fPattern, FC_FAMILY ), style); | |
| 751 } | |
| 752 | |
| 753 /** @param stream does not take ownership of the reference. */ | |
| 754 virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE { | |
| 755 const size_t length = stream->getLength(); | |
| 756 if (!(0 < length && length < (1u << 30))) { | |
|
mtklein
2014/07/22 15:48:45
Might invert this?
if (length <= 0 || length >= (
bungeman-skia
2014/07/22 22:09:57
Changed from !(in_bounds) to out_of_bounds.
| |
| 757 return NULL; | |
| 758 } | |
| 759 | |
| 760 SkTypeface::Style style = SkTypeface::kNormal; | |
| 761 bool isFixedWidth = false; | |
| 762 if (!SkTypeface_FreeType::ScanFont(stream, ttcIndex, NULL, &style, &isFi xedWidth)) { | |
| 763 return NULL; | |
| 764 } | |
| 765 | |
| 766 return SkNEW_ARGS(SkTypeface_stream, (style, isFixedWidth, ttcIndex, | |
| 767 reinterpret_cast<SkStreamAsset*>(s tream))); | |
|
mtklein
2014/07/22 15:48:44
static_cast?
bungeman-skia
2014/07/22 22:09:57
Done.
| |
| 768 } | |
| 769 | |
| 770 virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OV ERRIDE { | |
| 771 SkAutoTUnref<SkStreamAsset> stream(SkNEW_ARGS(SkMemoryStream, (data))); | |
| 772 return this->createFromStream(stream, ttcIndex); | |
| 773 } | |
| 774 | |
| 775 virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE { | |
| 776 SkAutoTUnref<SkStreamAsset> stream(SkStream::NewFromFile(path)); | |
| 777 return this->createFromStream(stream, ttcIndex); | |
| 778 } | |
| 779 | |
| 780 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[], | |
| 781 unsigned styleBits) const SK_OVER RIDE { | |
| 782 bool bold = styleBits & SkTypeface::kBold; | |
| 783 bool italic = styleBits & SkTypeface::kItalic; | |
| 784 SkFontStyle style = SkFontStyle(bold ? SkFontStyle::kBold_Weight | |
| 785 : SkFontStyle::kNormal_Weight, | |
| 786 SkFontStyle::kNormal_Width, | |
| 787 italic ? SkFontStyle::kItalic_Slant | |
| 788 : SkFontStyle::kUpright_Slant); | |
| 789 SkAutoTUnref<SkTypeface> typeface(this->matchFamilyStyle(familyName, sty le)); | |
| 790 if (NULL != typeface.get()) { | |
| 791 return typeface.detach(); | |
| 792 } | |
| 793 | |
| 794 return this->matchFamilyStyle(NULL, style); | |
| 795 } | |
| 796 }; | |
| 797 | |
| 798 SkFontMgr* SkFontMgr::Factory() { | |
| 799 return SkNEW_ARGS(SkFontMgr_fontconfig, ()); | |
|
mtklein
2014/07/22 15:48:44
Also known as SkNEW(SkFontMgr_fontconfig) :P
bungeman-skia
2014/07/22 22:09:57
Ah yes, another victim of copy pasta. Done.
| |
| 800 } | |
| OLD | NEW |