Bug 840431 - Cache an ellipsis textrun on gfxFontGroup. r=roc, a=tef+

This commit is contained in:
Jonathan Kew 2013-03-07 20:07:08 +00:00
Родитель 56fd7845ea
Коммит bf4c3680f3
5 изменённых файлов: 151 добавлений и 39 удалений

Просмотреть файл

@ -53,6 +53,9 @@ using mozilla::services::GetObserverService;
gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
static const PRUnichar kEllipsisChar[] = { 0x2026, 0x0 };
static const PRUnichar kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
#ifdef DEBUG_roc
#define DEBUG_TEXT_RUN_STORAGE_METRICS
#endif
@ -3826,6 +3829,39 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
}
}
gfxTextRun *
gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
LazyReferenceContextGetter& aRefContextGetter)
{
if (mCachedEllipsisTextRun &&
mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
return mCachedEllipsisTextRun;
}
// Use a Unicode ellipsis if the font supports it,
// otherwise use three ASCII periods as fallback.
gfxFont* firstFont = GetFontAt(0);
nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
? nsDependentString(kEllipsisChar,
ArrayLength(kEllipsisChar) - 1)
: nsDependentString(kASCIIPeriodsChar,
ArrayLength(kASCIIPeriodsChar) - 1);
nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
Parameters params = {
refCtx, nullptr, nullptr, nullptr, 0, uint32_t(aAppUnitsPerDevPixel)
};
gfxTextRun* textRun =
MakeTextRun(ellipsis.get(), ellipsis.Length(), &params, TEXT_IS_PERSISTENT);
if (!textRun) {
return nullptr;
}
mCachedEllipsisTextRun = textRun;
textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
// textrun prolong the fontgroup's life
return textRun;
}
already_AddRefed<gfxFont>
gfxFontGroup::TryOtherFamilyMembers(gfxFont* aFont, uint32_t aCh)
{
@ -4078,6 +4114,7 @@ gfxFontGroup::UpdateFontList()
ForEachFont(FindPlatformFont, this);
#endif
mCurrGeneration = GetGeneration();
mCachedEllipsisTextRun = nullptr;
}
}
@ -4517,7 +4554,8 @@ gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
: mUserData(aParams->mUserData),
mFontGroup(aFontGroup),
mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
mFlags(aFlags), mCharacterCount(aLength)
mFlags(aFlags), mCharacterCount(aLength),
mReleasedFontGroup(false)
{
NS_ASSERTION(mAppUnitsPerDevUnit != 0, "Invalid app unit scale");
MOZ_COUNT_CTOR(gfxTextRun);
@ -4546,10 +4584,24 @@ gfxTextRun::~gfxTextRun()
mFlags = 0xFFFFFFFF;
#endif
NS_RELEASE(mFontGroup);
// 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.
if (!mReleasedFontGroup) {
NS_RELEASE(mFontGroup);
}
MOZ_COUNT_DTOR(gfxTextRun);
}
void
gfxTextRun::ReleaseFontGroup()
{
NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
NS_RELEASE(mFontGroup);
mReleasedFontGroup = true;
}
bool
gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
uint8_t *aBreakBefore,

Просмотреть файл

@ -2870,6 +2870,14 @@ public:
nsExpirationState *GetExpirationState() { return &mExpirationState; }
// Tell the textrun to release its reference to its creating gfxFontGroup
// immediately, rather than on destruction. This is used for textruns
// that are actually owned by a gfxFontGroup, so that they don't keep it
// permanently alive due to a circular reference. (The caller of this is
// taking responsibility for ensuring the textrun will not outlive its
// mFontGroup.)
void ReleaseFontGroup();
struct LigatureData {
// textrun offsets of the start and end of the containing ligature
uint32_t mLigatureStart;
@ -2997,7 +3005,8 @@ private:
nsAutoTArray<GlyphRun,1> mGlyphRuns;
void *mUserData;
gfxFontGroup *mFontGroup; // addrefed
gfxFontGroup *mFontGroup; // addrefed on creation, but our reference
// may be released by ReleaseFontGroup()
gfxSkipChars mSkipChars;
nsExpirationState mExpirationState;
uint32_t mAppUnitsPerDevUnit;
@ -3007,6 +3016,8 @@ private:
bool mSkipDrawing; // true if the font group we used had a user font
// download that's in progress, so we should hide text
// until the download completes (or timeout fires)
bool mReleasedFontGroup; // we already called NS_RELEASE on
// mFontGroup, so don't do it again
};
class THEBES_API gfxFontGroup : public gfxTextRunFactory {
@ -3149,6 +3160,18 @@ public:
return mSkipDrawing;
}
class LazyReferenceContextGetter {
public:
virtual already_AddRefed<gfxContext> GetRefContext() = 0;
};
// The gfxFontGroup keeps ownership of this textrun.
// It is only guaranteed to exist until the next call to GetEllipsisTextRun
// (which might use a different appUnitsPerDev value) for the font group,
// or until UpdateFontList is called, or the fontgroup is destroyed.
// Get it/use it/forget it :) - don't keep a reference that might go stale.
gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
LazyReferenceContextGetter& aRefContextGetter);
protected:
nsString mFamilies;
gfxFontStyle mStyle;
@ -3158,6 +3181,10 @@ protected:
gfxUserFontSet* mUserFontSet;
uint64_t mCurrGeneration; // track the current user font set generation, rebuild font list if needed
// Cache a textrun representing an ellipsis (useful for CSS text-overflow)
// at a specific appUnitsPerDevPixel size
nsAutoPtr<gfxTextRun> mCachedEllipsisTextRun;
// cache the most recent pref font to avoid general pref font lookup
nsRefPtr<gfxFontFamily> mLastPrefFamily;
nsRefPtr<gfxFont> mLastPrefFont;

Просмотреть файл

@ -1966,6 +1966,7 @@ gfxPangoFontGroup::UpdateFontList()
mFonts[0] = NULL;
mFontSets.Clear();
mCachedEllipsisTextRun = nullptr;
mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
mCurrGeneration = newGeneration;
mSkipDrawing = false;

Просмотреть файл

@ -23,21 +23,33 @@
namespace mozilla {
namespace css {
static const PRUnichar kEllipsisChar[] = { 0x2026, 0x0 };
static const PRUnichar kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
class LazyReferenceRenderingContextGetterFromFrame MOZ_FINAL :
public gfxFontGroup::LazyReferenceContextGetter {
public:
LazyReferenceRenderingContextGetterFromFrame(nsIFrame* aFrame)
: mFrame(aFrame) {}
virtual already_AddRefed<gfxContext> GetRefContext() MOZ_OVERRIDE
{
nsRefPtr<nsRenderingContext> rc =
mFrame->PresContext()->PresShell()->GetReferenceRenderingContext();
nsRefPtr<gfxContext> ctx = rc->ThebesContext();
return ctx.forget();
}
private:
nsIFrame* mFrame;
};
// Return an ellipsis if the font supports it,
// otherwise use three ASCII periods as fallback.
static nsDependentString GetEllipsis(nsFontMetrics *aFontMetrics)
static gfxTextRun*
GetEllipsisTextRun(nsIFrame* aFrame)
{
// Check if the first font supports Unicode ellipsis.
gfxFontGroup* fontGroup = aFontMetrics->GetThebesFontGroup();
gfxFont* firstFont = fontGroup->GetFontAt(0);
return firstFont && firstFont->HasCharacter(kEllipsisChar[0])
? nsDependentString(kEllipsisChar,
ArrayLength(kEllipsisChar) - 1)
: nsDependentString(kASCIIPeriodsChar,
ArrayLength(kASCIIPeriodsChar) - 1);
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
nsLayoutUtils::FontSizeInflationFor(aFrame));
LazyReferenceRenderingContextGetterFromFrame lazyRefContextGetter(aFrame);
return fm->GetThebesFontGroup()->GetEllipsisTextRun(
aFrame->PresContext()->AppUnitsPerDevPixel(), lazyRefContextGetter);
}
static nsIFrame*
@ -147,10 +159,10 @@ class nsDisplayTextOverflowMarker : public nsDisplayItem
public:
nsDisplayTextOverflowMarker(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
const nsRect& aRect, nscoord aAscent,
const nsString& aString,
const nsStyleTextOverflowSide* aStyle,
uint32_t aIndex)
: nsDisplayItem(aBuilder, aFrame), mRect(aRect), mString(aString),
mAscent(aAscent), mIndex(aIndex) {
: nsDisplayItem(aBuilder, aFrame), mRect(aRect),
mStyle(aStyle), mAscent(aAscent), mIndex(aIndex) {
MOZ_COUNT_CTOR(nsDisplayTextOverflowMarker);
}
#ifdef NS_BUILD_REFCNT_LOGGING
@ -175,7 +187,7 @@ public:
NS_DISPLAY_DECL_NAME("TextOverflow", TYPE_TEXT_OVERFLOW)
private:
nsRect mRect; // in reference frame coordinates
const nsString mString; // the marker text
const nsStyleTextOverflowSide* mStyle;
nscoord mAscent; // baseline for the marker text in mRect
uint32_t mIndex;
};
@ -209,15 +221,28 @@ void
nsDisplayTextOverflowMarker::PaintTextToContext(nsRenderingContext* aCtx,
nsPoint aOffsetFromRect)
{
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm),
nsLayoutUtils::FontSizeInflationFor(mFrame));
aCtx->SetFont(fm);
gfxFloat y = nsLayoutUtils::GetSnappedBaselineY(mFrame, aCtx->ThebesContext(),
mRect.y, mAscent);
nsPoint baselinePt(mRect.x, NSToCoordFloor(y));
nsLayoutUtils::DrawString(mFrame, aCtx, mString.get(),
mString.Length(), baselinePt + aOffsetFromRect);
nsPoint pt = baselinePt + aOffsetFromRect;
if (mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
gfxTextRun* textRun = GetEllipsisTextRun(mFrame);
if (textRun) {
NS_ASSERTION(!textRun->IsRightToLeft(),
"Ellipsis textruns should always be LTR!");
gfxPoint gfxPt(pt.x, pt.y);
textRun->Draw(aCtx->ThebesContext(), gfxPt, gfxFont::GLYPH_FILL,
0, textRun->GetLength(), nullptr, nullptr, nullptr);
}
} else {
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm),
nsLayoutUtils::FontSizeInflationFor(mFrame));
aCtx->SetFont(fm);
nsLayoutUtils::DrawString(mFrame, aCtx, mStyle->mString.get(),
mStyle->mString.Length(), pt);
}
}
void
@ -690,7 +715,7 @@ TextOverflow::CreateMarkers(const nsLineBox* aLine,
markerRect += mBuilder->ToReferenceFrame(mBlock);
nsDisplayItem* marker = new (mBuilder)
nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
aLine->GetAscent(), mLeft.mMarkerString, 0);
aLine->GetAscent(), mLeft.mStyle, 0);
if (marker) {
marker = ClipMarker(mBuilder, mBlock, marker,
mContentArea + mBuilder->ToReferenceFrame(mBlock),
@ -706,7 +731,7 @@ TextOverflow::CreateMarkers(const nsLineBox* aLine,
markerRect += mBuilder->ToReferenceFrame(mBlock);
nsDisplayItem* marker = new (mBuilder)
nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
aLine->GetAscent(), mRight.mMarkerString, 1);
aLine->GetAscent(), mRight.mStyle, 1);
if (marker) {
marker = ClipMarker(mBuilder, mBlock, marker,
mContentArea + mBuilder->ToReferenceFrame(mBlock),
@ -722,17 +747,24 @@ TextOverflow::Marker::SetupString(nsIFrame* aFrame)
if (mInitialized) {
return;
}
nsRefPtr<nsRenderingContext> rc =
aFrame->PresContext()->PresShell()->GetReferenceRenderingContext();
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
nsLayoutUtils::FontSizeInflationFor(aFrame));
rc->SetFont(fm);
mMarkerString = mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS ?
GetEllipsis(fm) : mStyle->mString;
mWidth = nsLayoutUtils::GetStringWidth(aFrame, rc, mMarkerString.get(),
mMarkerString.Length());
if (mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
gfxTextRun* textRun = GetEllipsisTextRun(aFrame);
if (textRun) {
mWidth = textRun->GetAdvanceWidth(0, textRun->GetLength(), nullptr);
} else {
mWidth = 0;
}
} else {
nsRefPtr<nsRenderingContext> rc =
aFrame->PresContext()->PresShell()->GetReferenceRenderingContext();
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
nsLayoutUtils::FontSizeInflationFor(aFrame));
rc->SetFont(fm);
mWidth = nsLayoutUtils::GetStringWidth(aFrame, rc, mStyle->mString.get(),
mStyle->mString.Length());
}
mIntrinsicWidth = mWidth;
mInitialized = true;
}

Просмотреть файл

@ -11,7 +11,9 @@
#include "nsLineBox.h"
#include "nsStyleStruct.h"
#include "nsTHashtable.h"
class nsIScrollableFrame;
class gfxTextRun;
namespace mozilla {
namespace css {
@ -215,10 +217,8 @@ class TextOverflow {
// The current width of the marker, the range is [0 .. mIntrinsicWidth].
nscoord mWidth;
// The intrinsic width of the marker string.
// The intrinsic width of the marker.
nscoord mIntrinsicWidth;
// The marker text.
nsString mMarkerString;
// The style for this side.
const nsStyleTextOverflowSide* mStyle;
// True if there is visible overflowing inline content on this side.