mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 385423. Refactor textrun cache so that all textrun clients use a single global word-based cache. Responsibility for stripping out problematic characters (e.g. newlines) is given to the word cache. r=vlad,smontagu
This commit is contained in:
parent
fb292fb269
commit
33428c5bf6
@ -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) {
|
||||
|
@ -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';
|
||||
}
|
||||
|
||||
|
@ -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<CacheHashEntry> 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;
|
||||
|
@ -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<DeferredWord>* aDeferredWords);
|
||||
void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
|
||||
gfxContext *aContext,
|
||||
const nsTArray<DeferredWord>& aDeferredWords,
|
||||
PRBool aSuccessful);
|
||||
void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
|
||||
PRUint32 aEnd, PRUint32 aHash);
|
||||
|
||||
nsTHashtable<CacheHashEntry> mCache;
|
||||
static nsresult Init();
|
||||
static void Shutdown();
|
||||
};
|
||||
|
||||
#endif /* GFX_TEXT_RUN_WORD_CACHE_H */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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<gfxTextRun,3> {
|
||||
class TextRunExpiringCache : public nsExpirationTracker<gfxTextRun,3> {
|
||||
public:
|
||||
enum { TIMEOUT_SECONDS = 10 };
|
||||
TextRunCache()
|
||||
: nsExpirationTracker<gfxTextRun,3>(TIMEOUT_SECONDS*1000) {}
|
||||
~TextRunCache() {
|
||||
TextRunExpiringCache()
|
||||
: nsExpirationTracker<gfxTextRun,3>(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<gfxTextRun> 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<gfxTextRun> 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;
|
||||
|
@ -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<DeferredWord>* aDeferredWords);
|
||||
void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
|
||||
gfxContext *aContext,
|
||||
const nsTArray<DeferredWord>& aDeferredWords,
|
||||
PRBool aSuccessful);
|
||||
void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
|
||||
PRUint32 aEnd, PRUint32 aHash);
|
||||
|
||||
nsTHashtable<CacheHashEntry> 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<DeferredWord>* aDeferredWords)
|
||||
TextRunWordCache::LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont,
|
||||
PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash,
|
||||
nsTArray<DeferredWord>* 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<DeferredWord>& aDeferredWords,
|
||||
PRBool aSuccessful)
|
||||
TextRunWordCache::FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
|
||||
gfxContext *aContext,
|
||||
const nsTArray<DeferredWord>& 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<gfxTextRun> 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<gfxTextRun> 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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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<gfxTextRun> 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<gfxTextRun> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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<nsStyleContext>* styles = aTextRun->mStyles.Elements();
|
||||
// Create a textrun so we can check cluster-start properties
|
||||
nsAutoPtr<gfxTextRun> 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<gfxTextRun> child;
|
||||
nsAutoPtr<gfxTextRun> 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<gfxTextRun> child;
|
||||
nsAutoPtr<gfxTextRun> 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;
|
||||
|
@ -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<gfxTextRun> 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<gfxTextRun> 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);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -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<gfxContext> mCT;
|
||||
nsAutoPtr<gfxTextRun> mTextRun;
|
||||
nsRefPtr<gfxContext> mCT;
|
||||
gfxTextRunCache::AutoTextRun mTextRun;
|
||||
};
|
||||
|
||||
// The textrun must be released via gfxTextRunCache::AutoTextRun
|
||||
gfxTextRun *GetTextRun(gfxContext *aCtx,
|
||||
const nsString &aText);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user