Bug 435739 Poor performance of Firefox 3 with no X RENDER extension
[wine-gecko.git] / gfx / thebes / src / gfxWindowsPlatform.cpp
blob706bfe9e7e44b37651a49f59a6dd7b6fa5d57fff
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Foundation code.
17 * The Initial Developer of the Original Code is Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Stuart Parmenter <stuart@mozilla.com>
23 * Vladimir Vukicevic <vladimir@pobox.com>
24 * Masayuki Nakano <masayuki@d-toybox.com>
25 * Masatoshi Kimura <VYV03354@nifty.ne.jp>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "gfxWindowsPlatform.h"
43 #include "gfxImageSurface.h"
44 #include "gfxWindowsSurface.h"
46 #include "nsUnicharUtils.h"
48 #include "nsIPref.h"
49 #include "nsServiceManagerUtils.h"
51 #include "nsIWindowsRegKey.h"
53 #include "gfxWindowsFonts.h"
55 #include <string>
57 #include "lcms.h"
59 //#define DEBUG_CMAP_SIZE 1
61 // font info loader constants
62 static const PRUint32 kDelayBeforeLoadingCmaps = 8 * 1000; // 8secs
63 static const PRUint32 kIntervalBetweenLoadingCmaps = 150; // 150ms
64 static const PRUint32 kNumFontsPerSlice = 10; // read in info 10 fonts at a time
66 static __inline void
67 BuildKeyNameFromFontName(nsAString &aName)
69 if (aName.Length() >= LF_FACESIZE)
70 aName.Truncate(LF_FACESIZE - 1);
71 ToLowerCase(aName);
74 int PR_CALLBACK
75 gfxWindowsPlatform::PrefChangedCallback(const char *aPrefName, void *closure)
77 // XXX this could be made to only clear out the cache for the prefs that were changed
78 // but it probably isn't that big a deal.
79 gfxWindowsPlatform *plat = static_cast<gfxWindowsPlatform *>(closure);
80 plat->mPrefFonts.Clear();
81 return 0;
84 gfxWindowsPlatform::gfxWindowsPlatform()
85 : mStartIndex(0), mIncrement(kNumFontsPerSlice), mNumFamilies(0)
87 mFonts.Init(200);
88 mFontAliases.Init(20);
89 mFontSubstitutes.Init(50);
90 mPrefFonts.Init(10);
92 UpdateFontList();
94 nsCOMPtr<nsIPref> pref = do_GetService(NS_PREF_CONTRACTID);
95 pref->RegisterCallback("font.", PrefChangedCallback, this);
96 pref->RegisterCallback("font.name-list.", PrefChangedCallback, this);
97 pref->RegisterCallback("intl.accept_languages", PrefChangedCallback, this);
98 // don't bother unregistering. We'll get shutdown after the pref service
101 gfxWindowsPlatform::~gfxWindowsPlatform()
105 already_AddRefed<gfxASurface>
106 gfxWindowsPlatform::CreateOffscreenSurface(const gfxIntSize& size,
107 gfxASurface::gfxImageFormat imageFormat)
109 gfxASurface *surf = new gfxWindowsSurface(size, imageFormat);
110 NS_IF_ADDREF(surf);
111 return surf;
114 int CALLBACK
115 gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW *lpelfe,
116 const NEWTEXTMETRICEXW *nmetrics,
117 DWORD fontType, LPARAM data)
119 FontTable *ht = reinterpret_cast<FontTable*>(data);
121 const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
122 const LOGFONTW& logFont = lpelfe->elfLogFont;
124 // Ignore vertical fonts
125 if (logFont.lfFaceName[0] == L'@')
126 return 1;
128 nsAutoString name(logFont.lfFaceName);
129 BuildKeyNameFromFontName(name);
131 nsRefPtr<FontFamily> ff;
132 if (!ht->Get(name, &ff)) {
133 ff = new FontFamily(nsDependentString(logFont.lfFaceName));
134 ht->Put(name, ff);
137 return 1;
141 // general cmap reading routines moved to gfxFontUtils.cpp
143 struct FontListData {
144 FontListData(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) :
145 mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), mStringArray(aListOfFonts) {}
146 const nsACString& mLangGroup;
147 const nsACString& mGenericFamily;
148 nsStringArray& mStringArray;
151 PLDHashOperator PR_CALLBACK
152 gfxWindowsPlatform::HashEnumFunc(nsStringHashKey::KeyType aKey,
153 nsRefPtr<FontFamily>& aFontFamily,
154 void* userArg)
156 FontListData *data = (FontListData*)userArg;
158 // use the first variation for now. This data should be the same
159 // for all the variations and should probably be moved up to
160 // the Family
161 gfxFontStyle style;
162 style.langGroup = data->mLangGroup;
163 nsRefPtr<FontEntry> aFontEntry = aFontFamily->FindFontEntry(style);
165 /* skip symbol fonts */
166 if (aFontEntry->mSymbolFont)
167 return PL_DHASH_NEXT;
169 if (aFontEntry->SupportsLangGroup(data->mLangGroup) &&
170 aFontEntry->MatchesGenericFamily(data->mGenericFamily))
171 data->mStringArray.AppendString(aFontFamily->mName);
173 return PL_DHASH_NEXT;
176 nsresult
177 gfxWindowsPlatform::GetFontList(const nsACString& aLangGroup,
178 const nsACString& aGenericFamily,
179 nsStringArray& aListOfFonts)
181 FontListData data(aLangGroup, aGenericFamily, aListOfFonts);
183 mFonts.Enumerate(gfxWindowsPlatform::HashEnumFunc, &data);
185 aListOfFonts.Sort();
186 aListOfFonts.Compact();
188 return NS_OK;
191 static void
192 RemoveCharsetFromFontSubstitute(nsAString &aName)
194 PRInt32 comma = aName.FindChar(PRUnichar(','));
195 if (comma >= 0)
196 aName.Truncate(comma);
199 nsresult
200 gfxWindowsPlatform::UpdateFontList()
202 gfxFontCache *fc = gfxFontCache::GetCache();
203 if (fc)
204 fc->AgeAllGenerations();
205 mFonts.Clear();
206 mFontAliases.Clear();
207 mNonExistingFonts.Clear();
208 mFontSubstitutes.Clear();
209 mPrefFonts.Clear();
210 mCodepointsWithNoFonts.reset();
211 CancelLoader();
213 LOGFONTW logFont;
214 logFont.lfCharSet = DEFAULT_CHARSET;
215 logFont.lfFaceName[0] = 0;
216 logFont.lfPitchAndFamily = 0;
218 // Use the screen DC here.. should we use something else for printing?
219 HDC dc = ::GetDC(nsnull);
220 EnumFontFamiliesExW(dc, &logFont, (FONTENUMPROCW)gfxWindowsPlatform::FontEnumProc, (LPARAM)&mFonts, 0);
221 ::ReleaseDC(nsnull, dc);
223 // initialize the cmap loading process after font list has been initialized
224 StartLoader(kDelayBeforeLoadingCmaps, kIntervalBetweenLoadingCmaps);
226 // Create the list of FontSubstitutes
227 nsCOMPtr<nsIWindowsRegKey> regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1");
228 if (!regKey)
229 return NS_ERROR_FAILURE;
230 NS_NAMED_LITERAL_STRING(kFontSubstitutesKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
232 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
233 kFontSubstitutesKey, nsIWindowsRegKey::ACCESS_READ);
234 if (NS_FAILED(rv))
235 return rv;
237 PRUint32 count;
238 rv = regKey->GetValueCount(&count);
239 if (NS_FAILED(rv) || count == 0)
240 return rv;
241 for (PRUint32 i = 0; i < count; i++) {
242 nsAutoString substituteName;
243 rv = regKey->GetValueName(i, substituteName);
244 if (NS_FAILED(rv) || substituteName.IsEmpty() ||
245 substituteName.CharAt(1) == PRUnichar('@'))
246 continue;
247 PRUint32 valueType;
248 rv = regKey->GetValueType(substituteName, &valueType);
249 if (NS_FAILED(rv) || valueType != nsIWindowsRegKey::TYPE_STRING)
250 continue;
251 nsAutoString actualFontName;
252 rv = regKey->ReadStringValue(substituteName, actualFontName);
253 if (NS_FAILED(rv))
254 continue;
256 RemoveCharsetFromFontSubstitute(substituteName);
257 BuildKeyNameFromFontName(substituteName);
258 RemoveCharsetFromFontSubstitute(actualFontName);
259 BuildKeyNameFromFontName(actualFontName);
260 nsRefPtr<FontFamily> ff;
261 if (!actualFontName.IsEmpty() && mFonts.Get(actualFontName, &ff))
262 mFontSubstitutes.Put(substituteName, ff);
263 else
264 mNonExistingFonts.AppendString(substituteName);
267 // initialize ranges of characters for which system-wide font search should be skipped
268 mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
269 mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
271 InitBadUnderlineList();
273 return NS_OK;
276 struct FontFamilyListData {
277 FontFamilyListData(nsTArray<nsRefPtr<FontFamily> >& aFamilyArray)
278 : mFamilyArray(aFamilyArray)
281 static PLDHashOperator PR_CALLBACK AppendFamily(nsStringHashKey::KeyType aKey,
282 nsRefPtr<FontFamily>& aFamilyEntry,
283 void *aUserArg)
285 FontFamilyListData *data = (FontFamilyListData*)aUserArg;
286 data->mFamilyArray.AppendElement(aFamilyEntry);
287 return PL_DHASH_NEXT;
290 nsTArray<nsRefPtr<FontFamily> >& mFamilyArray;
293 void
294 gfxWindowsPlatform::GetFontFamilyList(nsTArray<nsRefPtr<FontFamily> >& aFamilyArray)
296 FontFamilyListData data(aFamilyArray);
297 mFonts.Enumerate(FontFamilyListData::AppendFamily, &data);
300 static PRBool SimpleResolverCallback(const nsAString& aName, void* aClosure)
302 nsString *result = static_cast<nsString*>(aClosure);
303 result->Assign(aName);
304 return PR_FALSE;
307 void
308 gfxWindowsPlatform::InitBadUnderlineList()
310 nsAutoTArray<nsString, 10> blacklist;
311 gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
312 PRUint32 numFonts = blacklist.Length();
313 for (PRUint32 i = 0; i < numFonts; i++) {
314 PRBool aborted;
315 nsAutoString resolved;
316 ResolveFontName(blacklist[i], SimpleResolverCallback, &resolved, aborted);
317 if (resolved.IsEmpty())
318 continue;
319 FontFamily *ff = FindFontFamily(resolved);
320 if (!ff)
321 continue;
322 ff->mIsBadUnderlineFont = 1;
326 nsresult
327 gfxWindowsPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
329 aFamilyName.Truncate();
330 PRBool aborted;
331 return ResolveFontName(aFontName, SimpleResolverCallback, &aFamilyName, aborted);
334 struct ResolveData {
335 ResolveData(gfxPlatform::FontResolverCallback aCallback,
336 gfxWindowsPlatform *aCaller, const nsAString *aFontName,
337 void *aClosure) :
338 mFoundCount(0), mCallback(aCallback), mCaller(aCaller),
339 mFontName(aFontName), mClosure(aClosure) {}
340 PRUint32 mFoundCount;
341 gfxPlatform::FontResolverCallback mCallback;
342 gfxWindowsPlatform *mCaller;
343 const nsAString *mFontName;
344 void *mClosure;
347 nsresult
348 gfxWindowsPlatform::ResolveFontName(const nsAString& aFontName,
349 FontResolverCallback aCallback,
350 void *aClosure,
351 PRBool& aAborted)
353 if (aFontName.IsEmpty())
354 return NS_ERROR_FAILURE;
356 nsAutoString keyName(aFontName);
357 BuildKeyNameFromFontName(keyName);
359 nsRefPtr<FontFamily> ff;
360 if (mFonts.Get(keyName, &ff) ||
361 mFontSubstitutes.Get(keyName, &ff) ||
362 mFontAliases.Get(keyName, &ff)) {
363 aAborted = !(*aCallback)(ff->mName, aClosure);
364 // XXX If the font has font link, we should add the linked font.
365 return NS_OK;
368 if (mNonExistingFonts.IndexOf(keyName) >= 0) {
369 aAborted = PR_FALSE;
370 return NS_OK;
373 LOGFONTW logFont;
374 logFont.lfCharSet = DEFAULT_CHARSET;
375 logFont.lfPitchAndFamily = 0;
376 PRInt32 len = aFontName.Length();
377 if (len >= LF_FACESIZE)
378 len = LF_FACESIZE - 1;
379 memcpy(logFont.lfFaceName,
380 nsPromiseFlatString(aFontName).get(), len * sizeof(PRUnichar));
381 logFont.lfFaceName[len] = 0;
383 HDC dc = ::GetDC(nsnull);
384 ResolveData data(aCallback, this, &keyName, aClosure);
385 aAborted = !EnumFontFamiliesExW(dc, &logFont,
386 (FONTENUMPROCW)gfxWindowsPlatform::FontResolveProc,
387 (LPARAM)&data, 0);
388 if (data.mFoundCount == 0)
389 mNonExistingFonts.AppendString(keyName);
390 ::ReleaseDC(nsnull, dc);
392 return NS_OK;
395 int CALLBACK
396 gfxWindowsPlatform::FontResolveProc(const ENUMLOGFONTEXW *lpelfe,
397 const NEWTEXTMETRICEXW *nmetrics,
398 DWORD fontType, LPARAM data)
400 const LOGFONTW& logFont = lpelfe->elfLogFont;
401 // Ignore vertical fonts
402 if (logFont.lfFaceName[0] == L'@' || logFont.lfFaceName[0] == 0)
403 return 1;
405 ResolveData *rData = reinterpret_cast<ResolveData*>(data);
407 nsAutoString name(logFont.lfFaceName);
409 // Save the alias name to cache
410 nsRefPtr<FontFamily> ff;
411 nsAutoString keyName(name);
412 BuildKeyNameFromFontName(keyName);
413 if (!rData->mCaller->mFonts.Get(keyName, &ff)) {
414 // This case only occurs on failing to build
415 // the list of font substitue. In this case, the user should
416 // reboot the Windows. Probably, we don't have the good way for
417 // resolving in this time.
418 NS_WARNING("Cannot find actual font");
419 return 1;
422 rData->mFoundCount++;
423 rData->mCaller->mFontAliases.Put(*(rData->mFontName), ff);
425 return (rData->mCallback)(name, rData->mClosure);
427 // XXX If the font has font link, we should add the linked font.
430 struct FontSearch {
431 FontSearch(PRUint32 aCh, gfxWindowsFont *aFont) :
432 ch(aCh), fontToMatch(aFont), matchRank(-1) {
434 PRUint32 ch;
435 nsRefPtr<gfxWindowsFont> fontToMatch;
436 PRInt32 matchRank;
437 nsRefPtr<FontEntry> bestMatch;
440 PLDHashOperator PR_CALLBACK
441 gfxWindowsPlatform::FindFontForCharProc(nsStringHashKey::KeyType aKey,
442 nsRefPtr<FontFamily>& aFontFamily,
443 void* userArg)
445 FontSearch *data = (FontSearch*)userArg;
447 const PRUint32 ch = data->ch;
449 nsRefPtr<FontEntry> fe = aFontFamily->FindFontEntry(*data->fontToMatch->GetStyle());
451 // skip over non-unicode and bitmap fonts and fonts that don't have
452 // the code point we're looking for
453 if (fe->IsCrappyFont() || !fe->mCharacterMap.test(ch))
454 return PL_DHASH_NEXT;
456 PRInt32 rank = 0;
457 // fonts that claim to support the range are more
458 // likely to be "better fonts" than ones that don't... (in theory)
459 if (fe->SupportsRange(gfxFontUtils::CharRangeBit(ch)))
460 rank += 1;
462 if (fe->SupportsLangGroup(data->fontToMatch->GetStyle()->langGroup))
463 rank += 2;
465 if (fe->mWindowsFamily == data->fontToMatch->GetFontEntry()->mWindowsFamily)
466 rank += 3;
467 if (fe->mWindowsPitch == data->fontToMatch->GetFontEntry()->mWindowsFamily)
468 rank += 3;
470 /* italic */
471 const PRBool italic = (data->fontToMatch->GetStyle()->style != FONT_STYLE_NORMAL);
472 if (fe->mItalic != italic)
473 rank += 3;
475 /* weight */
476 PRInt8 baseWeight, weightDistance;
477 data->fontToMatch->GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance);
478 if (fe->mWeight == (baseWeight * 100) + (weightDistance * 100))
479 rank += 2;
480 else if (fe->mWeight == data->fontToMatch->GetFontEntry()->mWeight)
481 rank += 1;
483 if (rank > data->matchRank ||
484 (rank == data->matchRank && Compare(fe->GetName(), data->bestMatch->GetName()) > 0)) {
485 data->bestMatch = fe;
486 data->matchRank = rank;
489 return PL_DHASH_NEXT;
492 already_AddRefed<gfxWindowsFont>
493 gfxWindowsPlatform::FindFontForChar(PRUint32 aCh, gfxWindowsFont *aFont)
495 // is codepoint with no matching font? return null immediately
496 if (mCodepointsWithNoFonts.test(aCh)) {
497 return nsnull;
500 FontSearch data(aCh, aFont);
502 // find fonts that support the character
503 mFonts.Enumerate(gfxWindowsPlatform::FindFontForCharProc, &data);
505 if (data.bestMatch) {
506 nsRefPtr<gfxWindowsFont> font =
507 gfxWindowsFont::GetOrMakeFont(data.bestMatch, aFont->GetStyle());
508 if (font->IsValid())
509 return font.forget();
510 return nsnull;
513 // no match? add to set of non-matching codepoints
514 mCodepointsWithNoFonts.set(aCh);
515 return nsnull;
518 gfxFontGroup *
519 gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies,
520 const gfxFontStyle *aStyle)
522 return new gfxWindowsFontGroup(aFamilies, aStyle);
525 FontFamily *
526 gfxWindowsPlatform::FindFontFamily(const nsAString& aName)
528 nsAutoString name(aName);
529 BuildKeyNameFromFontName(name);
531 nsRefPtr<FontFamily> ff;
532 if (!mFonts.Get(name, &ff) &&
533 !mFontSubstitutes.Get(name, &ff) &&
534 !mFontAliases.Get(name, &ff)) {
535 return nsnull;
537 return ff.get();
540 FontEntry *
541 gfxWindowsPlatform::FindFontEntry(const nsAString& aName, const gfxFontStyle& aFontStyle)
543 nsRefPtr<FontFamily> ff = FindFontFamily(aName);
544 if (!ff)
545 return nsnull;
547 return ff->FindFontEntry(aFontStyle);
550 cmsHPROFILE
551 gfxWindowsPlatform::GetPlatformCMSOutputProfile()
553 WCHAR str[1024+1];
554 DWORD size = 1024;
556 HDC dc = GetDC(nsnull);
557 GetICMProfileW(dc, &size, (LPWSTR)&str);
558 ReleaseDC(nsnull, dc);
560 cmsHPROFILE profile =
561 cmsOpenProfileFromFile(NS_ConvertUTF16toUTF8(str).get(), "r");
562 #ifdef DEBUG_tor
563 if (profile)
564 fprintf(stderr,
565 "ICM profile read from %s successfully\n",
566 NS_ConvertUTF16toUTF8(str).get());
567 #endif
568 return profile;
571 PRBool
572 gfxWindowsPlatform::GetPrefFontEntries(const nsCString& aKey, nsTArray<nsRefPtr<FontEntry> > *array)
574 return mPrefFonts.Get(aKey, array);
577 void
578 gfxWindowsPlatform::SetPrefFontEntries(const nsCString& aKey, nsTArray<nsRefPtr<FontEntry> >& array)
580 mPrefFonts.Put(aKey, array);
583 void
584 gfxWindowsPlatform::InitLoader()
586 GetFontFamilyList(mFontFamilies);
587 mStartIndex = 0;
588 mNumFamilies = mFontFamilies.Length();
591 PRBool
592 gfxWindowsPlatform::RunLoader()
594 PRUint32 i, endIndex = ( mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies );
596 // for each font family, load in various font info
597 for (i = mStartIndex; i < endIndex; i++) {
598 // load the cmaps for all variations
599 mFontFamilies[i]->FindStyleVariations();
602 mStartIndex += mIncrement;
603 if (mStartIndex < mNumFamilies)
604 return PR_FALSE;
605 return PR_TRUE;
608 void
609 gfxWindowsPlatform::FinishLoader()
611 mFontFamilies.Clear();
612 mNumFamilies = 0;