diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index dfc7d6591b59..d8cafc586385 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -8,8 +8,11 @@ #include +#include "gfx2DGlue.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Helpers.h" #include "mozilla/HashFunctions.h" #include "mozilla/MathAlgorithms.h" @@ -4014,13 +4017,13 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, // End table border-collapsing section -gfxRect +Rect nsCSSRendering::ExpandPaintingRectForDecorationLine( nsIFrame* aFrame, const uint8_t aStyle, - const gfxRect& aClippedRect, - const gfxFloat aICoordInFrame, - const gfxFloat aCycleLength, + const Rect& aClippedRect, + const Float aICoordInFrame, + const Float aCycleLength, bool aVertical) { switch (aStyle) { @@ -4049,12 +4052,12 @@ nsCSSRendering::ExpandPaintingRectForDecorationLine( NS_ENSURE_TRUE(block, aClippedRect); nsPresContext *pc = aFrame->PresContext(); - gfxFloat framePosInBlock = pc->AppUnitsToGfxUnits(framePosInBlockAppUnits); + Float framePosInBlock = Float(pc->AppUnitsToGfxUnits(framePosInBlockAppUnits)); int32_t rectPosInBlock = int32_t(NS_round(framePosInBlock + aICoordInFrame)); int32_t extraStartEdge = rectPosInBlock - (rectPosInBlock / int32_t(aCycleLength) * aCycleLength); - gfxRect rect(aClippedRect); + Rect rect(aClippedRect); if (aVertical) { rect.y -= extraStartEdge; rect.height += extraStartEdge; @@ -4067,11 +4070,11 @@ nsCSSRendering::ExpandPaintingRectForDecorationLine( void nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, - gfxContext* aGfxContext, - const gfxRect& aDirtyRect, + DrawTarget& aDrawTarget, + const Rect& aDirtyRect, const nscolor aColor, const gfxPoint& aPt, - const gfxFloat aICoordInFrame, + const Float aICoordInFrame, const gfxSize& aLineSize, const gfxFloat aAscent, const gfxFloat aOffset, @@ -4082,10 +4085,10 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, { NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none"); - gfxRect rect = + Rect rect = ToRect( GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset, aDecoration, aStyle, aVertical, - aDescentLimit); + aDescentLimit)); if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) { return; } @@ -4097,26 +4100,28 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, return; } - gfxFloat lineThickness = std::max(NS_round(aLineSize.height), 1.0); - bool contextIsSaved = false; + Float lineThickness = std::max(NS_round(aLineSize.height), 1.0); - gfxFloat oldLineWidth; - nsRefPtr oldPattern; + ColorPattern color(ToDeviceColor(aColor)); + StrokeOptions strokeOptions(lineThickness); + DrawOptions drawOptions; + + Float dash[2]; + + AutoPopClips autoPopClips(&aDrawTarget); switch (aStyle) { case NS_STYLE_TEXT_DECORATION_STYLE_SOLID: case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE: - oldLineWidth = aGfxContext->CurrentLineWidth(); - oldPattern = aGfxContext->GetPattern(); break; case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: { - aGfxContext->Save(); - contextIsSaved = true; - aGfxContext->Clip(rect); - gfxFloat dashWidth = lineThickness * DOT_LENGTH * DASH_LENGTH; - gfxFloat dash[2] = { dashWidth, dashWidth }; - aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT); - aGfxContext->SetDash(dash, 2, 0.0); + autoPopClips.PushClipRect(rect); + Float dashWidth = lineThickness * DOT_LENGTH * DASH_LENGTH; + dash[0] = dashWidth; + dash[1] = dashWidth; + strokeOptions.mDashPattern = dash; + strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash); + strokeOptions.mLineCap = CapStyle::BUTT; rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect, aICoordInFrame, dashWidth * 2, @@ -4126,20 +4131,18 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, break; } case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: { - aGfxContext->Save(); - contextIsSaved = true; - aGfxContext->Clip(rect); - gfxFloat dashWidth = lineThickness * DOT_LENGTH; - gfxFloat dash[2]; + autoPopClips.PushClipRect(rect); + Float dashWidth = lineThickness * DOT_LENGTH; if (lineThickness > 2.0) { - dash[0] = 0.0; - dash[1] = dashWidth * 2.0; - aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND); + dash[0] = 0.f; + dash[1] = dashWidth * 2.f; + strokeOptions.mLineCap = CapStyle::ROUND; } else { dash[0] = dashWidth; dash[1] = dashWidth; } - aGfxContext->SetDash(dash, 2, 0.0); + strokeOptions.mDashPattern = dash; + strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash); rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect, aICoordInFrame, dashWidth * 2, @@ -4149,16 +4152,14 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, break; } case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: - aGfxContext->Save(); - contextIsSaved = true; - aGfxContext->Clip(rect); + autoPopClips.PushClipRect(rect); if (lineThickness > 2.0) { - aGfxContext->SetAntialiasMode(AntialiasMode::SUBPIXEL); + drawOptions.mAntialiasMode = AntialiasMode::SUBPIXEL; } else { // Don't use anti-aliasing here. Because looks like lighter color wavy // line at this case. And probably, users don't think the // non-anti-aliased wavy line is not pretty. - aGfxContext->SetAntialiasMode(AntialiasMode::NONE); + drawOptions.mAntialiasMode = AntialiasMode::NONE; } break; default: @@ -4169,23 +4170,16 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, // The y position should be set to the middle of the line. rect.y += lineThickness / 2; - aGfxContext->SetColor(gfxRGBA(aColor)); - aGfxContext->SetLineWidth(lineThickness); switch (aStyle) { case NS_STYLE_TEXT_DECORATION_STYLE_SOLID: case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: - case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: - aGfxContext->NewPath(); - if (aVertical) { - aGfxContext->MoveTo(rect.TopLeft()); - aGfxContext->LineTo(rect.BottomLeft()); - } else { - aGfxContext->MoveTo(rect.TopLeft()); - aGfxContext->LineTo(rect.TopRight()); - } - aGfxContext->Stroke(); - break; - case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE: + case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: { + Point p1 = rect.TopLeft(); + Point p2 = aVertical ? rect.BottomLeft() : rect.TopRight(); + aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions); + return; + } + case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE: { /** * We are drawing double line as: * @@ -4200,22 +4194,21 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v * +-------------------------------------------+ */ - aGfxContext->NewPath(); + Point p1 = rect.TopLeft(); + Point p2 = aVertical ? rect.BottomLeft() : rect.TopRight(); + aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions); + if (aVertical) { - aGfxContext->MoveTo(rect.TopLeft()); - aGfxContext->LineTo(rect.BottomLeft()); rect.width -= lineThickness; - aGfxContext->MoveTo(rect.TopRight()); - aGfxContext->LineTo(rect.BottomRight()); } else { - aGfxContext->MoveTo(rect.TopLeft()); - aGfxContext->LineTo(rect.TopRight()); rect.height -= lineThickness; - aGfxContext->MoveTo(rect.BottomLeft()); - aGfxContext->LineTo(rect.BottomRight()); } - aGfxContext->Stroke(); - break; + + p1 = aVertical ? rect.TopRight() : rect.BottomLeft(); + p2 = rect.BottomRight(); + aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions); + return; + } case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: { /** * We are drawing wavy line as: @@ -4249,23 +4242,23 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, * directions in the above description. */ - gfxFloat& rectICoord = aVertical ? rect.y : rect.x; - gfxFloat& rectISize = aVertical ? rect.height : rect.width; - const gfxFloat rectBSize = aVertical ? rect.width : rect.height; + Float& rectICoord = aVertical ? rect.y : rect.x; + Float& rectISize = aVertical ? rect.height : rect.width; + const Float rectBSize = aVertical ? rect.width : rect.height; - const gfxFloat adv = rectBSize - lineThickness; - const gfxFloat flatLengthAtVertex = + const Float adv = rectBSize - lineThickness; + const Float flatLengthAtVertex = std::max((lineThickness - 1.0) * 2.0, 1.0); // Align the start of wavy lines to the nearest ancestor block. - const gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex); + const Float cycleLength = 2 * (adv + flatLengthAtVertex); rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect, aICoordInFrame, cycleLength, aVertical); // 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 - const gfxFloat dirtyRectICoord = aVertical ? aDirtyRect.y : aDirtyRect.x; + const Float dirtyRectICoord = aVertical ? aDirtyRect.y : aDirtyRect.x; int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength); if (skipCycles > 0) { rectICoord += skipCycles * cycleLength; @@ -4273,28 +4266,29 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, } rectICoord += lineThickness / 2.0; - gfxPoint pt(rect.TopLeft()); - gfxFloat& ptICoord = aVertical ? pt.y : pt.x; - gfxFloat& ptBCoord = aVertical ? pt.x : pt.y; + Point pt(rect.TopLeft()); + Float& ptICoord = aVertical ? pt.y : pt.x; + Float& ptBCoord = aVertical ? pt.x : pt.y; if (aVertical) { ptBCoord += adv + lineThickness / 2.0; } - gfxFloat iCoordLimit = ptICoord + rectISize + lineThickness; + Float iCoordLimit = ptICoord + rectISize + lineThickness; - const gfxFloat dirtyRectIMost = aVertical ? + const Float dirtyRectIMost = aVertical ? aDirtyRect.YMost() : aDirtyRect.XMost(); skipCycles = floor((iCoordLimit - dirtyRectIMost) / cycleLength); if (skipCycles > 0) { iCoordLimit -= skipCycles * cycleLength; } - aGfxContext->NewPath(); + RefPtr builder = aDrawTarget.CreatePathBuilder(); + RefPtr path; ptICoord -= lineThickness; - aGfxContext->MoveTo(pt); // 1 + builder->MoveTo(pt); // 1 ptICoord = rectICoord; - aGfxContext->LineTo(pt); // 2 + builder->LineTo(pt); // 2 // In vertical mode, to go "down" relative to the text we need to // decrease the block coordinate, whereas in horizontal we increase @@ -4305,34 +4299,28 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, 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); + path = builder->Finish(); + aDrawTarget.Stroke(path, color, strokeOptions, drawOptions); + builder = aDrawTarget.CreatePathBuilder(); + builder->MoveTo(pt); iter = 0; } ptICoord += adv; ptBCoord += goDown ? adv : -adv; - aGfxContext->LineTo(pt); // 3 and 5 + builder->LineTo(pt); // 3 and 5 ptICoord += flatLengthAtVertex; - aGfxContext->LineTo(pt); // 4 and 6 + builder->LineTo(pt); // 4 and 6 goDown = !goDown; } - aGfxContext->Stroke(); - break; + path = builder->Finish(); + aDrawTarget.Stroke(path, color, strokeOptions, drawOptions); + return; } default: NS_ERROR("Invalid style value!"); - break; - } - - if (contextIsSaved) { - aGfxContext->Restore(); - } else { - aGfxContext->SetPattern(oldPattern); - aGfxContext->SetLineWidth(oldLineWidth); } } diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index 0d45bd6fa83e..9fce2f149d3b 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -10,6 +10,7 @@ #include "gfxBlur.h" #include "gfxContext.h" +#include "mozilla/gfx/Rect.h" #include "nsLayoutUtils.h" #include "nsStyleStruct.h" #include "nsIFrame.h" @@ -21,6 +22,10 @@ class nsRenderingContext; namespace mozilla { +namespace gfx { +class DrawTarget; +} + namespace layers { class ImageContainer; } @@ -311,6 +316,9 @@ struct nsBackgroundLayerState { }; struct nsCSSRendering { + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::Float Float; + typedef mozilla::gfx::Rect Rect; typedef nsIFrame::Sides Sides; /** @@ -669,11 +677,11 @@ struct nsCSSRendering { * used for strikeout line and overline too. */ static void PaintDecorationLine(nsIFrame* aFrame, - gfxContext* aGfxContext, - const gfxRect& aDirtyRect, + DrawTarget& aDrawTarget, + const Rect& aDirtyRect, const nscolor aColor, const gfxPoint& aPt, - const gfxFloat aICoordInFrame, + const Float aICoordInFrame, const gfxSize& aLineSize, const gfxFloat aAscent, const gfxFloat aOffset, @@ -798,12 +806,12 @@ protected: * and aClippedRect.pos. * @param aCycleLength the width of one cycle of the line style. */ - static gfxRect ExpandPaintingRectForDecorationLine( + static Rect ExpandPaintingRectForDecorationLine( nsIFrame* aFrame, const uint8_t aStyle, - const gfxRect &aClippedRect, - const gfxFloat aICoordInFrame, - const gfxFloat aCycleLength, + const Rect &aClippedRect, + const Float aICoordInFrame, + const Float aCycleLength, bool aVertical); }; diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index fd8e41a41445..13b254e3e37c 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -7,6 +7,7 @@ #include "nsTextFrame.h" +#include "gfx2DGlue.h" #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/Likely.h" @@ -88,6 +89,7 @@ using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::gfx; struct TabWidth { TabWidth(uint32_t aOffset, uint32_t aWidth) @@ -5154,8 +5156,9 @@ PaintDecorationLine(nsIFrame* aFrame, aCallbacks->NotifySelectionDecorationLinePathEmitted(); } } else { - nsCSSRendering::PaintDecorationLine(aFrame, aCtx, aDirtyRect, lineColor, - aPt, aICoordInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle, + nsCSSRendering::PaintDecorationLine(aFrame, *aCtx->GetDrawTarget(), + ToRect(aDirtyRect), lineColor, + aPt, Float(aICoordInFrame), aLineSize, aAscent, aOffset, aDecoration, aStyle, aVertical, aDescentLimit); } } diff --git a/layout/generic/test/test_selection_underline.html b/layout/generic/test/test_selection_underline.html index c2b65c401d06..4112e114e5b2 100644 --- a/layout/generic/test/test_selection_underline.html +++ b/layout/generic/test/test_selection_underline.html @@ -262,6 +262,10 @@ function doTest(aTest) aTest.decoration.style == kDecorationStyleWavy) { todo(false, "Rendering of" + description); } else { + var fuzz = null; // no fuzz + if (navigator.userAgent.indexOf("Windows NT 6.2") > -1) { // Win8 + fuzz = { maxDifference: 1, numDifferentPixels: 200 }; + } assertSnapshots(result.snapshot, reference.snapshot, true, null /*no fuzz*/, description, ""); } diff --git a/layout/xul/nsTextBoxFrame.cpp b/layout/xul/nsTextBoxFrame.cpp index dc01d3236f68..da28522daefa 100644 --- a/layout/xul/nsTextBoxFrame.cpp +++ b/layout/xul/nsTextBoxFrame.cpp @@ -5,6 +5,7 @@ #include "nsTextBoxFrame.h" +#include "gfx2DGlue.h" #include "gfxUtils.h" #include "mozilla/gfx/2D.h" #include "nsFontMetrics.h" @@ -467,7 +468,7 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext, presContext->AppUnitsToGfxUnits(aTextRect.y)); gfxFloat width = presContext->AppUnitsToGfxUnits(aTextRect.width); gfxFloat ascentPixel = presContext->AppUnitsToGfxUnits(ascent); - gfxFloat xInFrame = PresContext()->AppUnitsToGfxUnits(mTextDrawRect.x); + Float xInFrame = Float(PresContext()->AppUnitsToGfxUnits(mTextDrawRect.x)); gfxRect dirtyRect(presContext->AppUnitsToGfxUnits(aDirtyRect)); // Underlines are drawn before overlines, and both before the text @@ -482,7 +483,8 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext, gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size); if ((decorations & NS_FONT_DECORATION_UNDERLINE) && underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, underColor, + nsCSSRendering::PaintDecorationLine(this, *drawTarget, + ToRect(dirtyRect), underColor, pt, xInFrame, gfxSize(width, sizePixel), ascentPixel, offsetPixel, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle, @@ -490,7 +492,8 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext, } if ((decorations & NS_FONT_DECORATION_OVERLINE) && overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, overColor, + nsCSSRendering::PaintDecorationLine(this, *drawTarget, + ToRect(dirtyRect), overColor, pt, xInFrame, gfxSize(width, sizePixel), ascentPixel, ascentPixel, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle, @@ -572,7 +575,8 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext, fontMet->GetStrikeout(offset, size); gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset); gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size); - nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, strikeColor, + nsCSSRendering::PaintDecorationLine(this, *drawTarget, ToRect(dirtyRect), + strikeColor, pt, xInFrame, gfxSize(width, sizePixel), ascentPixel, offsetPixel, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, strikeStyle, vertical);