зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1360620 - Optimize GlyphRun storage strategy in gfxTextRun, to reduce the overall size of the gfxTextRun object. r=jrmuizel
This commit is contained in:
Родитель
3de40d5fb3
Коммит
afaf811234
|
@ -59,16 +59,31 @@ extern uint32_t gGlyphExtentsSetupFallBackToTight;
|
|||
#endif
|
||||
|
||||
bool
|
||||
gfxTextRun::GlyphRunIterator::NextRun() {
|
||||
if (mNextIndex >= mTextRun->mGlyphRuns.Length())
|
||||
return false;
|
||||
mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
|
||||
if (mGlyphRun->mCharacterOffset >= mEndOffset)
|
||||
gfxTextRun::GlyphRunIterator::NextRun()
|
||||
{
|
||||
uint32_t glyphRunCount;
|
||||
if (mTextRun->mHasGlyphRunArray) {
|
||||
glyphRunCount = mTextRun->mGlyphRunArray.Length();
|
||||
if (mNextIndex >= glyphRunCount) {
|
||||
return false;
|
||||
}
|
||||
mGlyphRun = &mTextRun->mGlyphRunArray[mNextIndex];
|
||||
} else {
|
||||
if (mNextIndex > 0) {
|
||||
return false;
|
||||
}
|
||||
glyphRunCount = 1;
|
||||
mGlyphRun = &mTextRun->mSingleGlyphRun;
|
||||
}
|
||||
|
||||
if (mGlyphRun->mCharacterOffset >= mEndOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
|
||||
uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
|
||||
? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
|
||||
uint32_t last = mNextIndex + 1 < glyphRunCount
|
||||
? mTextRun->mGlyphRunArray[mNextIndex + 1].mCharacterOffset
|
||||
: mTextRun->GetLength();
|
||||
mStringEnd = std::min(mEndOffset, last);
|
||||
|
||||
++mNextIndex;
|
||||
|
@ -145,9 +160,11 @@ gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
|
|||
gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
|
||||
uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
|
||||
: gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
|
||||
, mSingleGlyphRun()
|
||||
, mUserData(aParams->mUserData)
|
||||
, mFontGroup(aFontGroup)
|
||||
, mReleasedFontGroup(false)
|
||||
, mHasGlyphRunArray(false)
|
||||
, mShapingState(eShapingState_Normal)
|
||||
{
|
||||
NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
|
||||
|
@ -183,6 +200,12 @@ gfxTextRun::~gfxTextRun()
|
|||
mFlags = 0xFFFFFFFF;
|
||||
#endif
|
||||
|
||||
if (mHasGlyphRunArray) {
|
||||
mGlyphRunArray.~nsTArray<GlyphRun>();
|
||||
} else {
|
||||
mSingleGlyphRun.mFont = nullptr;
|
||||
}
|
||||
|
||||
// The cached ellipsis textrun (if any) in a fontgroup will have already
|
||||
// been told to release its reference to the group, so we mustn't do that
|
||||
// again here.
|
||||
|
@ -1238,21 +1261,27 @@ uint32_t
|
|||
gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) const
|
||||
{
|
||||
NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
|
||||
NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
|
||||
NS_ASSERTION(GetLength() == 0 ||
|
||||
(!mHasGlyphRunArray && mSingleGlyphRun.mFont) ||
|
||||
(mHasGlyphRunArray && mGlyphRunArray.Length() > 0),
|
||||
"non-empty text but no glyph runs present!");
|
||||
if (aOffset == GetLength())
|
||||
return mGlyphRuns.Length();
|
||||
if (!mHasGlyphRunArray) {
|
||||
return 0;
|
||||
}
|
||||
if (aOffset == GetLength()) {
|
||||
return mGlyphRunArray.Length();
|
||||
}
|
||||
uint32_t start = 0;
|
||||
uint32_t end = mGlyphRuns.Length();
|
||||
uint32_t end = mGlyphRunArray.Length();
|
||||
while (end - start > 1) {
|
||||
uint32_t mid = (start + end)/2;
|
||||
if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
|
||||
if (mGlyphRunArray[mid].mCharacterOffset <= aOffset) {
|
||||
start = mid;
|
||||
} else {
|
||||
end = mid;
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
|
||||
NS_ASSERTION(mGlyphRunArray[start].mCharacterOffset <= aOffset,
|
||||
"Hmm, something went wrong, aOffset should have been found");
|
||||
return start;
|
||||
}
|
||||
|
@ -1268,9 +1297,22 @@ gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
|
|||
if (!aFont) {
|
||||
return NS_OK;
|
||||
}
|
||||
uint32_t numGlyphRuns = mGlyphRuns.Length();
|
||||
if (!mHasGlyphRunArray) {
|
||||
// We don't currently have an array.
|
||||
if (!mSingleGlyphRun.mFont) {
|
||||
// This is the first glyph run: just store it directly.
|
||||
mSingleGlyphRun.mFont = aFont;
|
||||
mSingleGlyphRun.mMatchType = aMatchType;
|
||||
mSingleGlyphRun.mOrientation = aOrientation;
|
||||
mSingleGlyphRun.mCharacterOffset = aUTF16Offset;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
uint32_t numGlyphRuns = mHasGlyphRunArray ? mGlyphRunArray.Length() : 1;
|
||||
if (!aForceNewRun && numGlyphRuns > 0) {
|
||||
GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
|
||||
GlyphRun* lastGlyphRun =
|
||||
mHasGlyphRunArray ? &mGlyphRunArray[numGlyphRuns - 1]
|
||||
: &mSingleGlyphRun;
|
||||
|
||||
NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
|
||||
"Glyph runs out of order (and run not forced)");
|
||||
|
@ -1291,11 +1333,14 @@ gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
|
|||
// font as the new one wants, merge with it instead of creating
|
||||
// adjacent runs with the same font
|
||||
if (numGlyphRuns > 1 &&
|
||||
mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
|
||||
mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType &&
|
||||
mGlyphRuns[numGlyphRuns - 2].mOrientation == aOrientation)
|
||||
mGlyphRunArray[numGlyphRuns - 2].mFont == aFont &&
|
||||
mGlyphRunArray[numGlyphRuns - 2].mMatchType == aMatchType &&
|
||||
mGlyphRunArray[numGlyphRuns - 2].mOrientation == aOrientation)
|
||||
{
|
||||
mGlyphRuns.TruncateLength(numGlyphRuns - 1);
|
||||
mGlyphRunArray.TruncateLength(numGlyphRuns - 1);
|
||||
if (mGlyphRunArray.Length() == 1) {
|
||||
ConvertFromGlyphRunArray();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1309,43 +1354,72 @@ gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
|
|||
NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
|
||||
"First run doesn't cover the first character (and run not forced)?");
|
||||
|
||||
GlyphRun *glyphRun = mGlyphRuns.AppendElement();
|
||||
if (!glyphRun)
|
||||
if (!mHasGlyphRunArray) {
|
||||
ConvertToGlyphRunArray();
|
||||
}
|
||||
|
||||
GlyphRun* glyphRun = mGlyphRunArray.AppendElement();
|
||||
if (!glyphRun) {
|
||||
if (mGlyphRunArray.Length() == 1) {
|
||||
ConvertFromGlyphRunArray();
|
||||
}
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
glyphRun->mFont = aFont;
|
||||
glyphRun->mCharacterOffset = aUTF16Offset;
|
||||
glyphRun->mMatchType = aMatchType;
|
||||
glyphRun->mOrientation = aOrientation;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::SortGlyphRuns()
|
||||
{
|
||||
if (mGlyphRuns.Length() <= 1)
|
||||
if (!mHasGlyphRunArray) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<GlyphRun> runs(mGlyphRuns);
|
||||
// We should never have an empty or one-element array here; if there's only
|
||||
// one glyphrun, it should be stored directly in the textrun without using
|
||||
// an array at all.
|
||||
MOZ_ASSERT(mGlyphRunArray.Length() > 1);
|
||||
|
||||
AutoTArray<GlyphRun,16> runs(Move(mGlyphRunArray));
|
||||
GlyphRunOffsetComparator comp;
|
||||
runs.Sort(comp);
|
||||
|
||||
// Now copy back, coalescing adjacent glyph runs that have the same font
|
||||
mGlyphRuns.Clear();
|
||||
uint32_t i, count = runs.Length();
|
||||
for (i = 0; i < count; ++i) {
|
||||
mGlyphRunArray.Clear();
|
||||
gfxFont* prevFont = nullptr;
|
||||
uint16_t prevOrient = 0;
|
||||
DebugOnly<uint32_t> prevOffset = 0;
|
||||
for (auto& run : runs) {
|
||||
// a GlyphRun with the same font and orientation as the previous can
|
||||
// just be skipped; the last GlyphRun will cover its character range.
|
||||
if (i == 0 || runs[i].mFont != runs[i - 1].mFont ||
|
||||
runs[i].mOrientation != runs[i - 1].mOrientation) {
|
||||
mGlyphRuns.AppendElement(runs[i]);
|
||||
MOZ_ASSERT(run.mFont != nullptr);
|
||||
if (prevFont == nullptr ||
|
||||
run.mFont != prevFont || run.mOrientation != prevOrient) {
|
||||
// If two fonts have the same character offset, Sort() will have
|
||||
// randomized the order.
|
||||
NS_ASSERTION(i == 0 ||
|
||||
runs[i].mCharacterOffset !=
|
||||
runs[i - 1].mCharacterOffset,
|
||||
"Two fonts for the same run, glyph indices may not match the font");
|
||||
MOZ_ASSERT(prevFont == nullptr ||
|
||||
run.mCharacterOffset != prevOffset,
|
||||
"Two fonts for the same run, glyph indices unreliable");
|
||||
prevFont = run.mFont;
|
||||
prevOrient = run.mOrientation;
|
||||
#ifdef DEBUG
|
||||
prevOffset = run.mCharacterOffset;
|
||||
#endif
|
||||
if (!mGlyphRunArray.AppendElement(Move(run))) {
|
||||
NS_WARNING("Failed to append glyph run!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mGlyphRunArray.Length() > 0);
|
||||
if (mGlyphRunArray.Length() == 1) {
|
||||
ConvertFromGlyphRunArray();
|
||||
}
|
||||
}
|
||||
|
||||
// Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
|
||||
|
@ -1354,29 +1428,37 @@ gfxTextRun::SortGlyphRuns()
|
|||
void
|
||||
gfxTextRun::SanitizeGlyphRuns()
|
||||
{
|
||||
if (mGlyphRuns.Length() <= 1)
|
||||
if (!mHasGlyphRunArray) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mGlyphRunArray.Length() > 1);
|
||||
|
||||
// If any glyph run starts with ligature-continuation characters, we need to advance it
|
||||
// to the first "real" character to avoid drawing partial ligature glyphs from wrong font
|
||||
// (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
|
||||
// it appear as if a ligature has been formed)
|
||||
int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
|
||||
int32_t i, lastRunIndex = mGlyphRunArray.Length() - 1;
|
||||
const CompressedGlyph *charGlyphs = mCharacterGlyphs;
|
||||
for (i = lastRunIndex; i >= 0; --i) {
|
||||
GlyphRun& run = mGlyphRuns[i];
|
||||
GlyphRun& run = mGlyphRunArray[i];
|
||||
while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
|
||||
run.mCharacterOffset < GetLength()) {
|
||||
run.mCharacterOffset++;
|
||||
}
|
||||
// if the run has become empty, eliminate it
|
||||
if ((i < lastRunIndex &&
|
||||
run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
|
||||
run.mCharacterOffset >= mGlyphRunArray[i+1].mCharacterOffset) ||
|
||||
(i == lastRunIndex && run.mCharacterOffset == GetLength())) {
|
||||
mGlyphRuns.RemoveElementAt(i);
|
||||
mGlyphRunArray.RemoveElementAt(i);
|
||||
--lastRunIndex;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mGlyphRunArray.Length() > 0);
|
||||
if (mGlyphRunArray.Length() == 1) {
|
||||
ConvertFromGlyphRunArray();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
@ -1580,10 +1662,11 @@ gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget)
|
|||
if (!needsGlyphExtents && !mDetailedGlyphs)
|
||||
return;
|
||||
|
||||
uint32_t i, runCount = mGlyphRuns.Length();
|
||||
uint32_t runCount;
|
||||
const GlyphRun* glyphRuns = GetGlyphRuns(&runCount);
|
||||
CompressedGlyph *charGlyphs = mCharacterGlyphs;
|
||||
for (i = 0; i < runCount; ++i) {
|
||||
const GlyphRun& run = mGlyphRuns[i];
|
||||
for (uint32_t i = 0; i < runCount; ++i) {
|
||||
const GlyphRun& run = glyphRuns[i];
|
||||
gfxFont *font = run.mFont;
|
||||
if (MOZ_UNLIKELY(font->GetStyle()->size == 0) ||
|
||||
MOZ_UNLIKELY(font->GetStyle()->sizeAdjust == 0.0f)) {
|
||||
|
@ -1592,7 +1675,7 @@ gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget)
|
|||
|
||||
uint32_t start = run.mCharacterOffset;
|
||||
uint32_t end = i + 1 < runCount ?
|
||||
mGlyphRuns[i + 1].mCharacterOffset : GetLength();
|
||||
glyphRuns[i + 1].mCharacterOffset : GetLength();
|
||||
bool fontIsSetup = false;
|
||||
uint32_t j;
|
||||
gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
|
||||
|
@ -1656,7 +1739,9 @@ gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
|
|||
{
|
||||
// The second arg is how much gfxTextRun::AllocateStorage would have
|
||||
// allocated.
|
||||
size_t total = mGlyphRuns.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
size_t total = mHasGlyphRunArray
|
||||
? mGlyphRunArray.ShallowSizeOfExcludingThis(aMallocSizeOf)
|
||||
: 0;
|
||||
|
||||
if (mDetailedGlyphs) {
|
||||
total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
|
||||
|
@ -1679,18 +1764,19 @@ gfxTextRun::Dump(FILE* aOutput) {
|
|||
aOutput = stdout;
|
||||
}
|
||||
|
||||
uint32_t i;
|
||||
fputc('[', aOutput);
|
||||
for (i = 0; i < mGlyphRuns.Length(); ++i) {
|
||||
uint32_t numGlyphRuns;
|
||||
const GlyphRun* glyphRuns = GetGlyphRuns(&numGlyphRuns);
|
||||
for (uint32_t i = 0; i < numGlyphRuns; ++i) {
|
||||
if (i > 0) {
|
||||
fputc(',', aOutput);
|
||||
}
|
||||
gfxFont* font = mGlyphRuns[i].mFont;
|
||||
gfxFont* font = glyphRuns[i].mFont;
|
||||
const gfxFontStyle* style = font->GetStyle();
|
||||
NS_ConvertUTF16toUTF8 fontName(font->GetName());
|
||||
nsAutoCString lang;
|
||||
style->language->ToUTF8String(lang);
|
||||
fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
|
||||
fprintf(aOutput, "%d: %s %f/%d/%d/%s", glyphRuns[i].mCharacterOffset,
|
||||
fontName.get(), style->size,
|
||||
style->weight, style->style, lang.get());
|
||||
}
|
||||
|
|
|
@ -452,12 +452,13 @@ public:
|
|||
uint32_t aLength, gfxFontGroup *aFontGroup,
|
||||
uint32_t aFlags);
|
||||
|
||||
// The text is divided into GlyphRuns as necessary
|
||||
// The text is divided into GlyphRuns as necessary. (In the vast majority
|
||||
// of cases, a gfxTextRun contains just a single GlyphRun.)
|
||||
struct GlyphRun {
|
||||
RefPtr<gfxFont> mFont; // never null
|
||||
uint32_t mCharacterOffset; // into original UTF16 string
|
||||
uint8_t mMatchType;
|
||||
uint16_t mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
|
||||
RefPtr<gfxFont> mFont; // never null in a valid GlyphRun
|
||||
uint32_t mCharacterOffset; // into original UTF16 string
|
||||
uint16_t mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
|
||||
uint8_t mMatchType;
|
||||
};
|
||||
|
||||
class GlyphRunIterator {
|
||||
|
@ -518,7 +519,15 @@ public:
|
|||
nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
|
||||
uint32_t aStartCharIndex, bool aForceNewRun,
|
||||
uint16_t aOrientation);
|
||||
void ResetGlyphRuns() { mGlyphRuns.Clear(); }
|
||||
void ResetGlyphRuns()
|
||||
{
|
||||
if (mHasGlyphRunArray) {
|
||||
mGlyphRunArray.~nsTArray<GlyphRun>();
|
||||
mHasGlyphRunArray = false;
|
||||
} else {
|
||||
mSingleGlyphRun.mFont = nullptr;
|
||||
}
|
||||
}
|
||||
void SortGlyphRuns();
|
||||
void SanitizeGlyphRuns();
|
||||
|
||||
|
@ -578,9 +587,16 @@ public:
|
|||
void FetchGlyphExtents(DrawTarget* aRefDrawTarget);
|
||||
|
||||
uint32_t CountMissingGlyphs() const;
|
||||
const GlyphRun* GetGlyphRuns(uint32_t* aNumGlyphRuns) const {
|
||||
*aNumGlyphRuns = mGlyphRuns.Length();
|
||||
return mGlyphRuns.Elements();
|
||||
|
||||
const GlyphRun* GetGlyphRuns(uint32_t* aNumGlyphRuns) const
|
||||
{
|
||||
if (mHasGlyphRunArray) {
|
||||
*aNumGlyphRuns = mGlyphRunArray.Length();
|
||||
return mGlyphRunArray.Elements();
|
||||
} else {
|
||||
*aNumGlyphRuns = mSingleGlyphRun.mFont ? 1 : 0;
|
||||
return &mSingleGlyphRun;
|
||||
}
|
||||
}
|
||||
// Returns the index of the GlyphRun containing the given offset.
|
||||
// Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
|
||||
|
@ -757,9 +773,29 @@ private:
|
|||
PropertyProvider *aProvider, Range aSpacingRange,
|
||||
TextRunDrawParams& aParams, uint16_t aOrientation) const;
|
||||
|
||||
// XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
|
||||
// for smaller size especially in the super-common one-glyphrun case
|
||||
AutoTArray<GlyphRun,1> mGlyphRuns;
|
||||
// The textrun holds either a single GlyphRun -or- an array;
|
||||
// the flag mHasGlyphRunArray tells us which is present.
|
||||
union {
|
||||
GlyphRun mSingleGlyphRun;
|
||||
nsTArray<GlyphRun> mGlyphRunArray;
|
||||
};
|
||||
|
||||
void ConvertToGlyphRunArray() {
|
||||
MOZ_ASSERT(!mHasGlyphRunArray && mSingleGlyphRun.mFont);
|
||||
GlyphRun tmp = mozilla::Move(mSingleGlyphRun);
|
||||
mSingleGlyphRun.~GlyphRun();
|
||||
new (&mGlyphRunArray) nsTArray<GlyphRun>(2);
|
||||
mGlyphRunArray.AppendElement(mozilla::Move(tmp));
|
||||
mHasGlyphRunArray = true;
|
||||
}
|
||||
|
||||
void ConvertFromGlyphRunArray() {
|
||||
MOZ_ASSERT(mHasGlyphRunArray && mGlyphRunArray.Length() == 1);
|
||||
GlyphRun tmp = mozilla::Move(mGlyphRunArray[0]);
|
||||
mGlyphRunArray.~nsTArray<GlyphRun>();
|
||||
new (&mSingleGlyphRun) GlyphRun(mozilla::Move(tmp));
|
||||
mHasGlyphRunArray = false;
|
||||
}
|
||||
|
||||
void *mUserData;
|
||||
gfxFontGroup *mFontGroup; // addrefed on creation, but our reference
|
||||
|
@ -771,6 +807,8 @@ private:
|
|||
// until the download completes (or timeout fires)
|
||||
bool mReleasedFontGroup; // we already called NS_RELEASE on
|
||||
// mFontGroup, so don't do it again
|
||||
bool mHasGlyphRunArray; // whether we're using an array or
|
||||
// just storing a single glyphrun
|
||||
|
||||
// shaping state for handling variant fallback features
|
||||
// such as subscript/superscript variant glyphs
|
||||
|
|
Загрузка…
Ссылка в новой задаче