зеркало из https://github.com/mozilla/gecko-dev.git
Bug 840431 - Cache an ellipsis textrun on gfxFontGroup. r=roc, a=tef+
This commit is contained in:
Родитель
56fd7845ea
Коммит
bf4c3680f3
|
@ -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(), ¶ms, 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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче