diff --git a/mozilla/gfx/src/thebes/nsThebesFontMetrics.h b/mozilla/gfx/src/thebes/nsThebesFontMetrics.h index 802d31ad71b..fca08d0755c 100644 --- a/mozilla/gfx/src/thebes/nsThebesFontMetrics.h +++ b/mozilla/gfx/src/thebes/nsThebesFontMetrics.h @@ -159,7 +159,7 @@ protected: public: AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC, const char* aString, PRInt32 aLength, PRBool aEnableSpacing) { - mTextRun = gfxGlobalTextRunCache::GetTextRun( + mTextRun = gfxTextRunCache::MakeTextRun( NS_REINTERPRET_CAST(const PRUint8*, aString), aLength, aMetrics->mFontGroup, NS_STATIC_CAST(gfxContext*, aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)), @@ -168,17 +168,17 @@ protected: } AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC, const PRUnichar* aString, PRInt32 aLength, PRBool aEnableSpacing) { - mTextRun = gfxGlobalTextRunCache::GetTextRun( + mTextRun = gfxTextRunCache::MakeTextRun( aString, aLength, aMetrics->mFontGroup, NS_STATIC_CAST(gfxContext*, aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)), aMetrics->mP2A, ComputeFlags(aMetrics, aEnableSpacing)); } - gfxTextRun* operator->() { return mTextRun; } - gfxTextRun* get() { return mTextRun; } + gfxTextRun* operator->() { return mTextRun.get(); } + gfxTextRun* get() { return mTextRun.get(); } private: - gfxTextRun* mTextRun; + gfxTextRunCache::AutoTextRun mTextRun; static PRUint32 ComputeFlags(nsThebesFontMetrics* aMetrics, PRBool aEnableSpacing) { diff --git a/mozilla/gfx/thebes/public/gfxFont.h b/mozilla/gfx/thebes/public/gfxFont.h index 10b791d6d6c..f91f135818f 100644 --- a/mozilla/gfx/thebes/public/gfxFont.h +++ b/mozilla/gfx/thebes/public/gfxFont.h @@ -429,9 +429,11 @@ public: // Flags in the mask 0x0000F000 are reserved for per-platform fonts // Flags in the mask 0x00000FFF are set by the textrun creator. enum { - USER_TEXT_FLAGS = 0xFFFF0000, + CACHE_TEXT_FLAGS = 0xF0000000, + USER_TEXT_FLAGS = 0x0FFF0000, PLATFORM_TEXT_FLAGS = 0x0000F000, TEXTRUN_TEXT_FLAGS = 0x00000FFF, + SETTABLE_FLAGS = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS, /** * When set, the text string pointer used to create the text run @@ -517,7 +519,7 @@ public: * gfxFont. The glyphs are associated with a string of source text, and the * gfxTextRun APIs take parameters that are offsets into that source text. * - * \r, \t and \n characters (for which gfxFontGroup::IsInvisibleChar returns + * \r, \t and \n characters (for which gfxFontGroup::IsInvalidChar returns * PR_TRUE) should be set to a CompressedGlyph with SetMissing() to make them * invisible and zero-width. * @@ -795,12 +797,12 @@ public: void SetUserData(void *aUserData) { mUserData = aUserData; } PRUint32 GetFlags() const { return mFlags; } void SetFlagBits(PRUint32 aFlags) { - NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::USER_TEXT_FLAGS), + NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS), "Only user flags should be mutable"); mFlags |= aFlags; } void ClearFlagBits(PRUint32 aFlags) { - NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::USER_TEXT_FLAGS), + NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS), "Only user flags should be mutable"); mFlags &= ~aFlags; } @@ -1168,10 +1170,9 @@ public: virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) = 0; /** - * Tabs, CRs and LFs should be zero-width and invisible. They should - * break up shaping. + * Tabs, CRs and LFs should not be passed in to MakeTextRun. */ - static PRBool IsInvisibleChar(PRUnichar ch) { + static PRBool IsInvalidChar(PRUnichar ch) { return ch == '\t' || ch == '\r' || ch == '\n'; } diff --git a/mozilla/gfx/thebes/public/gfxTextRunCache.h b/mozilla/gfx/thebes/public/gfxTextRunCache.h index 840cf18ec85..92d09446de3 100644 --- a/mozilla/gfx/thebes/public/gfxTextRunCache.h +++ b/mozilla/gfx/thebes/public/gfxTextRunCache.h @@ -39,153 +39,80 @@ #define GFX_TEXT_RUN_CACHE_H #include "gfxFont.h" -#include "nsCheapSets.h" /** - * A textrun cache object. A general textrun caching solution. If you use - * this class to create a textrun cache, you are responsible for managing - * textrun lifetimes. The full power of textrun creation is exposed; you can - * set all textrun creation flags and parameters. - */ -class THEBES_API gfxTextRunCache { -public: - gfxTextRunCache() { - mCache.Init(100); - } - ~gfxTextRunCache() { - NS_ASSERTION(mCache.Count() == 0, "Textrun cache not empty!"); - } - - /** - * Get a textrun from the cache, create one if necessary. - * @param aFlags the flags TEXT_IS_ASCII, TEXT_IS_8BIT and TEXT_HAS_SURROGATES - * are ignored; the cache sets them based on the string. - * @param aCallerOwns if this is null, the cache always creates a new - * textrun owned by the caller. If non-null, the cache may return a textrun - * that was previously created and is owned by some previous caller - * to GetOrMakeTextRun on this cache. If so, *aCallerOwns will be set - * to false. - */ - gfxTextRun *GetOrMakeTextRun(const PRUnichar *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aCallerOwns = nsnull); - /** - * Get a textrun from the cache, create one if necessary. - * @param aFlags the flags TEXT_IS_ASCII, TEXT_IS_8BIT and TEXT_HAS_SURROGATES - * are ignored; the cache sets them based on the string. - * @param aCallerOwns if this is null, the cache always creates a new - * textrun owned by the caller. If non-null, the cache may return a textrun - * that was previously created and is owned by some previous caller - * to GetOrMakeTextRun on this cache. If so, *aCallerOwns will be set - * to false. - */ - gfxTextRun *GetOrMakeTextRun(const PRUint8 *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aCallerOwns = nsnull); - - /** - * Notify that a text run was hit in the cache, a new one created, and - * that the new one has replaced the old one in the cache. - */ - virtual void NotifyRemovedFromCache(gfxTextRun *aTextRun) {} - - /** - * Remove a textrun from the cache. This must be called before aTextRun - * is deleted! - */ - void RemoveTextRun(gfxTextRun *aTextRun); - - /** The following flags are part of the cache key: */ - enum { FLAG_MASK = - gfxTextRunFactory::TEXT_IS_RTL | - gfxTextRunFactory::TEXT_ENABLE_SPACING | - gfxTextRunFactory::TEXT_ABSOLUTE_SPACING | - gfxTextRunFactory::TEXT_ENABLE_NEGATIVE_SPACING | - gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS | - gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX - }; - -protected: - struct THEBES_API CacheHashKey { - void *mFontOrGroup; - const void *mString; - PRUint32 mLength; - PRUint32 mAppUnitsPerDevUnit; - PRUint32 mFlags; - PRUint32 mStringHash; - - CacheHashKey(void *aFontOrGroup, const void *aString, PRUint32 aLength, - PRUint32 aAppUnitsPerDevUnit, PRUint32 aFlags, PRUint32 aStringHash) - : mFontOrGroup(aFontOrGroup), mString(aString), mLength(aLength), - mAppUnitsPerDevUnit(aAppUnitsPerDevUnit), mFlags(aFlags), - mStringHash(aStringHash) {} - }; - - class THEBES_API CacheHashEntry : public PLDHashEntryHdr { - public: - typedef const CacheHashKey &KeyType; - typedef const CacheHashKey *KeyTypePointer; - - // When constructing a new entry in the hashtable, mTextRuns will be - // blank. The caller of Put() will fill it in. - CacheHashEntry(KeyTypePointer aKey) { } - CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); } - ~CacheHashEntry() { } - - PRBool KeyEquals(const KeyTypePointer aKey) const; - static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } - static PLDHashNumber HashKey(const KeyTypePointer aKey); - enum { ALLOW_MEMMOVE = PR_TRUE }; - - gfxTextRun *mTextRun; - }; - - CacheHashKey GetKeyForTextRun(gfxTextRun *aTextRun); - - nsTHashtable mCache; -}; - -/** - * A simple global textrun cache for textruns that do not carry state + * A simple textrun cache for textruns that do not carry state * (e.g., actual or potential linebreaks) and do not need complex initialization. * The lifetimes of these textruns are managed by the cache (they are auto-expired * after a certain period of time). */ -class THEBES_API gfxGlobalTextRunCache { +class THEBES_API gfxTextRunCache { public: /** - * Get a textrun for the given text, using a global cache. The returned - * textrun is valid until the next event loop. We own it, the caller - * must not free it. + * Get a textrun for the given text, using a global cache. The textrun + * must be released via ReleaseTextRun, not deleted. * Do not set any state in the textrun (e.g. actual or potential linebreaks). * Flags IS_8BIT, IS_ASCII and HAS_SURROGATES are automatically set * appropriately. * Flag IS_PERSISTENT must NOT be set unless aText is guaranteed to live * forever. + * The string can contain any characters, invalid ones will be stripped + * properly. */ - static gfxTextRun *GetTextRun(const PRUnichar *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - gfxContext *aRefContext, - PRUint32 aAppUnitsPerDevUnit, - PRUint32 aFlags); + static gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + gfxContext *aRefContext, + PRUint32 aAppUnitsPerDevUnit, + PRUint32 aFlags); /** - * Get a textrun for the given text, using a global cache. The returned - * textrun is valid until the next event loop. We own it, the caller - * must not free it. + * As above, but allows a full Parameters object to be passed in. + */ + static gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxTextRunFactory::Parameters* aParams, + PRUint32 aFlags); + + /** + * Get a textrun for the given text, using a global cache. The textrun + * must be released via ReleaseTextRun, not deleted. * Do not set any state in the textrun (e.g. actual or potential linebreaks). * Flags IS_8BIT, IS_ASCII and HAS_SURROGATES are automatically set * appropriately. * Flag IS_PERSISTENT must NOT be set unless aText is guaranteed to live * forever. + * The string can contain any characters, invalid ones will be stripped + * properly. */ - static gfxTextRun *GetTextRun(const PRUint8 *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - gfxContext *aRefContext, - PRUint32 aAppUnitsPerDevUnit, - PRUint32 aFlags); + static gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + gfxContext *aRefContext, + PRUint32 aAppUnitsPerDevUnit, + PRUint32 aFlags); + + /** + * Release a previously acquired textrun. Consider using AutoTextRun + * instead of calling this. + */ + static void ReleaseTextRun(gfxTextRun *aTextRun); + + class AutoTextRun { + public: + AutoTextRun(gfxTextRun *aTextRun) : mTextRun(aTextRun) {} + AutoTextRun() : mTextRun(nsnull) {} + AutoTextRun& operator=(gfxTextRun *aTextRun) { + gfxTextRunCache::ReleaseTextRun(mTextRun); + mTextRun = aTextRun; + return *this; + } + ~AutoTextRun() { + gfxTextRunCache::ReleaseTextRun(mTextRun); + } + gfxTextRun *get() { return mTextRun; } + gfxTextRun *operator->() { return mTextRun; } + private: + gfxTextRun *mTextRun; + }; protected: friend class gfxPlatform; diff --git a/mozilla/gfx/thebes/public/gfxTextRunWordCache.h b/mozilla/gfx/thebes/public/gfxTextRunWordCache.h index 20f7656f0d1..99015a821f0 100644 --- a/mozilla/gfx/thebes/public/gfxTextRunWordCache.h +++ b/mozilla/gfx/thebes/public/gfxTextRunWordCache.h @@ -46,101 +46,46 @@ */ class THEBES_API gfxTextRunWordCache { public: - gfxTextRunWordCache() { - mCache.Init(100); - } - ~gfxTextRunWordCache() { - NS_ASSERTION(mCache.Count() == 0, "Textrun cache not empty!"); - } + enum { TEXT_IN_CACHE = 0x10000000 }; /** * Create a textrun using cached words. + * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically + * treated as invisible missing. * @param aFlags the flags TEXT_IS_ASCII and TEXT_HAS_SURROGATES must be set - * by the caller, if applicable - * @param aIsInCache if true is returned, then RemoveTextRun must be called - * before the textrun changes or dies. + * by the caller, if applicable; TEXT_IN_CACHE is added if we + * have a reference to the textrun in the cache and RemoveTextRun must + * be called when the textrun dies. */ - gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aIsInCache); + static gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags); /** - * Create a textrun using cached words - * @param aFlags the flag TEXT_IS_ASCII must be set by the caller, - * if applicable - * @param aIsInCache if true is returned, then RemoveTextRun must be called - * before the textrun changes or dies. + * Create a textrun using cached words. + * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically + * treated as invisible missing. + * @param aFlags the flags TEXT_IS_ASCII and TEXT_HAS_SURROGATES must be set + * by the caller, if applicable; TEXT_IN_CACHE is added if we + * have a reference to the textrun in the cache and RemoveTextRun must + * be called when the textrun dies. */ - gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aIsInCache); + static gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags); /** * Remove a textrun from the cache. This must be called before aTextRun * is deleted! The text in the textrun must still be valid. */ - void RemoveTextRun(gfxTextRun *aTextRun); + static void RemoveTextRun(gfxTextRun *aTextRun); protected: - struct THEBES_API CacheHashKey { - void *mFontOrGroup; - const void *mString; - PRUint32 mLength; - PRUint32 mAppUnitsPerDevUnit; - PRUint32 mStringHash; - PRPackedBool mIsDoubleByteText; - }; + friend class gfxPlatform; - class THEBES_API CacheHashEntry : public PLDHashEntryHdr { - public: - typedef const CacheHashKey &KeyType; - typedef const CacheHashKey *KeyTypePointer; - - // When constructing a new entry in the hashtable, the caller of Put() - // will fill us in. - CacheHashEntry(KeyTypePointer aKey) : mTextRun(nsnull), mWordOffset(0), - mHashedByFont(PR_FALSE) { } - CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); } - ~CacheHashEntry() { } - - PRBool KeyEquals(const KeyTypePointer aKey) const; - static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } - static PLDHashNumber HashKey(const KeyTypePointer aKey); - enum { ALLOW_MEMMOVE = PR_TRUE }; - - gfxTextRun *mTextRun; - // The offset of the start of the word in the textrun. The length of - // the word is not stored here because we can figure it out by - // looking at the textrun's text. - PRUint32 mWordOffset:31; - // This is set to true when the cache entry was hashed by the first - // font in mTextRun's fontgroup; it's false when the cache entry - // was hashed by the fontgroup itself. - PRUint32 mHashedByFont:1; - }; - - // Used to track words that should be copied from one textrun to - // another during the textrun construction process - struct DeferredWord { - gfxTextRun *mSourceTextRun; - PRUint32 mSourceOffset; - PRUint32 mDestOffset; - PRUint32 mLength; - PRUint32 mHash; - }; - - PRBool LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont, - PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash, - nsTArray* aDeferredWords); - void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun, - gfxContext *aContext, - const nsTArray& aDeferredWords, - PRBool aSuccessful); - void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart, - PRUint32 aEnd, PRUint32 aHash); - - nsTHashtable mCache; + static nsresult Init(); + static void Shutdown(); }; #endif /* GFX_TEXT_RUN_WORD_CACHE_H */ diff --git a/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp b/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp index 9e17135cc7f..7a4c3495fda 100644 --- a/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp +++ b/mozilla/gfx/thebes/src/gfxAtsuiFonts.cpp @@ -687,13 +687,8 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount, } } - gfxTextRun::CompressedGlyph g; - if (gfxFontGroup::IsInvisibleChar(aString[firstOffset/2])) { - NS_ASSERTION(firstOffset == lastOffset, - "Invisible character grouped with other characters?"); - aRun->SetCharacterGlyph(aSegmentStart + firstOffset/2, g.SetMissing()); - return; - } + NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aString[firstOffset/2]), + "Invalid char passed in"); if (!allMatched) { for (i = firstOffset; i <= lastOffset; ++i) { @@ -703,6 +698,7 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount, return; } + gfxTextRun::CompressedGlyph g; PRUint32 offset; for (offset = firstOffset + 2; offset <= lastOffset; offset += 2) { PRUint32 index = offset/2; diff --git a/mozilla/gfx/thebes/src/gfxPangoFonts.cpp b/mozilla/gfx/thebes/src/gfxPangoFonts.cpp index 591b25f68e3..227fe19bd24 100644 --- a/mozilla/gfx/thebes/src/gfxPangoFonts.cpp +++ b/mozilla/gfx/thebes/src/gfxPangoFonts.cpp @@ -1062,11 +1062,9 @@ SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount, if (ch >= 0x10000) { // Skip surrogate ++utf16Offset; - } else { - if (gfxFontGroup::IsInvisibleChar(PRUnichar(ch))) { - aTextRun->SetCharacterGlyph(utf16Offset, g.SetMissing()); - } } + NS_ASSERTION(!gfxFontGroup::IsInvalidChar(PRUnichar(ch)), + "Invalid character detected"); ++utf16Offset; // We produced this UTF8 so we don't need to worry about malformed stuff @@ -1256,9 +1254,8 @@ gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun, // treat this null byte as a missing glyph. Pango // doesn't create glyphs for these, not even missing-glyphs. aTextRun->SetMissingGlyph(utf16Offset, 0); - } else if (ch < 0x10000 && IsInvisibleChar(PRUnichar(ch))) { - aTextRun->SetCharacterGlyph(utf16Offset, g.SetMissing()); } else { + NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected"); FT_UInt glyph = XftCharIndex(dpy, xfont, ch); XGlyphInfo info; XftGlyphExtents(dpy, xfont, &glyph, 1, &info); diff --git a/mozilla/gfx/thebes/src/gfxPlatform.cpp b/mozilla/gfx/thebes/src/gfxPlatform.cpp index cc4a34b1981..dcafbdb3266 100644 --- a/mozilla/gfx/thebes/src/gfxPlatform.cpp +++ b/mozilla/gfx/thebes/src/gfxPlatform.cpp @@ -53,6 +53,7 @@ #include "gfxContext.h" #include "gfxImageSurface.h" #include "gfxTextRunCache.h" +#include "gfxTextRunWordCache.h" #include "nsIPref.h" #include "nsServiceManagerUtils.h" @@ -108,9 +109,16 @@ gfxPlatform::Init() return rv; } - rv = gfxGlobalTextRunCache::Init(); + rv = gfxTextRunWordCache::Init(); if (NS_FAILED(rv)) { - NS_ERROR("Could not initialize gfxGlobalTextRunCache"); + NS_ERROR("Could not initialize gfxTextRunWordCache"); + Shutdown(); + return rv; + } + + rv = gfxTextRunCache::Init(); + if (NS_FAILED(rv)) { + NS_ERROR("Could not initialize gfxTextRunCache"); Shutdown(); return rv; } @@ -123,7 +131,8 @@ gfxPlatform::Shutdown() { // These may be called before the corresponding subsystems have actually // started up. That's OK, they can handle it. - gfxGlobalTextRunCache::Shutdown(); + gfxTextRunCache::Shutdown(); + gfxTextRunWordCache::Shutdown(); gfxFontCache::Shutdown(); #if defined(XP_MACOSX) gfxQuartzFontCache::Shutdown(); diff --git a/mozilla/gfx/thebes/src/gfxTextRunCache.cpp b/mozilla/gfx/thebes/src/gfxTextRunCache.cpp index 6a2b0536447..c87de843829 100644 --- a/mozilla/gfx/thebes/src/gfxTextRunCache.cpp +++ b/mozilla/gfx/thebes/src/gfxTextRunCache.cpp @@ -36,345 +36,100 @@ * ***** END LICENSE BLOCK ***** */ #include "gfxTextRunCache.h" +#include "gfxTextRunWordCache.h" #include "nsExpirationTracker.h" -static inline PRUint32 -HashMix(PRUint32 aHash, PRUnichar aCh) -{ - return (aHash >> 28) ^ (aHash << 4) ^ aCh; -} - -static PRUint32 -HashString(const PRUnichar *aText, PRUint32 aLength, PRUint32 *aFlags) -{ - *aFlags &= ~(gfxFontGroup::TEXT_HAS_SURROGATES | gfxFontGroup::TEXT_IS_ASCII); - PRUint32 i; - PRUint32 hashCode = 0; - PRUnichar allBits = 0; - for (i = 0; i < aLength; ++i) { - PRUnichar ch = aText[i]; - hashCode = HashMix(hashCode, ch); - allBits |= ch; - if (IS_SURROGATE(ch)) { - *aFlags |= gfxFontGroup::TEXT_HAS_SURROGATES; - } - } - if (!(allBits & ~0x7F)) { - *aFlags |= gfxFontGroup::TEXT_IS_ASCII; - } - return hashCode; -} - -static PRUint32 -HashString(const PRUint8 *aText, PRUint32 aLength, PRUint32 *aFlags) -{ - *aFlags &= ~(gfxFontGroup::TEXT_HAS_SURROGATES | gfxFontGroup::TEXT_IS_ASCII); - *aFlags |= gfxFontGroup::TEXT_IS_8BIT; - PRUint32 i; - PRUint32 hashCode = 0; - PRUint8 allBits = 0; - for (i = 0; i < aLength; ++i) { - PRUint8 ch = aText[i]; - hashCode = HashMix(hashCode, ch); - allBits |= ch; - } - if (!(allBits & ~0x7F)) { - *aFlags |= gfxFontGroup::TEXT_IS_ASCII; - } - return hashCode; -} - -static void *GetCacheKeyFontOrGroup(gfxTextRun *aTextRun) -{ - PRUint32 glyphRunCount; - const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&glyphRunCount); - gfxFontGroup *fontGroup = aTextRun->GetFontGroup(); - gfxFont *firstFont = fontGroup->GetFontAt(0); - return glyphRunCount == 1 && glyphRuns[0].mFont == firstFont - ? NS_STATIC_CAST(void *, firstFont) - : NS_STATIC_CAST(void *, fontGroup); -} - -gfxTextRun * -gfxTextRunCache::GetOrMakeTextRun(const PRUnichar *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aCallerOwns) -{ - if (aCallerOwns) { - *aCallerOwns = PR_TRUE; - } - if (aLength == 0) { - aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT; - } else if (aLength == 1 && aText[0] == ' ') { - aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT; - static const PRUnichar space = ' '; - aText = &space; - } - - PRUint32 hashCode = HashString(aText, aLength, &aFlags); - gfxFont *font = aFontGroup->GetFontAt(0); - CacheHashKey key(font, aText, aLength, aParams->mAppUnitsPerDevUnit, aFlags, hashCode); - CacheHashEntry *entry = nsnull; - if (font) { - entry = mCache.GetEntry(key); - } - if (!entry) { - key.mFontOrGroup = aFontGroup; - entry = mCache.GetEntry(key); - } - if (entry) { - gfxTextRun *textRun = entry->mTextRun; - if (aCallerOwns) { - *aCallerOwns = PR_FALSE; - return textRun; - } - gfxTextRun *newRun = - textRun->Clone(aParams, aText, aLength, aFontGroup, aFlags); - if (newRun) { - newRun->SetHashCode(hashCode); - entry->mTextRun = newRun; - NotifyRemovedFromCache(textRun); - return newRun; - } - } - - gfxTextRun *newRun = - aFontGroup->MakeTextRun(aText, aLength, aParams, aFlags); - if (newRun) { - newRun->SetHashCode(hashCode); - key.mFontOrGroup = GetCacheKeyFontOrGroup(newRun); - entry = mCache.PutEntry(key); - if (entry) { - entry->mTextRun = newRun; - } - NS_ASSERTION(!entry || entry == mCache.GetEntry(GetKeyForTextRun(newRun)), - "Inconsistent hashing"); - } - return newRun; -} - -gfxTextRun * -gfxTextRunCache::GetOrMakeTextRun(const PRUint8 *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aCallerOwns) -{ - if (aCallerOwns) { - *aCallerOwns = PR_TRUE; - } - if (aLength == 0) { - aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT; - } else if (aLength == 1 && aText[0] == ' ') { - aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT; - static const PRUint8 space = ' '; - aText = &space; - } - - PRUint32 hashCode = HashString(aText, aLength, &aFlags); - gfxFont *font = aFontGroup->GetFontAt(0); - CacheHashKey key(font, aText, aLength, aParams->mAppUnitsPerDevUnit, aFlags, hashCode); - CacheHashEntry *entry = nsnull; - if (font) { - entry = mCache.GetEntry(key); - } - if (!entry) { - key.mFontOrGroup = aFontGroup; - entry = mCache.GetEntry(key); - } - if (entry) { - gfxTextRun *textRun = entry->mTextRun; - if (aCallerOwns) { - *aCallerOwns = PR_FALSE; - return textRun; - } - - gfxTextRun *newRun = - textRun->Clone(aParams, aText, aLength, - aFontGroup, aFlags); - if (newRun) { - newRun->SetHashCode(hashCode); - entry->mTextRun = newRun; - NotifyRemovedFromCache(textRun); - return newRun; - } - } - - gfxTextRun *newRun = - aFontGroup->MakeTextRun(aText, aLength, aParams, aFlags); - if (newRun) { - newRun->SetHashCode(hashCode); - key.mFontOrGroup = GetCacheKeyFontOrGroup(newRun); - entry = mCache.PutEntry(key); - if (entry) { - entry->mTextRun = newRun; - } - NS_ASSERTION(!entry || entry == mCache.GetEntry(GetKeyForTextRun(newRun)), - "Inconsistent hashing"); - } - return newRun; -} - -gfxTextRunCache::CacheHashKey -gfxTextRunCache::GetKeyForTextRun(gfxTextRun *aTextRun) -{ - const void *text; - PRUint32 length = aTextRun->GetLength(); - if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) { - text = aTextRun->GetText8Bit(); - } else { - text = aTextRun->GetTextUnicode(); - } - void *fontOrGroup = GetCacheKeyFontOrGroup(aTextRun); - return CacheHashKey(fontOrGroup, text, length, aTextRun->GetAppUnitsPerDevUnit(), - aTextRun->GetFlags(), aTextRun->GetHashCode()); -} - -void -gfxTextRunCache::RemoveTextRun(gfxTextRun *aTextRun) -{ - CacheHashKey key = GetKeyForTextRun(aTextRun); -#ifdef DEBUG - CacheHashEntry *entry = mCache.GetEntry(key); - NS_ASSERTION(entry && entry->mTextRun == aTextRun, - "Failed to find textrun in cache"); -#endif - mCache.RemoveEntry(key); -} - -static PRBool -CompareDifferentWidthStrings(const PRUint8 *aStr1, const PRUnichar *aStr2, - PRUint32 aLength) -{ - PRUint32 i; - for (i = 0; i < aLength; ++i) { - if (aStr1[i] != aStr2[i]) - return PR_FALSE; - } - return PR_TRUE; -} - -PRBool -gfxTextRunCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const -{ - gfxTextRun *textRun = mTextRun; - if (!textRun) - return PR_FALSE; - PRUint32 length = textRun->GetLength(); - if (aKey->mFontOrGroup != GetCacheKeyFontOrGroup(textRun) || - aKey->mLength != length || - aKey->mAppUnitsPerDevUnit != textRun->GetAppUnitsPerDevUnit() || - ((aKey->mFlags ^ textRun->GetFlags()) & FLAG_MASK)) - return PR_FALSE; - - if (textRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) { - if (aKey->mFlags & gfxFontGroup::TEXT_IS_8BIT) - return memcmp(textRun->GetText8Bit(), aKey->mString, length) == 0; - return CompareDifferentWidthStrings(textRun->GetText8Bit(), - NS_STATIC_CAST(const PRUnichar *, aKey->mString), length); - } else { - if (!(aKey->mFlags & gfxFontGroup::TEXT_IS_8BIT)) - return memcmp(textRun->GetTextUnicode(), aKey->mString, length*sizeof(PRUnichar)) == 0; - return CompareDifferentWidthStrings(NS_STATIC_CAST(const PRUint8 *, aKey->mString), - textRun->GetTextUnicode(), length); - } -} - -PLDHashNumber -gfxTextRunCache::CacheHashEntry::HashKey(const KeyTypePointer aKey) -{ - return aKey->mStringHash + (long)aKey->mFontOrGroup + aKey->mAppUnitsPerDevUnit + - (aKey->mFlags & FLAG_MASK); -} - /* * Cache textruns and expire them after 3*10 seconds of no use */ -class TextRunCache : public nsExpirationTracker { +class TextRunExpiringCache : public nsExpirationTracker { public: - enum { TIMEOUT_SECONDS = 10 }; - TextRunCache() - : nsExpirationTracker(TIMEOUT_SECONDS*1000) {} - ~TextRunCache() { + TextRunExpiringCache() + : nsExpirationTracker(10*1000) {} + ~TextRunExpiringCache() { AgeAllGenerations(); } // This gets called when the timeout has expired on a gfxTextRun virtual void NotifyExpired(gfxTextRun *aTextRun) { RemoveObject(aTextRun); - mCache.RemoveTextRun(aTextRun); + gfxTextRunWordCache::RemoveTextRun(aTextRun); delete aTextRun; } - - gfxTextRunCache mCache; }; -static TextRunCache *gTextRunCache = nsnull; - -static nsresult -UpdateOwnership(gfxTextRun *aTextRun, PRBool aOwned) -{ - if (!aTextRun) - return nsnull; - if (aOwned) - return gTextRunCache->AddObject(aTextRun); - if (!aTextRun->GetExpirationState()->IsTracked()) - return NS_OK; - return gTextRunCache->MarkUsed(aTextRun); -} +static TextRunExpiringCache *gTextRunCache = nsnull; gfxTextRun * -gfxGlobalTextRunCache::GetTextRun(const PRUnichar *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - gfxContext *aRefContext, - PRUint32 aAppUnitsPerDevUnit, - PRUint32 aFlags) +gfxTextRunCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxTextRunFactory::Parameters* aParams, + PRUint32 aFlags) +{ + if (!gTextRunCache) + return nsnull; + return gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup, + aParams, aFlags); +} + +gfxTextRun * +gfxTextRunCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + gfxContext *aRefContext, + PRUint32 aAppUnitsPerDevUnit, + PRUint32 aFlags) { if (!gTextRunCache) return nsnull; - PRBool owned; gfxTextRunFactory::Parameters params = { aRefContext, nsnull, nsnull, nsnull, 0, aAppUnitsPerDevUnit }; - nsAutoPtr textRun; - textRun = gTextRunCache->mCache.GetOrMakeTextRun(aText, aLength, aFontGroup, ¶ms, aFlags, &owned); - nsresult rv = UpdateOwnership(textRun, owned); - if (NS_FAILED(rv)) - return nsnull; - return textRun.forget(); + return gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup, + ¶ms, aFlags); } gfxTextRun * -gfxGlobalTextRunCache::GetTextRun(const PRUint8 *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - gfxContext *aRefContext, - PRUint32 aAppUnitsPerDevUnit, - PRUint32 aFlags) +gfxTextRunCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + gfxContext *aRefContext, + PRUint32 aAppUnitsPerDevUnit, + PRUint32 aFlags) { if (!gTextRunCache) return nsnull; - PRBool owned; gfxTextRunFactory::Parameters params = { aRefContext, nsnull, nsnull, nsnull, 0, aAppUnitsPerDevUnit - }; - nsAutoPtr textRun; - textRun = gTextRunCache->mCache.GetOrMakeTextRun(aText, aLength, aFontGroup, ¶ms, aFlags, &owned); - nsresult rv = UpdateOwnership(textRun, owned); - if (NS_FAILED(rv)) - return nsnull; - return textRun.forget(); + }; + return gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup, + ¶ms, aFlags); +} + +void +gfxTextRunCache::ReleaseTextRun(gfxTextRun *aTextRun) +{ + if (!aTextRun) + return; + if (!(aTextRun->GetFlags() & gfxTextRunWordCache::TEXT_IN_CACHE)) { + delete aTextRun; + return; + } + nsresult rv = gTextRunCache->AddObject(aTextRun); + if (NS_FAILED(rv)) { + delete aTextRun; + return; + } } nsresult -gfxGlobalTextRunCache::Init() +gfxTextRunCache::Init() { - gTextRunCache = new TextRunCache(); + gTextRunCache = new TextRunExpiringCache(); return gTextRunCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } void -gfxGlobalTextRunCache::Shutdown() +gfxTextRunCache::Shutdown() { delete gTextRunCache; gTextRunCache = nsnull; diff --git a/mozilla/gfx/thebes/src/gfxTextRunWordCache.cpp b/mozilla/gfx/thebes/src/gfxTextRunWordCache.cpp index 4ed28634c5a..59f30607940 100644 --- a/mozilla/gfx/thebes/src/gfxTextRunWordCache.cpp +++ b/mozilla/gfx/thebes/src/gfxTextRunWordCache.cpp @@ -37,6 +37,114 @@ #include "gfxTextRunWordCache.h" + +/** + * Cache individual "words" (strings delimited by white-space or white-space-like + * characters that don't involve kerning or ligatures) in textruns. + */ +class TextRunWordCache { +public: + TextRunWordCache() { + mCache.Init(100); + } + ~TextRunWordCache() { + NS_ASSERTION(mCache.Count() == 0, "Textrun cache not empty!"); + } + + /** + * Create a textrun using cached words. + * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically + * treated as invisible missing. + * @param aFlags the flags TEXT_IS_ASCII and TEXT_HAS_SURROGATES must be set + * by the caller, if applicable + * @param aIsInCache if true is returned, then RemoveTextRun must be called + * before the textrun changes or dies. + */ + gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags); + /** + * Create a textrun using cached words. + * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically + * treated as invisible missing. + * @param aFlags the flag TEXT_IS_ASCII must be set by the caller, + * if applicable + * @param aIsInCache if true is returned, then RemoveTextRun must be called + * before the textrun changes or dies. + */ + gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags); + + /** + * Remove a textrun from the cache. This must be called before aTextRun + * is deleted! The text in the textrun must still be valid. + */ + void RemoveTextRun(gfxTextRun *aTextRun); + +protected: + struct CacheHashKey { + void *mFontOrGroup; + const void *mString; + PRUint32 mLength; + PRUint32 mAppUnitsPerDevUnit; + PRUint32 mStringHash; + PRPackedBool mIsDoubleByteText; + }; + + class CacheHashEntry : public PLDHashEntryHdr { + public: + typedef const CacheHashKey &KeyType; + typedef const CacheHashKey *KeyTypePointer; + + // When constructing a new entry in the hashtable, the caller of Put() + // will fill us in. + CacheHashEntry(KeyTypePointer aKey) : mTextRun(nsnull), mWordOffset(0), + mHashedByFont(PR_FALSE) { } + CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); } + ~CacheHashEntry() { } + + PRBool KeyEquals(const KeyTypePointer aKey) const; + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + static PLDHashNumber HashKey(const KeyTypePointer aKey); + enum { ALLOW_MEMMOVE = PR_TRUE }; + + gfxTextRun *mTextRun; + // The offset of the start of the word in the textrun. The length of + // the word is not stored here because we can figure it out by + // looking at the textrun's text. + PRUint32 mWordOffset:31; + // This is set to true when the cache entry was hashed by the first + // font in mTextRun's fontgroup; it's false when the cache entry + // was hashed by the fontgroup itself. + PRUint32 mHashedByFont:1; + }; + + // Used to track words that should be copied from one textrun to + // another during the textrun construction process + struct DeferredWord { + gfxTextRun *mSourceTextRun; + PRUint32 mSourceOffset; + PRUint32 mDestOffset; + PRUint32 mLength; + PRUint32 mHash; + }; + + PRBool LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont, + PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash, + nsTArray* aDeferredWords); + void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun, + gfxContext *aContext, + const nsTArray& aDeferredWords, + PRBool aSuccessful); + void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart, + PRUint32 aEnd, PRUint32 aHash); + + nsTHashtable mCache; +}; + static PRLogModuleInfo *gWordCacheLog = PR_NewLogModule("wordCache"); static inline PRUint32 @@ -79,7 +187,7 @@ IsBoundarySpace(PRUnichar aChar) static PRBool IsWordBoundary(PRUnichar aChar) { - return IsBoundarySpace(aChar) || gfxFontGroup::IsInvisibleChar(aChar); + return IsBoundarySpace(aChar) || gfxFontGroup::IsInvalidChar(aChar); } /** @@ -104,9 +212,9 @@ IsWordBoundary(PRUnichar aChar) * @return true if the word was found in the cache, false otherwise. */ PRBool -gfxTextRunWordCache::LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont, - PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash, - nsTArray* aDeferredWords) +TextRunWordCache::LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont, + PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash, + nsTArray* aDeferredWords) { if (aEnd <= aStart) return PR_TRUE; @@ -172,11 +280,13 @@ gfxTextRunWordCache::LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont, * entries. */ void -gfxTextRunWordCache::FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun, - gfxContext *aContext, - const nsTArray& aDeferredWords, - PRBool aSuccessful) +TextRunWordCache::FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun, + gfxContext *aContext, + const nsTArray& aDeferredWords, + PRBool aSuccessful) { + aTextRun->SetFlagBits(gfxTextRunWordCache::TEXT_IN_CACHE); + PRUint32 i; gfxFontGroup *fontGroup = aTextRun->GetFontGroup(); gfxFont *font = fontGroup->GetFontAt(0); @@ -241,10 +351,10 @@ gfxTextRunWordCache::FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun, } gfxTextRun * -gfxTextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aIsInCache) +TextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags) { nsAutoPtr textRun; textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags); @@ -298,10 +408,8 @@ gfxTextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength, // We got everything from the cache, so we're done. No point in calling // FinishTextRun. // This textrun is not referenced by the cache. - *aIsInCache = PR_FALSE; return textRun.forget(); } - *aIsInCache = PR_TRUE; // create textrun for unknown words gfxTextRunFactory::Parameters params = @@ -315,10 +423,10 @@ gfxTextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength, } gfxTextRun * -gfxTextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength, - gfxFontGroup *aFontGroup, - const gfxFontGroup::Parameters *aParams, - PRUint32 aFlags, PRBool *aIsInCache) +TextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags) { aFlags |= gfxTextRunFactory::TEXT_IS_8BIT; nsAutoPtr textRun; @@ -373,10 +481,8 @@ gfxTextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength, // We got everything from the cache, so we're done. No point in calling // FinishTextRun. // This textrun is not referenced by the cache. - *aIsInCache = PR_FALSE; return textRun.forget(); } - *aIsInCache = PR_TRUE; // create textrun for unknown words gfxTextRunFactory::Parameters params = @@ -390,8 +496,8 @@ gfxTextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength, } void -gfxTextRunWordCache::RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart, - PRUint32 aEnd, PRUint32 aHash) +TextRunWordCache::RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart, + PRUint32 aEnd, PRUint32 aHash) { if (aEnd <= aStart) return; @@ -414,7 +520,7 @@ gfxTextRunWordCache::RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart, // Remove a textrun from the cache by looking up each word and removing it void -gfxTextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun) +TextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun) { PRUint32 i; PRUint32 wordStart = 0; @@ -464,7 +570,7 @@ GetFontOrGroup(gfxFontGroup *aFontGroup, PRBool aUseFont) } PRBool -gfxTextRunWordCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const +TextRunWordCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const { if (!mTextRun) return PR_FALSE; @@ -492,8 +598,54 @@ gfxTextRunWordCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const } PLDHashNumber -gfxTextRunWordCache::CacheHashEntry::HashKey(const KeyTypePointer aKey) +TextRunWordCache::CacheHashEntry::HashKey(const KeyTypePointer aKey) { return aKey->mStringHash + (long)aKey->mFontOrGroup + aKey->mAppUnitsPerDevUnit + aKey->mIsDoubleByteText; } + +static TextRunWordCache *gTextRunWordCache = nsnull; + +nsresult +gfxTextRunWordCache::Init() +{ + gTextRunWordCache = new TextRunWordCache(); + return gTextRunWordCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +void +gfxTextRunWordCache::Shutdown() +{ + delete gTextRunWordCache; + gTextRunWordCache = nsnull; +} + +gfxTextRun * +gfxTextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags) +{ + if (!gTextRunWordCache) + return nsnull; + return gTextRunWordCache->MakeTextRun(aText, aLength, aFontGroup, aParams, aFlags); +} + +gfxTextRun * +gfxTextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength, + gfxFontGroup *aFontGroup, + const gfxFontGroup::Parameters *aParams, + PRUint32 aFlags) +{ + if (!gTextRunWordCache) + return nsnull; + return gTextRunWordCache->MakeTextRun(aText, aLength, aFontGroup, aParams, aFlags); +} + +void +gfxTextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun) +{ + if (!gTextRunWordCache) + return; + gTextRunWordCache->RemoveTextRun(aTextRun); +} diff --git a/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp b/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp index b2b0162561a..f3430145cb1 100644 --- a/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp +++ b/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp @@ -645,9 +645,9 @@ SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC, lastWidth = partialWidthArray[i]; PRInt32 advanceAppUnits = advancePixels*appUnitsPerDevPixel; WCHAR glyph = aGlyphs[i]; - if (gfxFontGroup::IsInvisibleChar(aRun->GetChar(i))) { - aRun->SetCharacterGlyph(i, g.SetMissing()); - } else if (advanceAppUnits >= 0 && + NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aRun->GetChar(i)), + "Invalid character detected!"); + if (advanceAppUnits >= 0 && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { aRun->SetCharacterGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph)); diff --git a/mozilla/gfx/thebes/test/gfxFontSelectionTest.cpp b/mozilla/gfx/thebes/test/gfxFontSelectionTest.cpp index 3978f315c07..6e68d079c76 100644 --- a/mozilla/gfx/thebes/test/gfxFontSelectionTest.cpp +++ b/mozilla/gfx/thebes/test/gfxFontSelectionTest.cpp @@ -299,16 +299,15 @@ RunTest (TestEntry *test, gfxContext *ctx) { flags |= gfxTextRunFactory::TEXT_IS_RTL; } PRUint32 length; - PRBool isInCache; if (test->stringType == S_ASCII) { flags |= gfxTextRunFactory::TEXT_IS_ASCII | gfxTextRunFactory::TEXT_IS_8BIT; length = strlen(test->string); - textRun = gTextRunCache->MakeTextRun(NS_REINTERPRET_CAST(PRUint8*, test->string), length, fontGroup, ¶ms, flags, &isInCache); + textRun = gfxTextRunWordCache::MakeTextRun(NS_REINTERPRET_CAST(PRUint8*, test->string), length, fontGroup, ¶ms, flags); } else { flags |= gfxTextRunFactory::TEXT_HAS_SURROGATES; // just in case NS_ConvertUTF8toUTF16 str(nsDependentCString(test->string)); length = str.Length(); - textRun = gTextRunCache->MakeTextRun(str.get(), length, fontGroup, ¶ms, flags, &isInCache); + textRun = gfxTextRunWordCache::MakeTextRun(str.get(), length, fontGroup, ¶ms, flags); } gfxFontTestStore::NewStore(); diff --git a/mozilla/gfx/thebes/test/gfxWordCacheTest.cpp b/mozilla/gfx/thebes/test/gfxWordCacheTest.cpp index a556831ad1b..3307f37b788 100644 --- a/mozilla/gfx/thebes/test/gfxWordCacheTest.cpp +++ b/mozilla/gfx/thebes/test/gfxWordCacheTest.cpp @@ -82,7 +82,7 @@ public: if (aTextRun->GetExpirationState()->IsTracked()) { RemoveObject(aTextRun); } - mCache.RemoveTextRun(aTextRun); + gfxTextRunWordCache::RemoveTextRun(aTextRun); } // This gets called when the timeout has expired on a gfxTextRun @@ -90,8 +90,6 @@ public: RemoveFromCache(aTextRun); delete aTextRun; } - - gfxTextRunWordCache mCache; }; static gfxTextRun * @@ -105,11 +103,8 @@ MakeTextRun(const PRUnichar *aText, PRUint32 aLength, } else if (aLength == 1 && aText[0] == ' ') { textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags); } else { - PRBool isInCache; - textRun = gTextRuns->mCache.MakeTextRun(aText, aLength, aFontGroup, - aParams, aFlags, &isInCache); - if (!isInCache && textRun) { - } + textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup, + aParams, aFlags); } if (!textRun) return nsnull; @@ -181,6 +176,7 @@ main (int argc, char **argv) { nsDependentCString cStr(cString); NS_ConvertUTF8toUTF16 str(cStr); gfxTextRun *tr = MakeTextRun(str.get(), str.Length(), fontGroup, ¶ms, flags); + tr->GetAdvanceWidth(0, str.Length(), nsnull); // Now try to trigger an assertion with a word cache bug. The first // word is in the cache so it gets added to the new textrun directly. @@ -189,7 +185,6 @@ main (int argc, char **argv) { nsDependentCString cStr2(cString2); NS_ConvertUTF8toUTF16 str2(cStr2); gfxTextRun *tr2 = MakeTextRun(str2.get(), str2.Length(), fontGroup, ¶ms, flags); - tr2->GetAdvanceWidth(0, str2.Length(), nsnull); } diff --git a/mozilla/layout/generic/nsTextFrameThebes.cpp b/mozilla/layout/generic/nsTextFrameThebes.cpp index ebda3aad4ef..03e60125f61 100644 --- a/mozilla/layout/generic/nsTextFrameThebes.cpp +++ b/mozilla/layout/generic/nsTextFrameThebes.cpp @@ -138,16 +138,10 @@ // This bit is set on frames that trimmed trailing whitespace characters when // calculating their width during reflow. #define TEXT_TRIMMED_TRAILING_WHITESPACE 0x01000000 -// Set when the frame's text may have a different style from its in-flow -// brethren (e.g. the frame is in first-letter or first-line), so text runs -// may need to be reconstructed when the frame's content offset/length changes. -// We set this on the frame that has in first-letter or first-line, but not -// in-flow siblings outside first-letter or first-line. -#define TEXT_RUN_LAYOUT_DEPENDENT 0x02000000 #define TEXT_REFLOW_FLAGS \ (TEXT_FIRST_LETTER|TEXT_START_OF_LINE|TEXT_END_OF_LINE|TEXT_HYPHEN_BREAK| \ - TEXT_TRIMMED_TRAILING_WHITESPACE|TEXT_RUN_LAYOUT_DEPENDENT) + TEXT_TRIMMED_TRAILING_WHITESPACE) // Cache bits for IsEmpty(). // Set this bit if the textframe is known to be only collapsible whitespace. @@ -504,6 +498,15 @@ public: PRInt32 GetContentLength() const { return mContentLength; } PRInt32 GetContentEnd() const { return mContentOffset + mContentLength; } + /** + * Fix up the content offsets for all next-in-flows so that they do not + * overlap this frame's content. + * @param aClearTextRuns if true, then any next-in-flows whose content + * offsets changed have their textruns cleared (as would be necessary + * if this frame could have a different style to those frames) + */ + void AdjustNextInFlowContentOffsetsForGrowth(PRBool aClearTextRuns); + // Compute the length of the content mapped by this frame // and all its in-flow siblings. Basically this means starting at mContentOffset // and going to the end of the text node or the next bidi continuation @@ -559,8 +562,6 @@ protected: PRBool isRTLChars, PRBool isOddLevel, PRBool isBidiSystem); - - void SetOffsets(PRInt32 start, PRInt32 end); }; static void @@ -632,8 +633,8 @@ public: if (aTextRun->GetExpirationState()->IsTracked()) { RemoveObject(aTextRun); } - if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_UNCACHED)) { - mCache.RemoveTextRun(aTextRun); + if (aTextRun->GetFlags() & gfxTextRunWordCache::TEXT_IN_CACHE) { + gfxTextRunWordCache::RemoveTextRun(aTextRun); } } @@ -643,8 +644,6 @@ public: RemoveFromCache(aTextRun); delete aTextRun; } - - gfxTextRunWordCache mCache; }; static gfxTextRun * @@ -654,16 +653,12 @@ MakeTextRun(const PRUnichar *aText, PRUint32 aLength, { nsAutoPtr textRun; if (aLength == 0) { - textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED); + textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags); } else if (aLength == 1 && aText[0] == ' ') { - textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED); + textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags); } else { - PRBool isInCache; - textRun = gTextRuns->mCache.MakeTextRun(aText, aLength, aFontGroup, - aParams, aFlags, &isInCache); - if (!isInCache && textRun) { - textRun->SetFlagBits(nsTextFrameUtils::TEXT_IS_UNCACHED); - } + textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup, + aParams, aFlags); } if (!textRun) return nsnull; @@ -682,16 +677,12 @@ MakeTextRun(const PRUint8 *aText, PRUint32 aLength, { nsAutoPtr textRun; if (aLength == 0) { - textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED); + textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags); } else if (aLength == 1 && aText[0] == ' ') { - textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED); + textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags); } else { - PRBool isInCache; - textRun = gTextRuns->mCache.MakeTextRun(aText, aLength, aFontGroup, - aParams, aFlags, &isInCache); - if (!isInCache && textRun) { - textRun->SetFlagBits(nsTextFrameUtils::TEXT_IS_UNCACHED); - } + textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup, + aParams, aFlags); } if (!textRun) return nsnull; @@ -1350,13 +1341,9 @@ void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame) !HasTerminalNewline(mLastFrame)) { nsTextFrame* frame = NS_STATIC_CAST(nsTextFrame*, aFrame); mappedFlow->mEndFrame = NS_STATIC_CAST(nsTextFrame*, frame->GetNextInFlow()); - // Frames in the same flow can overlap at least temporarily - // (e.g. when first-line builds its textrun, we need to have it suck - // up all the in-flow content because we don't know how long the line - // is going to be). - mappedFlow->mContentEndOffset = - PR_MAX(mappedFlow->mContentEndOffset, - frame->GetContentOffset() + frame->GetContentLength()); + NS_ASSERTION(mappedFlow->mContentEndOffset <= frame->GetContentOffset(), + "frame offsets overlap!"); + mappedFlow->mContentEndOffset = frame->GetContentEnd(); AccumulateRunInfo(frame); return; } @@ -1379,8 +1366,8 @@ void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame) mappedFlow->mEndFrame = NS_STATIC_CAST(nsTextFrame*, frame->GetNextInFlow()); mappedFlow->mAncestorControllingInitialBreak = mCommonAncestorWithLastFrame; mappedFlow->mContentOffset = frame->GetContentOffset(); - mappedFlow->mContentEndOffset = - frame->GetContentOffset() + frame->GetContentLength(); + mappedFlow->mContentEndOffset = frame->GetContentEnd(); + // This is temporary: it's overwritten in BuildTextRunForFrames mappedFlow->mTransformedTextOffset = 0; mLastFrame = frame; @@ -1476,15 +1463,15 @@ GetHyphenTextRun(gfxTextRun* aTextRun, nsIRenderingContext* aRefContext) static const PRUnichar unicodeHyphen = 0x2010; gfxTextRun* textRun = - gfxGlobalTextRunCache::GetTextRun(&unicodeHyphen, 1, fontGroup, ctx, - aTextRun->GetAppUnitsPerDevUnit(), flags); + gfxTextRunCache::MakeTextRun(&unicodeHyphen, 1, fontGroup, ctx, + aTextRun->GetAppUnitsPerDevUnit(), flags); if (textRun && textRun->CountMissingGlyphs() == 0) return textRun; static const PRUint8 dash = '-'; - return gfxGlobalTextRunCache::GetTextRun(&dash, 1, fontGroup, ctx, - aTextRun->GetAppUnitsPerDevUnit(), - flags); + return gfxTextRunCache::MakeTextRun(&dash, 1, fontGroup, ctx, + aTextRun->GetAppUnitsPerDevUnit(), + flags); } static gfxFont::Metrics @@ -1574,12 +1561,8 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) PRInt32 contentLength = contentEnd - contentStart; if (content == lastContent) { - NS_ASSERTION(endOfLastContent >= contentStart, - "Gap in textframes mapping content?!"); - // Text frames can overlap (see comment in ScanFrame below) - contentStart = PR_MAX(contentStart, endOfLastContent); - if (contentStart >= contentEnd) - continue; + NS_ASSERTION(endOfLastContent == contentStart, + "Gap in textframes mapping content, or overlap?!"); userData->mMappedFlows[finalMappedFlowCount - 1].mContentLength += contentLength; } else { TextRunMappedFlow* newFlow = &userData->mMappedFlows[finalMappedFlowCount]; @@ -1711,7 +1694,6 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) styles.AppendElement(sc); } } - textFlags |= nsTextFrameUtils::TEXT_IS_UNCACHED; } if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) { @@ -3283,18 +3265,18 @@ nsContinuingTextFrame::Init(nsIContent* aContent, // NOTE: bypassing nsTextFrame::Init!!! nsresult rv = nsFrame::Init(aContent, aParent, aPrevInFlow); - nsIFrame* nextContinuation = aPrevInFlow->GetNextContinuation(); // Hook the frame into the flow - SetPrevInFlow(aPrevInFlow); - aPrevInFlow->SetNextInFlow(this); nsTextFrame* prev = NS_STATIC_CAST(nsTextFrame*, aPrevInFlow); - mTextRun = prev->GetTextRun(); - mContentOffset = prev->GetContentOffset() + prev->GetContentLength(); -#ifdef IBMBIDI - if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) { - PRInt32 start, end; - aPrevInFlow->GetOffsets(start, mContentOffset); + nsTextFrame* nextContinuation = NS_STATIC_CAST(nsTextFrame*, + prev->GetNextContinuation()); + SetPrevInFlow(prev); + prev->SetNextInFlow(this); + mTextRun = prev->GetTextRun(); + mContentOffset = prev->GetContentEnd(); + mContentLength = mContent->TextLength() - mContentOffset; +#ifdef IBMBIDI + if (prev->GetStateBits() & NS_FRAME_IS_BIDI) { nsPropertyTable *propTable = PresContext()->PropertyTable(); propTable->SetProperty(this, nsGkAtoms::embeddingLevel, propTable->GetProperty(aPrevInFlow, nsGkAtoms::embeddingLevel), @@ -3305,11 +3287,13 @@ nsContinuingTextFrame::Init(nsIContent* aContent, propTable->SetProperty(this, nsGkAtoms::charType, propTable->GetProperty(aPrevInFlow, nsGkAtoms::charType), nsnull, nsnull); + if (nextContinuation) { SetNextContinuation(nextContinuation); nextContinuation->SetPrevContinuation(this); - nextContinuation->GetOffsets(start, end); - mContentLength = PR_MAX(1, start - mContentOffset); + NS_ASSERTION(mContentOffset <= nextContinuation->GetContentOffset(), + "Overlapping text ranges!"); + mContentLength = nextContinuation->GetContentOffset() - mContentOffset; } mState |= NS_FRAME_IS_BIDI; } // prev frame is bidi @@ -3516,7 +3500,7 @@ nsTextFrame::ClearTextRun() // return; // } - if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_UNCACHED) { + if (!(textRun->GetFlags() & gfxTextRunWordCache::TEXT_IN_CACHE)) { // Remove it now because it's not doing anything useful gTextRuns->RemoveFromCache(textRun); delete textRun; @@ -3674,8 +3658,8 @@ nsTextFrame::GetSelectionDetails() SelectionDetails* sd; for (sd = details; sd; sd = sd->mNext) { // The entire text is selected! - sd->mStart = mContentOffset; - sd->mEnd = mContentOffset + mContentLength; + sd->mStart = GetContentOffset(); + sd->mEnd = GetContentEnd(); } return details; } @@ -4281,8 +4265,8 @@ nsTextFrame::IsVisibleInSelection(nsISelection* aSelection) // where are the selection points "really" SelectionDetails *sdptr = details; while (sdptr) { - if (sdptr->mEnd > mContentOffset && - sdptr->mStart < mContentOffset + mContentLength && + if (sdptr->mEnd > GetContentOffset() && + sdptr->mStart < GetContentEnd() && sdptr->mType == nsISelectionController::SELECTION_NORMAL) { found = PR_TRUE; break; @@ -4409,7 +4393,7 @@ nsTextFrame::SetSelected(nsPresContext* aPresContext, if (thisNode == startNode) { - if ((mContentOffset + mContentLength) >= startOffset) + if (GetContentEnd() >= startOffset) { found = PR_TRUE; if (thisNode == endNode) @@ -4501,12 +4485,12 @@ nsTextFrame::GetPointFromOffset(nsPresContext* aPresContext, // place if it's positioned there properties.InitializeForDisplay(PR_FALSE); - if (inOffset < mContentOffset){ + if (inOffset < GetContentOffset()){ NS_WARNING("offset before this frame's content"); - inOffset = mContentOffset; - } else if (inOffset > mContentOffset + mContentLength) { + inOffset = GetContentOffset(); + } else if (inOffset > GetContentEnd()) { NS_WARNING("offset after this frame's content"); - inOffset = mContentOffset + mContentLength; + inOffset = GetContentEnd(); } PRInt32 trimmedOffset = properties.GetStart().GetOriginalOffset(); PRInt32 trimmedEnd = trimmedOffset + properties.GetOriginalLength(); @@ -4859,17 +4843,24 @@ nsTextFrame::CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, NS_IMETHODIMP nsTextFrame::GetOffsets(PRInt32 &start, PRInt32 &end) const { - start = mContentOffset; - end = mContentOffset+mContentLength; + start = GetContentOffset(); + end = GetContentEnd(); return NS_OK; } -// returns PR_TRUE if this text frame completes the first-letter, PR_FALSE -// if it does not contain a true "letter" -// if returns PR_TRUE, then it also updates aLength to cover just the first-letter -// text -// XXX :first-letter should be handled during frame construction -// (and it has a good bit in common with nextBidi) +/** + * Returns PR_TRUE if this text frame completes the first-letter, PR_FALSE + * if it does not contain a true "letter". + * If returns PR_TRUE, then it also updates aLength to cover just the first-letter + * text. + * + * XXX :first-letter should be handled during frame construction + * (and it has a good bit in common with nextBidi) + * + * @param aLength an in/out parameter: on entry contains the maximum length to + * return, on exit returns length of the first-letter fragment (which may + * include leading punctuation, for example) + */ static PRBool FindFirstLetterRange(const nsTextFragment* aFrag, gfxTextRun* aTextRun, @@ -5170,6 +5161,26 @@ HasSoftHyphenBefore(const nsTextFragment* aFrag, gfxTextRun* aTextRun, return PR_FALSE; } +void +nsTextFrame::AdjustNextInFlowContentOffsetsForGrowth(PRBool aClearTextRuns) +{ + PRInt32 end = GetContentEnd(); + nsTextFrame* f = this; + NS_ASSERTION(!GetNextInFlow() || + NS_STATIC_CAST(nsTextFrame*, GetNextInFlow())->GetContentOffset() <= GetContentEnd(), + "We shrunk, this should not be called"); + while (PR_TRUE) { + f = NS_STATIC_CAST(nsTextFrame*, f->GetNextInFlow()); + if (!f || f->GetContentOffset() >= end) + break; + f->mContentLength = PR_MAX(end, f->GetContentEnd()) - end; + f->mContentOffset = end; + if (aClearTextRuns) { + f->ClearTextRun(); + } + } +} + NS_IMETHODIMP nsTextFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, @@ -5193,25 +5204,16 @@ nsTextFrame::Reflow(nsPresContext* aPresContext, // can change whether the frame maps whitespace-only text or not. RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS); - nsTextFrame* prevInFlow = NS_STATIC_CAST(nsTextFrame*, GetPrevInFlow()); - if (prevInFlow) { - // Our mContentOffset may be out of date due to our prev-in-flow growing or - // shrinking. Update it. - mContentOffset = prevInFlow->GetContentOffset() + prevInFlow->GetContentLength(); - } - - // Temporarily map all possible content while we construct our new textrun. - // so that when doing reflow our styles prevail over any part of the - // textrun we look at. Note that next-in-flows may be mapping the same - // content; gfxTextRun construction logic will ensure that we take priority. + NS_ASSERTION(!GetPrevInFlow() || + NS_STATIC_CAST(nsTextFrame*, GetPrevInFlow())->GetContentEnd() == mContentOffset, + "Discontinuous content offsets!"); PRInt32 maxContentLength = GetInFlowContentLength(); - mContentLength = maxContentLength; // XXX If there's no line layout, we shouldn't even have created this // frame. This may happen if, for example, this is text inside a table // but not inside a cell. For now, just don't reflow. We also don't need to // reflow if there is no content. - if (!aReflowState.mLineLayout || !mContentLength) { + if (!aReflowState.mLineLayout || !maxContentLength) { ClearMetrics(aMetrics); aStatus = NS_FRAME_COMPLETE; return NS_OK; @@ -5250,11 +5252,15 @@ nsTextFrame::Reflow(nsPresContext* aPresContext, PRBool layoutDependentTextRun = lineLayout.GetFirstLetterStyleOK() || lineLayout.GetInFirstLine(); if (layoutDependentTextRun) { - AddStateBits(TEXT_RUN_LAYOUT_DEPENDENT); - } - if (layoutDependentTextRun || - (prevInFlow && (prevInFlow->GetStateBits() & TEXT_RUN_LAYOUT_DEPENDENT))) { ClearTextRun(); + // Temporarily map all possible content while we construct our new textrun. + // so that when doing reflow our styles prevail over any part of the + // textrun we look at. + mContentLength = maxContentLength; + // The following loop is going to traverse all in-flow frames, which could + // be kinda slow, but we're going to have to rebuild all their text runs + // anyway so this shouldn't be any worse + AdjustNextInFlowContentOffsetsForGrowth(PR_TRUE); } PRUint32 flowEndInTextRun; @@ -5273,7 +5279,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext, // DOM offsets of the text range we need to measure, after trimming // whitespace, restricting to first-letter, and restricting preformatted text // to nearest newline - PRInt32 length = mContentLength; + PRInt32 length = maxContentLength; PRInt32 offset = mContentOffset; // Restrict preformatted text to the nearest newline @@ -5428,6 +5434,27 @@ nsTextFrame::Reflow(nsPresContext* aPresContext, } mContentLength = offset + charsFit - mContentOffset; + // Now fix up content offsets/lengths for in-flows + nsTextFrame* f = NS_STATIC_CAST(nsTextFrame*, GetNextInFlow()); + if (f) { + if (f->GetContentOffset() > GetContentEnd()) { + // We must have shrunk. Add the leftover text to the start of f. + f->mContentLength = f->GetContentEnd() - GetContentEnd(); + f->mContentOffset = GetContentEnd(); + if (layoutDependentTextRun) { + // f's textrun may need to change since the text style may be + // different. + f->ClearTextRun(); + } + } else if (f->GetContentOffset() < GetContentEnd()) { + // We must have grown. Remove the text from f and possibly its + // continuations. + NS_ASSERTION(!layoutDependentTextRun, + "We should have grown up above and be shrinking here!"); + AdjustNextInFlowContentOffsetsForGrowth(layoutDependentTextRun); + } + } + ///////////////////////////////////////////////////////////////////// // Compute output metrics ///////////////////////////////////////////////////////////////////// @@ -5595,9 +5622,9 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext, if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) { aLastCharIsJustifiable = PR_TRUE; - } else if (trimmed.mStart + trimmed.mLength < mContentOffset + mContentLength) { + } else if (trimmed.mStart + trimmed.mLength < GetContentEnd()) { gfxSkipCharsIterator end = iter; - PRUint32 endOffset = end.ConvertOriginalToSkipped(mContentOffset + mContentLength); + PRUint32 endOffset = end.ConvertOriginalToSkipped(GetContentEnd()); if (trimmedEnd < endOffset) { // We can't be dealing with tabs here ... they wouldn't be trimmed. So it's // OK to pass null for the line container. @@ -5763,7 +5790,7 @@ nsTextFrame::List(FILE* out, PRInt32 aIndent) const ToCString(tmp, &totalContentLength); // Output the first/last content offset and prev/next in flow info - PRBool isComplete = (mContentOffset + mContentLength) == totalContentLength; + PRBool isComplete = GetContentEnd() == totalContentLength; fprintf(out, "[%d,%d,%c] ", mContentOffset, mContentLength, isComplete ? 'T':'F'); @@ -5872,20 +5899,24 @@ void nsTextFrame::AdjustOffsetsForBidi(PRInt32 aStart, PRInt32 aEnd) { AddStateBits(NS_FRAME_IS_BIDI); - SetOffsets(aStart, aEnd); - /* - * After Bidi resolution we may need to reassign text runs. - * This is called during bidi resolution from the block container, so we - * shouldn't be holding a local reference to a textrun anywhere. - */ - ClearTextRunsInFlowChain(this); -} - -void -nsTextFrame::SetOffsets(PRInt32 aStart, PRInt32 aEnd) -{ mContentOffset = aStart; mContentLength = aEnd - aStart; + nsTextFrame* f = this; + // We were just assigned all the content for this in-flow run, so unmap all + // next-in-flows + while (PR_TRUE) { + /* + * After Bidi resolution we may need to reassign text runs. + * This is called during bidi resolution from the block container, so we + * shouldn't be holding a local reference to a textrun anywhere. + */ + f->ClearTextRun(); + f = NS_STATIC_CAST(nsTextFrame*, f->GetNextInFlow()); + if (!f) + break; + f->mContentOffset = aEnd; + f->mContentLength = 0; + } } /** diff --git a/mozilla/layout/generic/nsTextFrameUtils.h b/mozilla/layout/generic/nsTextFrameUtils.h index f94aaea3427..86ce8e8ddb1 100644 --- a/mozilla/layout/generic/nsTextFrameUtils.h +++ b/mozilla/layout/generic/nsTextFrameUtils.h @@ -72,8 +72,7 @@ public: TEXT_INCOMING_WHITESPACE = 0x200000, TEXT_TRAILING_WHITESPACE = 0x400000, TEXT_COMPRESSED_LEADING_WHITESPACE = 0x800000, - TEXT_IS_UNCACHED = 0x1000000, - TEXT_NO_BREAKS = 0x2000000 + TEXT_NO_BREAKS = 0x1000000 }; static PRBool diff --git a/mozilla/layout/generic/nsTextRunTransformations.cpp b/mozilla/layout/generic/nsTextRunTransformations.cpp index ed4399659be..29885a4fb0e 100644 --- a/mozilla/layout/generic/nsTextRunTransformations.cpp +++ b/mozilla/layout/generic/nsTextRunTransformations.cpp @@ -188,41 +188,6 @@ CountGlyphs(const gfxTextRun::DetailedGlyph* aDetails) { return glyphCount + 1; } -/** - * Concatenate textruns together just by copying their glyphrun data - */ -static void -AppendTextRun(gfxTextRun* aDest, gfxTextRun* aSrc, PRUint32 aOffset) -{ - PRUint32 numGlyphRuns; - const gfxTextRun::GlyphRun* glyphRuns = aSrc->GetGlyphRuns(&numGlyphRuns); - PRUint32 j; - PRUint32 offset = aOffset; - for (j = 0; j < numGlyphRuns; ++j) { - PRUint32 runOffset = glyphRuns[j].mCharacterOffset; - PRUint32 len = - (j + 1 < numGlyphRuns ? glyphRuns[j + 1].mCharacterOffset : aSrc->GetLength()) - - runOffset; - nsresult rv = aDest->AddGlyphRun(glyphRuns[j].mFont, offset); - if (NS_FAILED(rv)) - return; - - PRUint32 k; - for (k = 0; k < len; ++k) { - gfxTextRun::CompressedGlyph g = aSrc->GetCharacterGlyphs()[runOffset + k]; - if (g.IsComplexCluster()) { - const gfxTextRun::DetailedGlyph* details = aSrc->GetDetailedGlyphs(runOffset + k); - aDest->SetDetailedGlyphs(offset, details, CountGlyphs(details)); - } else { - aDest->SetCharacterGlyph(offset, g); - } - ++offset; - } - } - NS_ASSERTION(offset - aOffset == aSrc->GetLength(), - "Something went wrong in our length calculations..."); -} - /** * Copy a given textrun, but merge certain characters into a single logical * character. Glyphs for a character are added to the glyph list for the previous @@ -317,7 +282,7 @@ GetParametersForInner(nsTransformedTextRun* aTextRun, PRUint32* aFlags, { gfxTextRunFactory::Parameters params = { aRefContext, nsnull, nsnull, - nsnull, nsnull, PRUint32(aTextRun->GetAppUnitsPerDevUnit()) + nsnull, nsnull, aTextRun->GetAppUnitsPerDevUnit() }; *aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT; return params; @@ -341,17 +306,14 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, PRUint32 flags; gfxTextRunFactory::Parameters innerParams = GetParametersForInner(aTextRun, &flags, aRefContext); - // The text outlives the child and inner textruns - flags |= gfxFontGroup::TEXT_IS_PERSISTENT; PRUint32 length = aTextRun->GetLength(); const PRUnichar* str = aTextRun->GetTextUnicode(); nsRefPtr* styles = aTextRun->mStyles.Elements(); // Create a textrun so we can check cluster-start properties - nsAutoPtr inner; - // This text is going to outlive the inner text run - inner = fontGroup->MakeTextRun(str, length, &innerParams, flags); - if (!inner) + gfxTextRunCache::AutoTextRun inner( + gfxTextRunCache::MakeTextRun(str, length, fontGroup, &innerParams, flags)); + if (!inner.get()) return; nsCaseTransformTextRunFactory uppercaseFactory(nsnull, PR_TRUE); @@ -392,16 +354,21 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, } if ((i == length || runIsLowercase != isLowercase) && runStart < i) { - nsAutoPtr child; + nsAutoPtr transformedChild; + gfxTextRunCache::AutoTextRun cachedChild; + gfxTextRun* child; // Setup actual line break data for child (which may affect shaping) innerParams.mInitialBreaks = lineBreakBeforeArray.Elements(); innerParams.mInitialBreakCount = lineBreakBeforeArray.Length(); if (runIsLowercase) { - child = uppercaseFactory.MakeTextRun(str + runStart, i - runStart, + transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart, &innerParams, smallFont, flags, styleArray.Elements(), PR_FALSE); + child = transformedChild; } else { - child = fontGroup-> - MakeTextRun(str + runStart, i - runStart, &innerParams, flags); + cachedChild = + gfxTextRunCache::MakeTextRun(str + runStart, i - runStart, fontGroup, + &innerParams, flags); + child = cachedChild.get(); } if (!child) return; @@ -411,7 +378,7 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, "lost some break-before values?"); child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), canBreakBeforeArray.Elements(), aRefContext); - AppendTextRun(aTextRun, child, runStart); + aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), runStart, PR_FALSE); runStart = i; styleArray.Clear(); @@ -518,20 +485,22 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, GetParametersForInner(aTextRun, &flags, aRefContext); gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); - nsAutoPtr child; + nsAutoPtr transformedChild; + gfxTextRunCache::AutoTextRun cachedChild; + gfxTextRun* child; // Setup actual line break data for child (which may affect shaping) innerParams.mInitialBreaks = lineBreakBeforeArray.Elements(); innerParams.mInitialBreakCount = lineBreakBeforeArray.Length(); - // The text outlives 'child' - flags |= gfxFontGroup::TEXT_IS_PERSISTENT; if (mInnerTransformingTextRunFactory) { - child = mInnerTransformingTextRunFactory->MakeTextRun( + transformedChild = mInnerTransformingTextRunFactory->MakeTextRun( convertedString.BeginReading(), convertedString.Length(), &innerParams, fontGroup, flags, styleArray.Elements(), PR_FALSE); + child = transformedChild.get(); } else { - child = fontGroup->MakeTextRun( - convertedString.BeginReading(), convertedString.Length(), &innerParams, - flags); + cachedChild = gfxTextRunCache::MakeTextRun( + convertedString.BeginReading(), convertedString.Length(), fontGroup, + &innerParams, flags); + child = cachedChild.get(); } if (!child) return; diff --git a/mozilla/layout/svg/base/src/nsSVGGlyphFrame.cpp b/mozilla/layout/svg/base/src/nsSVGGlyphFrame.cpp index 1c29fc377ee..3a1313dd088 100644 --- a/mozilla/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/mozilla/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -54,6 +54,7 @@ #include "gfxContext.h" #include "gfxMatrix.h" #include "gfxPlatform.h" +#include "gfxTextRunCache.h" // XXX: This initial straightforward conversion from accessing cairo // directly to Thebes doesn't handle clusters. Pretty much all code @@ -215,9 +216,9 @@ nsSVGGlyphFrame::LoopCharacters(gfxContext *aCtx, const nsString &aText, const nsSVGCharacterPosition *aCP, FillOrStroke aFillOrStroke) { - nsAutoPtr textRun(GetTextRun(aCtx, aText)); + gfxTextRunCache::AutoTextRun textRun = GetTextRun(aCtx, aText); - if (!textRun) + if (!textRun.get()) return; if (!aCP) { @@ -606,8 +607,8 @@ nsSVGGlyphFrame::GetCharacterPosition(gfxContext *aContext, gfxFloat length = data->GetLength(); PRUint32 strLength = aText.Length(); - nsAutoPtr textRun(GetTextRun(aContext, aText)); - if (!textRun) + gfxTextRunCache::AutoTextRun textRun = GetTextRun(aContext, aText); + if (!textRun.get()) return NS_ERROR_OUT_OF_MEMORY; nsSVGCharacterPosition *cp = new nsSVGCharacterPosition[strLength]; @@ -1347,17 +1348,11 @@ nsSVGGlyphFrame::GetTextRun(gfxContext *aCtx, const nsString &aText) // References: // https://bugzilla.mozilla.org/show_bug.cgi?id=375141 // http://weblogs.mozillazine.org/roc/archives/2007/03/text_text_text.html - - gfxTextRunFactory::Parameters params = - { aCtx, nsnull, nsnull, - nsnull, nsnull, - 1 // see note above - }; - if (!mFontGroup) return nsnull; - return mFontGroup->MakeTextRun(aText.get(), aText.Length(), ¶ms, 0); + return gfxTextRunCache::MakeTextRun(aText.get(), aText.Length(), + mFontGroup, aCtx, 1, 0); } //---------------------------------------------------------------------- diff --git a/mozilla/layout/svg/base/src/nsSVGGlyphFrame.h b/mozilla/layout/svg/base/src/nsSVGGlyphFrame.h index 69a097be4a4..d87b75b384b 100644 --- a/mozilla/layout/svg/base/src/nsSVGGlyphFrame.h +++ b/mozilla/layout/svg/base/src/nsSVGGlyphFrame.h @@ -44,6 +44,7 @@ #include "nsISVGChildFrame.h" #include "gfxContext.h" #include "gfxFont.h" +#include "gfxTextRunCache.h" struct nsSVGCharacterPosition; class nsSVGTextFrame; @@ -164,7 +165,7 @@ protected: friend class nsSVGAutoGlyphHelperContext; // A helper class to deal with gfxTextRuns and temporary thebes - // contexts. It destroys them when it goes out of scope. + // contexts. class nsSVGAutoGlyphHelperContext { public: @@ -179,15 +180,16 @@ protected: nsSVGCharacterPosition **cp); gfxContext *GetContext() { return mCT; } - gfxTextRun *GetTextRun() { return mTextRun; } + gfxTextRun *GetTextRun() { return mTextRun.get(); } private: void Init(nsSVGGlyphFrame *aSource, const nsString &aText); - nsRefPtr mCT; - nsAutoPtr mTextRun; + nsRefPtr mCT; + gfxTextRunCache::AutoTextRun mTextRun; }; + // The textrun must be released via gfxTextRunCache::AutoTextRun gfxTextRun *GetTextRun(gfxContext *aCtx, const nsString &aText);