bug 686497 - avoid passing excessively long wavy-underline path to cairo for stroking. r=roc

This commit is contained in:
Jonathan Kew 2011-09-16 20:23:29 +01:00
Родитель 44e05fde0e
Коммит ee56ede693
6 изменённых файлов: 69 добавлений и 26 удалений

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

@ -3367,6 +3367,7 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext,
void
nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
const gfxRect& aDirtyRect,
const nscolor aColor,
const gfxPoint& aPt,
const gfxSize& aLineSize,
@ -3381,8 +3382,9 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
gfxRect rect =
GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
aDecoration, aStyle, aDescentLimit);
if (rect.IsEmpty())
if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
return;
}
if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
@ -3524,14 +3526,30 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
* 7. Repeat from 2 until reached to right-most edge of the area.
*/
rect.x += lineHeight / 2.0;
aGfxContext->NewPath();
gfxPoint pt(rect.TopLeft());
gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
gfxFloat adv = rect.Height() - lineHeight;
gfxFloat flatLengthAtVertex = NS_MAX((lineHeight - 1.0) * 2.0, 1.0);
// figure out if we can trim whole cycles from the left and right edges
// of the line, to try and avoid creating an unnecessarily long and
// complex path
gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
PRInt32 skipCycles = floor((aDirtyRect.x - rect.x) / cycleLength);
if (skipCycles > 0) {
rect.x += skipCycles * cycleLength;
rect.width -= skipCycles * cycleLength;
}
rect.x += lineHeight / 2.0;
gfxPoint pt(rect.TopLeft());
gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
skipCycles = floor((rightMost - aDirtyRect.XMost()) / cycleLength);
if (skipCycles > 0) {
rightMost -= skipCycles * cycleLength;
}
aGfxContext->NewPath();
pt.x -= lineHeight;
aGfxContext->MoveTo(pt); // 1
@ -3539,7 +3557,16 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
aGfxContext->LineTo(pt); // 2
PRBool goDown = PR_TRUE;
PRUint32 iter = 0;
while (pt.x < rightMost) {
if (++iter > 1000) {
// stroke the current path and start again, to avoid pathological
// behavior in cairo with huge numbers of path segments
aGfxContext->Stroke();
aGfxContext->NewPath();
aGfxContext->MoveTo(pt);
iter = 0;
}
pt.x += adv;
pt.y += goDown ? adv : -adv;

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

@ -292,6 +292,7 @@ struct nsCSSRendering {
* not app units.
* input:
* @param aGfxContext
* @param aDirtyRect no need to paint outside this rect
* @param aColor the color of the decoration line
* @param aPt the top/left edge of the text
* @param aLineSize the width and the height of the decoration
@ -319,6 +320,7 @@ struct nsCSSRendering {
* used for strikeout line and overline too.
*/
static void PaintDecorationLine(gfxContext* aGfxContext,
const gfxRect& aDirtyRect,
const nscolor aColor,
const gfxPoint& aPt,
const gfxSize& aLineSize,

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

@ -514,6 +514,7 @@ protected:
PRBool aDrawSoftHyphen);
void DrawTextRunAndDecorations(gfxContext* const aCtx,
const gfxRect& aDirtyRect,
const gfxPoint& aFramePt,
const gfxPoint& aTextBaselinePt,
PRUint32 aOffset,
@ -527,6 +528,7 @@ protected:
const nscolor* const aDecorationOverrideColor);
void DrawText(gfxContext* const aCtx,
const gfxRect& aDirtyRect,
const gfxPoint& aFramePt,
const gfxPoint& aTextBaselinePt,
PRUint32 aOffset,

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

@ -4526,7 +4526,9 @@ ComputeSelectionUnderlineHeight(nsPresContext* aPresContext,
* This, plus SelectionTypesWithDecorations, encapsulates all knowledge about
* drawing text decoration for selections.
*/
static void DrawSelectionDecorations(gfxContext* aContext, SelectionType aType,
static void DrawSelectionDecorations(gfxContext* aContext,
const gfxRect& aDirtyRect,
SelectionType aType,
nsTextFrame* aFrame,
nsTextPaintStyle& aTextPaintStyle,
const nsTextRangeStyle &aRangeStyle,
@ -4608,7 +4610,7 @@ static void DrawSelectionDecorations(gfxContext* aContext, SelectionType aType,
}
size.height *= relativeSize;
nsCSSRendering::PaintDecorationLine(
aContext, color, pt, size, aAscent, aFontMetrics.underlineOffset,
aContext, aDirtyRect, color, pt, size, aAscent, aFontMetrics.underlineOffset,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, style, descentLimit);
}
@ -4844,7 +4846,9 @@ nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
// Remember that the box blur context has a device offset on it, so we don't need to
// translate any coordinates to fit on the surface.
gfxFloat advanceWidth;
DrawText(shadowContext, aFramePt + shadowOffset,
gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
aDirtyRect.width, aDirtyRect.height);
DrawText(shadowContext, dirtyRect, aFramePt + shadowOffset,
aTextBaselinePt + shadowOffset, aOffset, aLength, *aProvider,
nsTextPaintStyle(this), aClipEdges, advanceWidth,
(GetStateBits() & TEXT_HYPHEN_BREAK) != 0, decorationOverrideColor);
@ -4955,7 +4959,7 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
aCtx->SetColor(gfxRGBA(foreground));
gfxFloat advance;
DrawText(aCtx, aFramePt, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y),
DrawText(aCtx, aDirtyRect, aFramePt, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y),
offset, length, aProvider, aTextPaintStyle, aClipEdges, advance,
hyphenWidth > 0);
if (hyphenWidth) {
@ -5016,6 +5020,8 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
PRInt32 app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
// XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
gfxPoint pt(0.0, (aTextBaselinePt.y - mAscent) / app);
gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
aDirtyRect.width / app, aDirtyRect.height / app);
SelectionType type;
nsTextRangeStyle selectedStyle;
while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
@ -5026,7 +5032,7 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
pt.x = (aFramePt.x + xOffset -
(mTextRun->IsRightToLeft() ? advance : 0)) / app;
gfxFloat width = NS_ABS(advance) / app;
DrawSelectionDecorations(aCtx, aSelectionType, this, aTextPaintStyle,
DrawSelectionDecorations(aCtx, dirtyRect, aSelectionType, this, aTextPaintStyle,
selectedStyle,
pt, width, mAscent / app, decorationMetrics);
}
@ -5306,7 +5312,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
ctx->SetColor(gfxRGBA(foregroundColor));
gfxFloat advanceWidth;
DrawText(ctx, framePt, textBaselinePt, startOffset, maxLength, provider,
DrawText(ctx, dirtyRect, framePt, textBaselinePt, startOffset, maxLength, provider,
textPaintStyle, clipEdges, advanceWidth,
(GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
}
@ -5339,7 +5345,7 @@ nsTextFrame::DrawTextRun(gfxContext* const aCtx,
void
nsTextFrame::DrawTextRunAndDecorations(
gfxContext* const aCtx,
gfxContext* const aCtx, const gfxRect& aDirtyRect,
const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
PRUint32 aOffset, PRUint32 aLength,
PropertyProvider& aProvider,
@ -5362,6 +5368,9 @@ nsTextFrame::DrawTextRunAndDecorations(
const gfxFloat ascent = gfxFloat(mAscent) / app;
const gfxFloat frameTop = aFramePt.y;
gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
aDirtyRect.Width() / app, aDirtyRect.Height() / app);
// Underlines
for (PRUint32 i = aDecorations.mUnderlines.Length(); i-- > 0; ) {
const LineDecoration& dec = aDecorations.mUnderlines[i];
@ -5373,7 +5382,7 @@ nsTextFrame::DrawTextRunAndDecorations(
decPt.y = (frameTop - dec.mBaselineOffset) / app;
const nscolor lineColor = aDecorationOverrideColor ? *aDecorationOverrideColor : dec.mColor;
nsCSSRendering::PaintDecorationLine(aCtx, lineColor, decPt, decSize, ascent,
nsCSSRendering::PaintDecorationLine(aCtx, dirtyRect, lineColor, decPt, decSize, ascent,
metrics.underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
dec.mStyle);
}
@ -5388,7 +5397,7 @@ nsTextFrame::DrawTextRunAndDecorations(
decPt.y = (frameTop - dec.mBaselineOffset) / app;
const nscolor lineColor = aDecorationOverrideColor ? *aDecorationOverrideColor : dec.mColor;
nsCSSRendering::PaintDecorationLine(aCtx, lineColor, decPt, decSize, ascent,
nsCSSRendering::PaintDecorationLine(aCtx, dirtyRect, lineColor, decPt, decSize, ascent,
metrics.maxAscent, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle);
}
@ -5408,7 +5417,7 @@ nsTextFrame::DrawTextRunAndDecorations(
decPt.y = (frameTop - dec.mBaselineOffset) / app;
const nscolor lineColor = aDecorationOverrideColor ? *aDecorationOverrideColor : dec.mColor;
nsCSSRendering::PaintDecorationLine(aCtx, lineColor, decPt, decSize, ascent,
nsCSSRendering::PaintDecorationLine(aCtx, dirtyRect, lineColor, decPt, decSize, ascent,
metrics.strikeoutOffset, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
dec.mStyle);
}
@ -5416,7 +5425,7 @@ nsTextFrame::DrawTextRunAndDecorations(
void
nsTextFrame::DrawText(
gfxContext* const aCtx,
gfxContext* const aCtx, const gfxRect& aDirtyRect,
const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
PRUint32 aOffset, PRUint32 aLength,
PropertyProvider& aProvider,
@ -5433,7 +5442,7 @@ nsTextFrame::DrawText(
const bool drawDecorations = !aProvider.GetFontGroup()->ShouldSkipDrawing() &&
decorations.HasDecorationLines();
if (drawDecorations) {
DrawTextRunAndDecorations(aCtx, aFramePt, aTextBaselinePt, aOffset, aLength,
DrawTextRunAndDecorations(aCtx, aDirtyRect, aFramePt, aTextBaselinePt, aOffset, aLength,
aProvider, aTextStyle, aClipEdges, aAdvanceWidth,
aDrawSoftHyphen, decorations,
aDecorationOverrideColor);

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

@ -413,11 +413,12 @@ nsTextBoxFrame::PaintTitle(nsRenderingContext& aRenderingContext,
if (mTitle.IsEmpty())
return;
DrawText(aRenderingContext, mTextDrawRect + aPt, aOverrideColor);
DrawText(aRenderingContext, aDirtyRect, mTextDrawRect + aPt, aOverrideColor);
}
void
nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aTextRect,
const nscolor* aOverrideColor)
{
@ -497,6 +498,7 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
presContext->AppUnitsToGfxUnits(aTextRect.y));
gfxFloat width = presContext->AppUnitsToGfxUnits(aTextRect.width);
gfxFloat ascentPixel = presContext->AppUnitsToGfxUnits(ascent);
gfxRect dirtyRect(presContext->AppUnitsToGfxUnits(aDirtyRect));
// Underlines are drawn before overlines, and both before the text
// itself, per http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
@ -510,14 +512,14 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
if ((decorations & NS_FONT_DECORATION_UNDERLINE) &&
underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
nsCSSRendering::PaintDecorationLine(ctx, underColor,
nsCSSRendering::PaintDecorationLine(ctx, dirtyRect, underColor,
pt, gfxSize(width, sizePixel),
ascentPixel, offsetPixel,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle);
}
if ((decorations & NS_FONT_DECORATION_OVERLINE) &&
overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
nsCSSRendering::PaintDecorationLine(ctx, overColor,
nsCSSRendering::PaintDecorationLine(ctx, dirtyRect, overColor,
pt, gfxSize(width, sizePixel),
ascentPixel, ascentPixel,
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle);
@ -599,7 +601,7 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
fontMet->GetStrikeout(offset, size);
gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset);
gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
nsCSSRendering::PaintDecorationLine(ctx, strikeColor,
nsCSSRendering::PaintDecorationLine(ctx, dirtyRect, strikeColor,
pt, gfxSize(width, sizePixel), ascentPixel, offsetPixel,
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
strikeStyle);

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

@ -133,6 +133,7 @@ private:
PRBool InsertSeparatorBeforeAccessKey();
void DrawText(nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aTextRect,
const nscolor* aOverrideColor);