зеркало из https://github.com/mozilla/gecko-dev.git
Bug 384100. Implement word-based textrun cache. r=vlad
This commit is contained in:
Родитель
36c86d446a
Коммит
622f3bf11b
|
@ -25,6 +25,7 @@ EXPORTS = gfxASurface.h \
|
|||
gfxSkipChars.h \
|
||||
gfxTypes.h \
|
||||
gfxTextRunCache.h \
|
||||
gfxTextRunWordCache.h \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS += gfxFontTest.h
|
||||
|
|
|
@ -430,6 +430,10 @@ 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,
|
||||
PLATFORM_TEXT_FLAGS = 0x0000F000,
|
||||
TEXTRUN_TEXT_FLAGS = 0x00000FFF,
|
||||
|
||||
/**
|
||||
* When set, the text string pointer used to create the text run
|
||||
* is guaranteed to be available during the lifetime of the text run.
|
||||
|
@ -789,6 +793,16 @@ public:
|
|||
void *GetUserData() const { return mUserData; }
|
||||
void SetUserData(void *aUserData) { mUserData = aUserData; }
|
||||
PRUint32 GetFlags() const { return mFlags; }
|
||||
void SetFlagBits(PRUint32 aFlags) {
|
||||
NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::USER_TEXT_FLAGS),
|
||||
"Only user flags should be mutable");
|
||||
mFlags |= aFlags;
|
||||
}
|
||||
void ClearFlagBits(PRUint32 aFlags) {
|
||||
NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::USER_TEXT_FLAGS),
|
||||
"Only user flags should be mutable");
|
||||
mFlags &= ~aFlags;
|
||||
}
|
||||
const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
|
||||
PRUint32 GetAppUnitsPerDevUnit() const { return mAppUnitsPerDevUnit; }
|
||||
gfxFontGroup *GetFontGroup() const { return mFontGroup; }
|
||||
|
@ -796,6 +810,11 @@ public:
|
|||
{ return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? mText.mSingle : nsnull; }
|
||||
const PRUnichar *GetTextUnicode() const
|
||||
{ return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? nsnull : mText.mDouble; }
|
||||
const void *GetTextAt(PRUint32 aIndex) {
|
||||
return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT)
|
||||
? NS_STATIC_CAST(const void *, mText.mSingle + aIndex)
|
||||
: NS_STATIC_CAST(const void *, mText.mDouble + aIndex);
|
||||
}
|
||||
const PRUnichar GetChar(PRUint32 i) const
|
||||
{ return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? mText.mSingle[i] : mText.mDouble[i]; }
|
||||
PRUint32 GetHashCode() const { return mHashCode; }
|
||||
|
@ -1020,6 +1039,7 @@ public:
|
|||
void SetDetailedGlyphs(PRUint32 aCharIndex, const DetailedGlyph *aGlyphs,
|
||||
PRUint32 aNumGlyphs);
|
||||
void SetMissingGlyph(PRUint32 aCharIndex, PRUnichar aChar);
|
||||
void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex);
|
||||
|
||||
// API for access to the raw glyph data, needed by gfxFont::Draw
|
||||
// and gfxFont::GetBoundingBox
|
||||
|
@ -1034,6 +1054,16 @@ public:
|
|||
*aNumGlyphRuns = mGlyphRuns.Length();
|
||||
return mGlyphRuns.Elements();
|
||||
}
|
||||
// Returns the index of the GlyphRun containing the given offset.
|
||||
// Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
|
||||
PRUint32 FindFirstGlyphRunContaining(PRUint32 aOffset);
|
||||
// Copy glyph data for a range of characters from aSource to this
|
||||
// textrun. If aStealData is true then we actually steal the glyph data,
|
||||
// setting the data in aSource to "missing". aDest should be in the last
|
||||
// glyphrun.
|
||||
virtual void CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
|
||||
PRUint32 aLength, PRUint32 aDest,
|
||||
PRBool aStealData);
|
||||
|
||||
nsExpirationState *GetExpirationState() { return &mExpirationState; }
|
||||
|
||||
|
@ -1042,9 +1072,6 @@ private:
|
|||
|
||||
// Allocate aCount DetailedGlyphs for the given index
|
||||
DetailedGlyph *AllocateDetailedGlyphs(PRUint32 aCharIndex, PRUint32 aCount);
|
||||
// Returns the index of the GlyphRun containing the given offset.
|
||||
// Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
|
||||
PRUint32 FindFirstGlyphRunContaining(PRUint32 aOffset);
|
||||
// Computes the x-advance for a given cluster starting at aClusterOffset. Does
|
||||
// not include any spacing. Result is in appunits.
|
||||
PRInt32 ComputeClusterAdvance(PRUint32 aClusterOffset);
|
||||
|
@ -1140,7 +1167,8 @@ public:
|
|||
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) = 0;
|
||||
|
||||
/**
|
||||
* Tabs, CRs and LFs should be zero-width and invisible.
|
||||
* Tabs, CRs and LFs should be zero-width and invisible. They should
|
||||
* break up shaping.
|
||||
*/
|
||||
static PRBool IsInvisibleChar(PRUnichar ch) {
|
||||
return ch == '\t' || ch == '\r' || ch == '\n';
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Foundation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef GFX_TEXT_RUN_WORD_CACHE_H
|
||||
#define GFX_TEXT_RUN_WORD_CACHE_H
|
||||
|
||||
#include "gfxFont.h"
|
||||
|
||||
/**
|
||||
* Cache individual "words" (strings delimited by white-space or white-space-like
|
||||
* characters that don't involve kerning or ligatures) in textruns.
|
||||
*/
|
||||
class THEBES_API gfxTextRunWordCache {
|
||||
public:
|
||||
gfxTextRunWordCache() {
|
||||
mCache.Init(100);
|
||||
}
|
||||
~gfxTextRunWordCache() {
|
||||
NS_ASSERTION(mCache.Count() == 0, "Textrun cache not empty!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textrun using cached words.
|
||||
* @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, PRBool *aIsInCache);
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
|
||||
gfxFontGroup *aFontGroup,
|
||||
const gfxFontGroup::Parameters *aParams,
|
||||
PRUint32 aFlags, PRBool *aIsInCache);
|
||||
|
||||
/**
|
||||
* 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 THEBES_API CacheHashKey {
|
||||
void *mFontOrGroup;
|
||||
const void *mString;
|
||||
PRUint32 mLength;
|
||||
PRUint32 mAppUnitsPerDevUnit;
|
||||
PRUint32 mStringHash;
|
||||
PRPackedBool mIsDoubleByteText;
|
||||
};
|
||||
|
||||
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,
|
||||
const nsTArray<DeferredWord>& aDeferredWords,
|
||||
PRBool aSuccessful);
|
||||
void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
|
||||
PRUint32 aEnd, PRUint32 aHash);
|
||||
|
||||
nsTHashtable<CacheHashEntry> mCache;
|
||||
};
|
||||
|
||||
#endif /* GFX_TEXT_RUN_WORD_CACHE_H */
|
|
@ -35,6 +35,7 @@ CPPSRCS = \
|
|||
gfxRect.cpp \
|
||||
gfxSkipChars.cpp \
|
||||
gfxTextRunCache.cpp \
|
||||
gfxTextRunWordCache.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_TREE_CAIRO
|
||||
|
|
|
@ -531,24 +531,13 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags)
|
|||
aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
|
||||
static const PRUint8 space = ' ';
|
||||
|
||||
gfxFont *font = GetFontAt(0);
|
||||
PRUint32 spaceGlyph = font->GetSpaceGlyph();
|
||||
float spaceWidth = font->GetMetrics().spaceWidth;
|
||||
PRUint32 spaceWidthAppUnits = NS_lroundf(spaceWidth*aParams->mAppUnitsPerDevUnit);
|
||||
if (!spaceGlyph ||
|
||||
!gfxTextRun::CompressedGlyph::IsSimpleGlyphID(spaceGlyph) ||
|
||||
!gfxTextRun::CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits))
|
||||
return MakeTextRun(&space, 1, aParams, aFlags);
|
||||
|
||||
nsAutoPtr<gfxTextRun> textRun;
|
||||
textRun = new gfxTextRun(aParams, &space, 1, this, aFlags);
|
||||
if (!textRun)
|
||||
if (!textRun || !textRun->GetCharacterGlyphs())
|
||||
return nsnull;
|
||||
if (NS_FAILED(textRun->AddGlyphRun(font, 0)))
|
||||
return nsnull;
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
|
||||
textRun->SetCharacterGlyph(0, g);
|
||||
|
||||
gfxFont *font = GetFontAt(0);
|
||||
textRun->SetSpaceGlyph(font, aParams->mContext, 0);
|
||||
return textRun.forget();
|
||||
}
|
||||
|
||||
|
@ -691,35 +680,7 @@ gfxTextRun::Clone(const gfxTextRunFactory::Parameters *aParams, const void *aTex
|
|||
if (!textRun || !textRun->mCharacterGlyphs)
|
||||
return nsnull;
|
||||
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mGlyphRuns.Length(); ++i) {
|
||||
if (NS_FAILED(textRun->AddGlyphRun(mGlyphRuns[i].mFont,
|
||||
mGlyphRuns[i].mCharacterOffset)))
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
CompressedGlyph g = mCharacterGlyphs[i];
|
||||
g.SetCanBreakBefore(PR_FALSE);
|
||||
textRun->mCharacterGlyphs[i] = g;
|
||||
}
|
||||
|
||||
if (mDetailedGlyphs) {
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
DetailedGlyph *details = mDetailedGlyphs[i];
|
||||
if (details) {
|
||||
PRUint32 glyphCount = 1;
|
||||
while (!details[glyphCount - 1].mIsLastGlyph) {
|
||||
++glyphCount;
|
||||
}
|
||||
DetailedGlyph *dest = textRun->AllocateDetailedGlyphs(i, glyphCount);
|
||||
if (!dest)
|
||||
return nsnull;
|
||||
memcpy(dest, details, sizeof(DetailedGlyph)*glyphCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textRun->CopyGlyphDataFrom(this, 0, mCharacterCount, 0, PR_FALSE);
|
||||
return textRun.forget();
|
||||
}
|
||||
|
||||
|
@ -1490,8 +1451,24 @@ gfxTextRun::FindFirstGlyphRunContaining(PRUint32 aOffset)
|
|||
nsresult
|
||||
gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint32 aUTF16Offset)
|
||||
{
|
||||
NS_ASSERTION(mGlyphRuns.Length() > 0 || aUTF16Offset == 0,
|
||||
PRUint32 numGlyphRuns = mGlyphRuns.Length();
|
||||
if (numGlyphRuns > 0) {
|
||||
GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
|
||||
|
||||
NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
|
||||
"Glyph runs out of order");
|
||||
|
||||
if (lastGlyphRun->mFont == aFont)
|
||||
return NS_OK;
|
||||
if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
|
||||
lastGlyphRun->mFont = aFont;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
NS_ASSERTION(numGlyphRuns > 0 || aUTF16Offset == 0,
|
||||
"First run doesn't cover the first character?");
|
||||
|
||||
GlyphRun *glyphRun = mGlyphRuns.AppendElement();
|
||||
if (!glyphRun)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -1584,3 +1561,122 @@ gfxTextRun::RecordSurrogates(const PRUnichar *aString)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PRUint32
|
||||
CountDetailedGlyphs(gfxTextRun::DetailedGlyph *aGlyphs)
|
||||
{
|
||||
PRUint32 i = 0;
|
||||
while (!aGlyphs[i].mIsLastGlyph) {
|
||||
++i;
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
static void
|
||||
ClearCharacters(gfxTextRun::CompressedGlyph *aGlyphs, PRUint32 aLength)
|
||||
{
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
g.SetMissing();
|
||||
PRUint32 i;
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
aGlyphs[i] = g;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
|
||||
PRUint32 aLength, PRUint32 aDest,
|
||||
PRBool aStealData)
|
||||
{
|
||||
NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
|
||||
"Source substring out of range");
|
||||
NS_ASSERTION(aDest + aLength <= GetLength(),
|
||||
"Destination substring out of range");
|
||||
|
||||
PRUint32 i;
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
CompressedGlyph g = aSource->mCharacterGlyphs[i + aStart];
|
||||
g.SetCanBreakBefore(PR_FALSE);
|
||||
mCharacterGlyphs[i + aDest] = g;
|
||||
if (aStealData) {
|
||||
aSource->mCharacterGlyphs[i + aStart].SetMissing();
|
||||
}
|
||||
}
|
||||
|
||||
if (aSource->mDetailedGlyphs) {
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
DetailedGlyph *details = aSource->mDetailedGlyphs[i + aStart];
|
||||
if (details) {
|
||||
if (aStealData) {
|
||||
if (!mDetailedGlyphs) {
|
||||
mDetailedGlyphs = new nsAutoArrayPtr<DetailedGlyph>[mCharacterCount];
|
||||
if (!mDetailedGlyphs) {
|
||||
ClearCharacters(&mCharacterGlyphs[aDest], aLength);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mDetailedGlyphs[i + aDest] = details;
|
||||
aSource->mDetailedGlyphs[i + aStart].forget();
|
||||
} else {
|
||||
PRUint32 glyphCount = CountDetailedGlyphs(details);
|
||||
DetailedGlyph *dest = AllocateDetailedGlyphs(i + aDest, glyphCount);
|
||||
if (!dest) {
|
||||
ClearCharacters(&mCharacterGlyphs[aDest], aLength);
|
||||
return;
|
||||
}
|
||||
memcpy(dest, details, sizeof(DetailedGlyph)*glyphCount);
|
||||
}
|
||||
} else if (mDetailedGlyphs) {
|
||||
mDetailedGlyphs[i + aDest] = nsnull;
|
||||
}
|
||||
}
|
||||
} else if (mDetailedGlyphs) {
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
mDetailedGlyphs[i + aDest] = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
GlyphRunIterator iter(aSource, aStart, aLength);
|
||||
while (iter.NextRun()) {
|
||||
gfxFont *font = iter.GetGlyphRun()->mFont;
|
||||
PRUint32 start = iter.GetStringStart();
|
||||
PRUint32 end = iter.GetStringEnd();
|
||||
NS_ASSERTION(aSource->IsClusterStart(start),
|
||||
"Started word in the middle of a cluster...");
|
||||
NS_ASSERTION(end == aSource->GetLength() || aSource->IsClusterStart(end),
|
||||
"Ended word in the middle of a cluster...");
|
||||
|
||||
nsresult rv = AddGlyphRun(font, start - aStart + aDest);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex)
|
||||
{
|
||||
PRUint32 spaceGlyph = aFont->GetSpaceGlyph();
|
||||
float spaceWidth = aFont->GetMetrics().spaceWidth;
|
||||
PRUint32 spaceWidthAppUnits = NS_lroundf(spaceWidth*mAppUnitsPerDevUnit);
|
||||
if (!spaceGlyph ||
|
||||
!CompressedGlyph::IsSimpleGlyphID(spaceGlyph) ||
|
||||
!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
|
||||
gfxTextRunFactory::Parameters params = {
|
||||
aContext, nsnull, nsnull, nsnull, 0, mAppUnitsPerDevUnit
|
||||
};
|
||||
static const PRUint8 space = ' ';
|
||||
nsAutoPtr<gfxTextRun> textRun;
|
||||
textRun = mFontGroup->MakeTextRun(&space, 1, ¶ms,
|
||||
gfxTextRunFactory::TEXT_IS_8BIT | gfxTextRunFactory::TEXT_IS_ASCII |
|
||||
gfxTextRunFactory::TEXT_IS_PERSISTENT);
|
||||
if (!textRun || !textRun->mCharacterGlyphs)
|
||||
return;
|
||||
CopyGlyphDataFrom(textRun, 0, 1, aCharIndex, PR_TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
AddGlyphRun(aFont, aCharIndex);
|
||||
CompressedGlyph g;
|
||||
g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
|
||||
SetCharacterGlyph(aCharIndex, g);
|
||||
}
|
||||
|
|
|
@ -88,13 +88,10 @@ gfxSkipChars::BuildShortcuts()
|
|||
void
|
||||
gfxSkipCharsIterator::SetOffsets(PRUint32 aOffset, PRBool aInOriginalString)
|
||||
{
|
||||
NS_ASSERTION(aOffset <= mSkipChars->mCharCount,
|
||||
"Invalid offset");
|
||||
|
||||
if (mSkipChars->mListLength == 0) {
|
||||
// Special case: all chars kept, original and stripped strings are equal
|
||||
if (aOffset < 0) {
|
||||
aOffset = 0;
|
||||
} else if (aOffset > mSkipChars->mCharCount) {
|
||||
aOffset = mSkipChars->mCharCount;
|
||||
}
|
||||
mOriginalStringOffset = mSkippedStringOffset = aOffset;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,469 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Foundation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "gfxTextRunWordCache.h"
|
||||
|
||||
static inline PRUint32
|
||||
HashMix(PRUint32 aHash, PRUnichar aCh)
|
||||
{
|
||||
return (aHash >> 28) ^ (aHash << 4) ^ aCh;
|
||||
}
|
||||
|
||||
// If the substring of the textrun is rendered entirely in the first font
|
||||
// of the textrun's fontgroup, then return that font. Otherwise return the
|
||||
// fontgroup.
|
||||
static void *GetWordFontOrGroup(gfxTextRun *aTextRun, PRUint32 aOffset,
|
||||
PRUint32 aLength)
|
||||
{
|
||||
PRUint32 glyphRunCount;
|
||||
const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&glyphRunCount);
|
||||
PRUint32 glyphRunIndex = aTextRun->FindFirstGlyphRunContaining(aOffset);
|
||||
gfxFontGroup *fontGroup = aTextRun->GetFontGroup();
|
||||
gfxFont *firstFont = fontGroup->GetFontAt(0);
|
||||
if (glyphRuns[glyphRunIndex].mFont != firstFont)
|
||||
return fontGroup;
|
||||
|
||||
PRUint32 glyphRunEnd = glyphRunIndex == glyphRunCount - 1
|
||||
? aTextRun->GetLength() : glyphRuns[glyphRunIndex + 1].mCharacterOffset;
|
||||
if (aOffset + aLength <= glyphRunEnd)
|
||||
return firstFont;
|
||||
return fontGroup;
|
||||
}
|
||||
|
||||
#define UNICODE_NBSP 0x00A0
|
||||
|
||||
// XXX should we treat NBSP or SPACE combined with other characters as a word
|
||||
// boundary? Currently this does.
|
||||
static PRBool
|
||||
IsBoundarySpace(PRUnichar aChar)
|
||||
{
|
||||
return aChar == ' ' || aChar == UNICODE_NBSP;
|
||||
}
|
||||
|
||||
static PRBool
|
||||
IsWordBoundary(PRUnichar aChar)
|
||||
{
|
||||
return IsBoundarySpace(aChar) || gfxFontGroup::IsInvisibleChar(aChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a word in the cache. If the word is found in the cache
|
||||
* (which could be from an existing textrun or an earlier word in the same
|
||||
* textrun), we copy the glyphs from the word into the textrun, unless
|
||||
* aDeferredWords is non-null (meaning that all words from now on must be
|
||||
* copied later instead of now), in which case we add the word to be copied
|
||||
* to the list.
|
||||
*
|
||||
* If the word is not found in the cache then we add it to the cache with
|
||||
* aFirstFont as the key, on the assumption that the word will be matched
|
||||
* by aFirstFont. If this assumption is false we fix up the cache later in
|
||||
* FinishTextRun. We make this assumption for two reasons:
|
||||
* 1) it's usually true so it saves an extra cache lookup if we had to insert
|
||||
* the entry later
|
||||
* 2) we need to record words that appear in the string in some kind
|
||||
* of hash table so we can detect and use them if they appear later in the
|
||||
* (in general the string might be huge and contain many repeated words).
|
||||
* We might as well use the master hash table for this.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if (aEnd <= aStart)
|
||||
return PR_TRUE;
|
||||
|
||||
PRUint32 length = aEnd - aStart;
|
||||
CacheHashKey key =
|
||||
{ aFirstFont, aTextRun->GetTextAt(aStart),
|
||||
length, aTextRun->GetAppUnitsPerDevUnit(), aHash,
|
||||
(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) == 0 };
|
||||
CacheHashEntry *fontEntry = mCache.PutEntry(key);
|
||||
if (!fontEntry)
|
||||
return PR_FALSE;
|
||||
CacheHashEntry *existingEntry = nsnull;
|
||||
|
||||
if (fontEntry->mTextRun) {
|
||||
existingEntry = fontEntry;
|
||||
} else {
|
||||
key.mFontOrGroup = aTextRun->GetFontGroup();
|
||||
CacheHashEntry *groupEntry = mCache.GetEntry(key);
|
||||
if (groupEntry) {
|
||||
existingEntry = groupEntry;
|
||||
mCache.RawRemoveEntry(fontEntry);
|
||||
fontEntry = nsnull;
|
||||
}
|
||||
}
|
||||
// At this point, either existingEntry is non-null and points to (surprise!)
|
||||
// an entry for a word in an existing textrun, or fontEntry points
|
||||
// to a cache entry for this word with aFirstFont, which needs to be
|
||||
// filled in or removed.
|
||||
|
||||
if (existingEntry) {
|
||||
if (aDeferredWords) {
|
||||
DeferredWord word = { existingEntry->mTextRun,
|
||||
existingEntry->mWordOffset, aStart, aEnd - aStart, aHash };
|
||||
aDeferredWords->AppendElement(word);
|
||||
} else {
|
||||
aTextRun->CopyGlyphDataFrom(existingEntry->mTextRun,
|
||||
existingEntry->mWordOffset, aEnd - aStart, aStart, PR_FALSE);
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
// Set up the cache entry so that if later in this textrun we hit this
|
||||
// entry, we'll copy within our own textrun
|
||||
fontEntry->mTextRun = aTextRun;
|
||||
fontEntry->mWordOffset = aStart;
|
||||
fontEntry->mHashedByFont = PR_TRUE;
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes all deferred words. Each word is copied from the source
|
||||
* textrun to the output textrun. (The source may be an earlier word in the
|
||||
* output textrun.) If the word was not matched by the textrun's fontgroup's
|
||||
* first font, then we remove the entry we optimistically added to the cache
|
||||
* with that font in the key, and add a new entry keyed with the fontgroup
|
||||
* instead.
|
||||
*
|
||||
* @param aSuccessful if false, then we don't do any word copies and we don't
|
||||
* add anything to the cache, but we still remove all the optimistic cache
|
||||
* entries.
|
||||
*/
|
||||
void
|
||||
gfxTextRunWordCache::FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
|
||||
const nsTArray<DeferredWord>& aDeferredWords,
|
||||
PRBool aSuccessful)
|
||||
{
|
||||
PRUint32 i;
|
||||
gfxFontGroup *fontGroup = aTextRun->GetFontGroup();
|
||||
gfxFont *font = fontGroup->GetFontAt(0);
|
||||
// copy deferred words from various sources into destination textrun
|
||||
for (i = 0; i < aDeferredWords.Length(); ++i) {
|
||||
const DeferredWord *word = &aDeferredWords[i];
|
||||
gfxTextRun *source = word->mSourceTextRun;
|
||||
if (!source) {
|
||||
source = aNewRun;
|
||||
// we created a cache entry for this word based on the assumption
|
||||
// that the word matches GetFontAt(0). If this assumption is false,
|
||||
// we need to remove that cache entry and replace it with an entry
|
||||
// keyed off the fontgroup.
|
||||
if (!aSuccessful ||
|
||||
GetWordFontOrGroup(aNewRun, word->mSourceOffset, word->mLength) != font) {
|
||||
CacheHashKey key =
|
||||
{ font, aTextRun->GetTextAt(word->mDestOffset),
|
||||
word->mLength, aTextRun->GetAppUnitsPerDevUnit(), word->mHash,
|
||||
(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) == 0 };
|
||||
NS_ASSERTION(mCache.GetEntry(key),
|
||||
"This entry should have been added previously!");
|
||||
mCache.RemoveEntry(key);
|
||||
|
||||
if (aSuccessful) {
|
||||
key.mFontOrGroup = fontGroup;
|
||||
CacheHashEntry *groupEntry = mCache.PutEntry(key);
|
||||
if (groupEntry) {
|
||||
groupEntry->mTextRun = aTextRun;
|
||||
groupEntry->mWordOffset = word->mDestOffset;
|
||||
groupEntry->mHashedByFont = PR_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aSuccessful) {
|
||||
// Copy the word. If the source is aNewRun, then
|
||||
// allow CopyGlyphDataFrom to steal the internal data of
|
||||
// aNewRun since that's only temporary anyway.
|
||||
aTextRun->CopyGlyphDataFrom(source,
|
||||
word->mSourceOffset, word->mLength, word->mDestOffset,
|
||||
source == aNewRun);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxTextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
|
||||
gfxFontGroup *aFontGroup,
|
||||
const gfxFontGroup::Parameters *aParams,
|
||||
PRUint32 aFlags, PRBool *aIsInCache)
|
||||
{
|
||||
nsAutoPtr<gfxTextRun> textRun;
|
||||
textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
|
||||
if (!textRun || !textRun->GetCharacterGlyphs())
|
||||
return nsnull;
|
||||
|
||||
gfxFont *font = aFontGroup->GetFontAt(0);
|
||||
nsresult rv = textRun->AddGlyphRun(font, 0);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
nsAutoTArray<PRUnichar,200> tempString;
|
||||
nsAutoTArray<DeferredWord,50> deferredWords;
|
||||
PRUint32 i;
|
||||
PRUint32 wordStart = 0;
|
||||
PRUint32 hash = 0;
|
||||
for (i = 0; i <= aLength; ++i) {
|
||||
PRUnichar ch = i < aLength ? aText[i] : ' ';
|
||||
if (IsWordBoundary(ch)) {
|
||||
PRBool hit = LookupWord(textRun, font, wordStart, i, hash,
|
||||
deferredWords.Length() == 0 ? nsnull : &deferredWords);
|
||||
if (!hit) {
|
||||
if (tempString.Length() > 0) {
|
||||
tempString.AppendElement(' ');
|
||||
}
|
||||
PRUint32 offset = tempString.Length();
|
||||
PRUint32 length = i - wordStart;
|
||||
PRUnichar *chars = tempString.AppendElements(length);
|
||||
if (!chars) {
|
||||
FinishTextRun(textRun, nsnull, deferredWords, PR_FALSE);
|
||||
return nsnull;
|
||||
}
|
||||
memcpy(chars, aText + wordStart, length*sizeof(PRUnichar));
|
||||
DeferredWord word = { nsnull, offset, wordStart, length, hash };
|
||||
deferredWords.AppendElement(word);
|
||||
}
|
||||
|
||||
if (IsBoundarySpace(ch) && i < aLength) {
|
||||
textRun->SetSpaceGlyph(font, aParams->mContext, i);
|
||||
} // else we should set this character to be invisible missing,
|
||||
// but it already is because the textrun is blank!
|
||||
hash = 0;
|
||||
wordStart = i + 1;
|
||||
} else {
|
||||
hash = HashMix(hash, ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (deferredWords.Length() == 0) {
|
||||
// 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 =
|
||||
{ aParams->mContext, nsnull, nsnull, nsnull, 0, aParams->mAppUnitsPerDevUnit };
|
||||
nsAutoPtr<gfxTextRun> newRun;
|
||||
newRun = aFontGroup->MakeTextRun(tempString.Elements(), tempString.Length(),
|
||||
¶ms, aFlags | gfxTextRunFactory::TEXT_IS_PERSISTENT);
|
||||
|
||||
FinishTextRun(textRun, newRun, deferredWords, newRun != nsnull);
|
||||
return textRun.forget();
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxTextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
|
||||
gfxFontGroup *aFontGroup,
|
||||
const gfxFontGroup::Parameters *aParams,
|
||||
PRUint32 aFlags, PRBool *aIsInCache)
|
||||
{
|
||||
aFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
|
||||
nsAutoPtr<gfxTextRun> textRun;
|
||||
textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
|
||||
if (!textRun || !textRun->GetCharacterGlyphs())
|
||||
return nsnull;
|
||||
|
||||
gfxFont *font = aFontGroup->GetFontAt(0);
|
||||
nsresult rv = textRun->AddGlyphRun(font, 0);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
nsAutoTArray<PRUint8,200> tempString;
|
||||
nsAutoTArray<DeferredWord,50> deferredWords;
|
||||
PRUint32 i;
|
||||
PRUint32 wordStart = 0;
|
||||
PRUint32 hash = 0;
|
||||
for (i = 0; i <= aLength; ++i) {
|
||||
PRUint8 ch = i < aLength ? aText[i] : ' ';
|
||||
if (IsWordBoundary(ch)) {
|
||||
PRBool hit = LookupWord(textRun, font, wordStart, i, hash,
|
||||
deferredWords.Length() == 0 ? nsnull : &deferredWords);
|
||||
if (!hit) {
|
||||
if (tempString.Length() > 0) {
|
||||
tempString.AppendElement(' ');
|
||||
}
|
||||
PRUint32 offset = tempString.Length();
|
||||
PRUint32 length = i - wordStart;
|
||||
PRUint8 *chars = tempString.AppendElements(length);
|
||||
if (!chars) {
|
||||
FinishTextRun(textRun, nsnull, deferredWords, PR_FALSE);
|
||||
return nsnull;
|
||||
}
|
||||
memcpy(chars, aText + wordStart, length*sizeof(PRUint8));
|
||||
DeferredWord word = { nsnull, offset, wordStart, length, hash };
|
||||
deferredWords.AppendElement(word);
|
||||
}
|
||||
|
||||
if (IsBoundarySpace(ch) && i < aLength) {
|
||||
textRun->SetSpaceGlyph(font, aParams->mContext, i);
|
||||
} // else we should set this character to be invisible missing,
|
||||
// but it already is because the textrun is blank!
|
||||
hash = 0;
|
||||
wordStart = i + 1;
|
||||
} else {
|
||||
hash = HashMix(hash, ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (deferredWords.Length() == 0) {
|
||||
// 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 =
|
||||
{ aParams->mContext, nsnull, nsnull, nsnull, 0, aParams->mAppUnitsPerDevUnit };
|
||||
nsAutoPtr<gfxTextRun> newRun;
|
||||
newRun = aFontGroup->MakeTextRun(tempString.Elements(), tempString.Length(),
|
||||
¶ms, aFlags | gfxTextRunFactory::TEXT_IS_PERSISTENT);
|
||||
|
||||
FinishTextRun(textRun, newRun, deferredWords, newRun != nsnull);
|
||||
return textRun.forget();
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRunWordCache::RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
|
||||
PRUint32 aEnd, PRUint32 aHash)
|
||||
{
|
||||
if (aEnd <= aStart)
|
||||
return;
|
||||
|
||||
PRUint32 length = aEnd - aStart;
|
||||
CacheHashKey key =
|
||||
{ GetWordFontOrGroup(aTextRun, aStart, length), aTextRun->GetTextAt(aStart),
|
||||
length, aTextRun->GetAppUnitsPerDevUnit(), aHash,
|
||||
(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) == 0 };
|
||||
CacheHashEntry *entry = mCache.GetEntry(key);
|
||||
if (entry && entry->mTextRun == aTextRun) {
|
||||
// XXX would like to use RawRemoveEntry here plus some extra method
|
||||
// that conditionally shrinks the hashtable
|
||||
mCache.RemoveEntry(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a textrun from the cache by looking up each word and removing it
|
||||
void
|
||||
gfxTextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun)
|
||||
{
|
||||
PRUint32 i;
|
||||
PRUint32 wordStart = 0;
|
||||
PRUint32 hash = 0;
|
||||
for (i = 0; i < aTextRun->GetLength(); ++i) {
|
||||
PRUnichar ch = aTextRun->GetChar(i);
|
||||
if (IsWordBoundary(ch)) {
|
||||
RemoveWord(aTextRun, wordStart, i, hash);
|
||||
hash = 0;
|
||||
wordStart = i + 1;
|
||||
} else {
|
||||
hash = HashMix(hash, ch);
|
||||
}
|
||||
}
|
||||
RemoveWord(aTextRun, wordStart, i, hash);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static PRBool
|
||||
IsWordEnd(gfxTextRun *aTextRun, PRUint32 aOffset)
|
||||
{
|
||||
PRUint32 runLength = aTextRun->GetLength();
|
||||
if (aOffset == runLength)
|
||||
return PR_TRUE;
|
||||
if (aOffset > runLength)
|
||||
return PR_FALSE;
|
||||
return IsWordBoundary(aTextRun->GetChar(aOffset));
|
||||
}
|
||||
|
||||
static void *
|
||||
GetFontOrGroup(gfxFontGroup *aFontGroup, PRBool aUseFont)
|
||||
{
|
||||
return aUseFont
|
||||
? NS_STATIC_CAST(void *, aFontGroup->GetFontAt(0))
|
||||
: NS_STATIC_CAST(void *, aFontGroup);
|
||||
}
|
||||
|
||||
PRBool
|
||||
gfxTextRunWordCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
|
||||
{
|
||||
if (!mTextRun)
|
||||
return PR_FALSE;
|
||||
|
||||
PRUint32 length = aKey->mLength;
|
||||
gfxFontGroup *fontGroup = mTextRun->GetFontGroup();
|
||||
if (!IsWordEnd(mTextRun, mWordOffset + length) ||
|
||||
GetFontOrGroup(fontGroup, mHashedByFont) != aKey->mFontOrGroup ||
|
||||
aKey->mAppUnitsPerDevUnit != mTextRun->GetAppUnitsPerDevUnit())
|
||||
return PR_FALSE;
|
||||
|
||||
if (mTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
|
||||
const PRUint8 *text = mTextRun->GetText8Bit() + mWordOffset;
|
||||
if (!aKey->mIsDoubleByteText)
|
||||
return memcmp(text, aKey->mString, length) == 0;
|
||||
return CompareDifferentWidthStrings(text,
|
||||
NS_STATIC_CAST(const PRUnichar *, aKey->mString), length);
|
||||
} else {
|
||||
const PRUnichar *text = mTextRun->GetTextUnicode() + mWordOffset;
|
||||
if (aKey->mIsDoubleByteText)
|
||||
return memcmp(text, aKey->mString, length*sizeof(PRUnichar)) == 0;
|
||||
return CompareDifferentWidthStrings(NS_STATIC_CAST(const PRUint8 *, aKey->mString),
|
||||
text, length);
|
||||
}
|
||||
}
|
||||
|
||||
PLDHashNumber
|
||||
gfxTextRunWordCache::CacheHashEntry::HashKey(const KeyTypePointer aKey)
|
||||
{
|
||||
return aKey->mStringHash + (long)aKey->mFontOrGroup + aKey->mAppUnitsPerDevUnit +
|
||||
aKey->mIsDoubleByteText;
|
||||
}
|
Загрузка…
Ссылка в новой задаче