Bug 1074735 pt 2 - Support drawing CSS text decorations (underline, overline, strikeout) on vertical text frames. r=smontagu

This commit is contained in:
Jonathan Kew 2014-10-08 10:32:55 +01:00
parent 5194872bdc
commit 45724811c5
4 changed files with 264 additions and 144 deletions

View File

@ -3817,11 +3817,13 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext,
// End table border-collapsing section
gfxRect
nsCSSRendering::ExpandPaintingRectForDecorationLine(nsIFrame* aFrame,
const uint8_t aStyle,
const gfxRect& aClippedRect,
const gfxFloat aXInFrame,
const gfxFloat aCycleLength)
nsCSSRendering::ExpandPaintingRectForDecorationLine(
nsIFrame* aFrame,
const uint8_t aStyle,
const gfxRect& aClippedRect,
const gfxFloat aICoordInFrame,
const gfxFloat aCycleLength,
bool aVertical)
{
switch (aStyle) {
case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
@ -3836,25 +3838,32 @@ nsCSSRendering::ExpandPaintingRectForDecorationLine(nsIFrame* aFrame,
nsBlockFrame* block = nullptr;
// Note that when we paint the decoration lines in relative positioned
// box, we should paint them like all of the boxes are positioned as static.
nscoord frameXInBlockAppUnits = 0;
nscoord framePosInBlockAppUnits = 0;
for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
block = do_QueryFrame(f);
if (block) {
break;
}
frameXInBlockAppUnits += f->GetNormalPosition().x;
framePosInBlockAppUnits += aVertical ?
f->GetNormalPosition().y : f->GetNormalPosition().x;
}
NS_ENSURE_TRUE(block, aClippedRect);
nsPresContext *pc = aFrame->PresContext();
gfxFloat frameXInBlock = pc->AppUnitsToGfxUnits(frameXInBlockAppUnits);
int32_t rectXInBlock = int32_t(NS_round(frameXInBlock + aXInFrame));
int32_t extraLeft =
rectXInBlock - (rectXInBlock / int32_t(aCycleLength) * aCycleLength);
gfxFloat framePosInBlock = 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.x -= extraLeft;
rect.width += extraLeft;
if (aVertical) {
rect.y -= extraStartEdge;
rect.height += extraStartEdge;
} else {
rect.x -= extraStartEdge;
rect.width += extraStartEdge;
}
return rect;
}
@ -3864,19 +3873,21 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
const gfxRect& aDirtyRect,
const nscolor aColor,
const gfxPoint& aPt,
const gfxFloat aXInFrame,
const gfxFloat aICoordInFrame,
const gfxSize& aLineSize,
const gfxFloat aAscent,
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
bool aVertical,
const gfxFloat aDescentLimit)
{
NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
gfxRect rect =
GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
aDecoration, aStyle, aDescentLimit);
aDecoration, aStyle, aVertical,
aDescentLimit);
if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
return;
}
@ -3888,7 +3899,7 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
return;
}
gfxFloat lineHeight = std::max(NS_round(aLineSize.height), 1.0);
gfxFloat lineThickness = std::max(NS_round(aLineSize.height), 1.0);
bool contextIsSaved = false;
gfxFloat oldLineWidth;
@ -3904,12 +3915,14 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
aGfxContext->Save();
contextIsSaved = true;
aGfxContext->Clip(rect);
gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH;
gfxFloat dashWidth = lineThickness * DOT_LENGTH * DASH_LENGTH;
gfxFloat dash[2] = { dashWidth, dashWidth };
aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
aGfxContext->SetDash(dash, 2, 0.0);
rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
aXInFrame, dashWidth * 2);
aICoordInFrame,
dashWidth * 2,
aVertical);
// We should continue to draw the last dash even if it is not in the rect.
rect.width += dashWidth;
break;
@ -3918,9 +3931,9 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
aGfxContext->Save();
contextIsSaved = true;
aGfxContext->Clip(rect);
gfxFloat dashWidth = lineHeight * DOT_LENGTH;
gfxFloat dashWidth = lineThickness * DOT_LENGTH;
gfxFloat dash[2];
if (lineHeight > 2.0) {
if (lineThickness > 2.0) {
dash[0] = 0.0;
dash[1] = dashWidth * 2.0;
aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
@ -3930,7 +3943,9 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
}
aGfxContext->SetDash(dash, 2, 0.0);
rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
aXInFrame, dashWidth * 2);
aICoordInFrame,
dashWidth * 2,
aVertical);
// We should continue to draw the last dot even if it is not in the rect.
rect.width += dashWidth;
break;
@ -3939,7 +3954,7 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
aGfxContext->Save();
contextIsSaved = true;
aGfxContext->Clip(rect);
if (lineHeight > 2.0) {
if (lineThickness > 2.0) {
aGfxContext->SetAntialiasMode(AntialiasMode::SUBPIXEL);
} else {
// Don't use anti-aliasing here. Because looks like lighter color wavy
@ -3954,15 +3969,22 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
}
// The y position should be set to the middle of the line.
rect.y += lineHeight / 2;
rect.y += lineThickness / 2;
aGfxContext->SetColor(gfxRGBA(aColor));
aGfxContext->SetLineWidth(lineHeight);
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();
aGfxContext->MoveTo(rect.TopLeft());
aGfxContext->LineTo(rect.TopRight());
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:
@ -3971,28 +3993,29 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
*
* +-------------------------------------------+
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
* | |
* | |
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
* +-------------------------------------------+
*/
aGfxContext->NewPath();
aGfxContext->MoveTo(rect.TopLeft());
aGfxContext->LineTo(rect.TopRight());
rect.height -= lineHeight;
aGfxContext->MoveTo(rect.BottomLeft());
aGfxContext->LineTo(rect.BottomRight());
aGfxContext->Stroke();
break;
case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
aGfxContext->NewPath();
aGfxContext->MoveTo(rect.TopLeft());
aGfxContext->LineTo(rect.TopRight());
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;
case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: {
@ -4023,44 +4046,64 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
* 5. Goes up to top of the area at 45 degrees.
* 6. Slides to right horizontaly.
* 7. Repeat from 2 until reached to right-most edge of the area.
*
* In the vertical case, swap horizontal and vertical coordinates and
* directions in the above description.
*/
gfxFloat adv = rect.Height() - lineHeight;
gfxFloat flatLengthAtVertex = std::max((lineHeight - 1.0) * 2.0, 1.0);
gfxFloat& rectICoord = aVertical ? rect.y : rect.x;
gfxFloat& rectISize = aVertical ? rect.height : rect.width;
const gfxFloat rectBSize = aVertical ? rect.width : rect.height;
const gfxFloat adv = rectBSize - lineThickness;
const gfxFloat flatLengthAtVertex =
std::max((lineThickness - 1.0) * 2.0, 1.0);
// Align the start of wavy lines to the nearest ancestor block.
gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
const gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
aXInFrame, cycleLength);
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
int32_t skipCycles = floor((aDirtyRect.x - rect.x) / cycleLength);
const gfxFloat dirtyRectICoord = aVertical ? aDirtyRect.y : aDirtyRect.x;
int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength);
if (skipCycles > 0) {
rect.x += skipCycles * cycleLength;
rect.width -= skipCycles * cycleLength;
rectICoord += skipCycles * cycleLength;
rectISize -= skipCycles * cycleLength;
}
rect.x += lineHeight / 2.0;
rectICoord += lineThickness / 2.0;
gfxPoint pt(rect.TopLeft());
gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
gfxFloat& ptICoord = aVertical ? pt.y : pt.x;
gfxFloat& ptBCoord = aVertical ? pt.x : pt.y;
if (aVertical) {
ptBCoord += adv + lineThickness / 2.0;
}
gfxFloat iCoordLimit = ptICoord + rectISize + lineThickness;
skipCycles = floor((rightMost - aDirtyRect.XMost()) / cycleLength);
const gfxFloat dirtyRectIMost = aVertical ?
aDirtyRect.YMost() : aDirtyRect.XMost();
skipCycles = floor((iCoordLimit - dirtyRectIMost) / cycleLength);
if (skipCycles > 0) {
rightMost -= skipCycles * cycleLength;
iCoordLimit -= skipCycles * cycleLength;
}
aGfxContext->NewPath();
pt.x -= lineHeight;
ptICoord -= lineThickness;
aGfxContext->MoveTo(pt); // 1
pt.x = rect.X();
ptICoord = rectICoord;
aGfxContext->LineTo(pt); // 2
bool goDown = true;
// In vertical mode, to go "down" relative to the text we need to
// decrease the block coordinate, whereas in horizontal we increase
// it. So the sense of this flag is effectively inverted.
bool goDown = aVertical ? false : true;
uint32_t iter = 0;
while (pt.x < rightMost) {
while (ptICoord < iCoordLimit) {
if (++iter > 1000) {
// stroke the current path and start again, to avoid pathological
// behavior in cairo with huge numbers of path segments
@ -4069,12 +4112,12 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
aGfxContext->MoveTo(pt);
iter = 0;
}
pt.x += adv;
pt.y += goDown ? adv : -adv;
ptICoord += adv;
ptBCoord += goDown ? adv : -adv;
aGfxContext->LineTo(pt); // 3 and 5
pt.x += flatLengthAtVertex;
ptICoord += flatLengthAtVertex;
aGfxContext->LineTo(pt); // 4 and 6
goDown = !goDown;
@ -4101,12 +4144,13 @@ nsCSSRendering::DecorationLineToPath(nsIFrame* aFrame,
const gfxRect& aDirtyRect,
const nscolor aColor,
const gfxPoint& aPt,
const gfxFloat aXInFrame,
const gfxFloat aICoordInFrame,
const gfxSize& aLineSize,
const gfxFloat aAscent,
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
bool aVertical,
const gfxFloat aDescentLimit)
{
NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
@ -4115,7 +4159,8 @@ nsCSSRendering::DecorationLineToPath(nsIFrame* aFrame,
gfxRect rect =
GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
aDecoration, aStyle, aDescentLimit);
aDecoration, aStyle, aVertical,
aDescentLimit);
if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
return;
}
@ -4132,14 +4177,14 @@ nsCSSRendering::DecorationLineToPath(nsIFrame* aFrame,
return;
}
gfxFloat lineHeight = std::max(NS_round(aLineSize.height), 1.0);
gfxFloat lineThickness = std::max(NS_round(aLineSize.height), 1.0);
// The y position should be set to the middle of the line.
rect.y += lineHeight / 2;
rect.y += lineThickness / 2;
aGfxContext->Rectangle
(gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineHeight / 2)),
gfxSize(rect.Width(), lineHeight)));
(gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineThickness / 2)),
gfxSize(rect.Width(), lineThickness)));
}
nsRect
@ -4149,6 +4194,7 @@ nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
bool aVertical,
const gfxFloat aDescentLimit)
{
NS_ASSERTION(aPresContext, "aPresContext is null");
@ -4156,7 +4202,8 @@ nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
gfxRect rect =
GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
aDecoration, aStyle, aDescentLimit);
aDecoration, aStyle, aVertical,
aDescentLimit);
// The rect values are already rounded to nearest device pixels.
nsRect r;
r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
@ -4173,6 +4220,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
bool aVertical,
const gfxFloat aDescentLimit)
{
NS_ASSERTION(aStyle <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY,
@ -4183,42 +4231,52 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
bool canLiftUnderline = aDescentLimit >= 0.0;
const gfxFloat left = floor(aPt.x + 0.5),
right = floor(aPt.x + aLineSize.width + 0.5);
gfxFloat iCoord = aVertical ? aPt.y : aPt.x;
gfxFloat bCoord = aVertical ? aPt.x : aPt.y;
// 'left' and 'right' are relative to the line, so for vertical writing modes
// they will actually become top and bottom of the rendered line.
// Similarly, aLineSize.width and .height are actually length and thickness
// of the line, which runs horizontally or vertically according to aVertical.
const gfxFloat left = floor(iCoord + 0.5),
right = floor(iCoord + aLineSize.width + 0.5);
// We compute |r| as if for a horizontal text run, and then swap vertical
// and horizontal coordinates at the end if vertical was requested.
gfxRect r(left, 0, right - left, 0);
gfxFloat lineHeight = NS_round(aLineSize.height);
lineHeight = std::max(lineHeight, 1.0);
gfxFloat lineThickness = NS_round(aLineSize.height);
lineThickness = std::max(lineThickness, 1.0);
gfxFloat ascent = NS_round(aAscent);
gfxFloat descentLimit = floor(aDescentLimit);
gfxFloat suggestedMaxRectHeight = std::max(std::min(ascent, descentLimit), 1.0);
r.height = lineHeight;
r.height = lineThickness;
if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) {
/**
* We will draw double line as:
*
* +-------------------------------------------+
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
* | | ^
* | | | gap
* | | v
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
* +-------------------------------------------+
*/
gfxFloat gap = NS_round(lineHeight / 2.0);
gfxFloat gap = NS_round(lineThickness / 2.0);
gap = std::max(gap, 1.0);
r.height = lineHeight * 2.0 + gap;
r.height = lineThickness * 2.0 + gap;
if (canLiftUnderline) {
if (r.Height() > suggestedMaxRectHeight) {
// Don't shrink the line height, because the thickness has some meaning.
// We can just shrink the gap at this time.
r.height = std::max(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0 + 1.0);
}
}
} else if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
@ -4227,7 +4285,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
*
* +-------------------------------------------+
* |XXXXX XXXXXX XXXXXX | ^
* |XXXXXX XXXXXXXX XXXXXXXX | | lineHeight
* |XXXXXX XXXXXXXX XXXXXXXX | | lineThickness
* |XXXXXXX XXXXXXXXXX XXXXXXXXXX| v
* | XXX XXX XXX XXX XX|
* | XXXXXXXXXX XXXXXXXXXX X|
@ -4235,19 +4293,19 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
* | XXXXXX XXXXXX |
* +-------------------------------------------+
*/
r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
r.height = lineThickness > 2.0 ? lineThickness * 4.0 : lineThickness * 3.0;
if (canLiftUnderline) {
if (r.Height() > suggestedMaxRectHeight) {
// Don't shrink the line height even if there is not enough space,
// because the thickness has some meaning. E.g., the 1px wavy line and
// 2px wavy line can be used for different meaning in IME selections
// at same time.
r.height = std::max(suggestedMaxRectHeight, lineHeight * 2.0);
r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0);
}
}
}
gfxFloat baseline = floor(aPt.y + aAscent + 0.5);
gfxFloat baseline = floor(bCoord + aAscent + 0.5);
gfxFloat offset = 0.0;
switch (aDecoration) {
case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
@ -4265,18 +4323,27 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
}
break;
case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE:
offset = aOffset - lineHeight + r.Height();
offset = aOffset - lineThickness + r.Height();
break;
case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
extra = std::max(extra, lineHeight);
offset = aOffset - lineHeight + extra;
extra = std::max(extra, lineThickness);
offset = aOffset - lineThickness + extra;
break;
}
default:
NS_ERROR("Invalid decoration value!");
}
r.y = baseline - floor(offset + 0.5);
if (aVertical) {
r.y = baseline + floor(aOffset + 0.5); // this will need updating when we
// support sideways-left orientation
Swap(r.x, r.y);
Swap(r.width, r.height);
} else {
r.y = baseline - floor(aOffset + 0.5);
}
return r;
}

View File

@ -598,23 +598,29 @@ struct nsCSSRendering {
* Function for painting the decoration lines for the text.
* NOTE: aPt, aLineSize, aAscent and aOffset are non-rounded device pixels,
* not app units.
* NOTE: aLineSize is a "logical" size in textRun orientation, so that for
* a vertical textrun, aLineSize.width (which is the decoration line
* length) will actually be a physical height; and conversely,
* aLineSize.height [thickness] will be a physical width. The alternate
* names in [brackets] in the comments here apply to the vertical case.
*
* input:
* @param aFrame the frame which needs the decoration line
* @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 aXInFrame the distance between aPt.x and left edge of
* aFrame. If the decoration line is for shadow,
* set the distance between the left edge of
* the aFrame and the position of the text as
* @param aICoordInFrame the distance between aPt.x [y] and left [top]
* edge of aFrame. If the decoration line is for
* shadow, set the distance between the left edge
* of the aFrame and the position of the text as
* positioned without offset of the shadow.
* @param aLineSize the width and the height of the decoration
* line
* @param aLineSize the width [length] and the height [thickness]
* of the decoration line
* @param aAscent the ascent of the text
* @param aOffset the offset of the decoration line from
* the baseline of the text (if the value is
* positive, the line is lifted up)
* positive, the line is lifted up [right])
* @param aDecoration which line will be painted. The value can be
* NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or
* NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or
@ -638,12 +644,13 @@ struct nsCSSRendering {
const gfxRect& aDirtyRect,
const nscolor aColor,
const gfxPoint& aPt,
const gfxFloat aXInFrame,
const gfxFloat aICoordInFrame,
const gfxSize& aLineSize,
const gfxFloat aAscent,
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
bool aVertical,
const gfxFloat aDescentLimit = -1.0);
/**
@ -658,12 +665,13 @@ struct nsCSSRendering {
const gfxRect& aDirtyRect,
const nscolor aColor,
const gfxPoint& aPt,
const gfxFloat aXInFrame,
const gfxFloat aICoordInFrame,
const gfxSize& aLineSize,
const gfxFloat aAscent,
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
bool aVertical,
const gfxFloat aDescentLimit = -1.0);
/**
@ -705,6 +713,7 @@ struct nsCSSRendering {
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
bool aVertical,
const gfxFloat aDescentLimit = -1.0);
static gfxContext::GraphicsOperator GetGFXBlendMode(uint8_t mBlendMode) {
@ -738,7 +747,8 @@ protected:
const gfxFloat aOffset,
const uint8_t aDecoration,
const uint8_t aStyle,
const gfxFloat aDscentLimit);
bool aVertical,
const gfxFloat aDescentLimit);
/**
* Returns inflated rect for painting a decoration line.
@ -755,16 +765,17 @@ protected:
* NS_STYLE_TEXT_DECORATION_STYLE_WAVY.
* @param aClippedRect the clipped rect for the decoration line.
* in other words, visible area of the line.
* @param aXInFrame the distance between left edge of aFrame and
* aClippedRect.pos.x.
* @param aICoordInFrame the distance between inline-start edge of aFrame
* and aClippedRect.pos.
* @param aCycleLength the width of one cycle of the line style.
*/
static gfxRect ExpandPaintingRectForDecorationLine(
nsIFrame* aFrame,
const uint8_t aStyle,
const gfxRect &aClippedRect,
const gfxFloat aXInFrame,
const gfxFloat aCycleLength);
const gfxFloat aICoordInFrame,
const gfxFloat aCycleLength,
bool aVertical);
};
/*

View File

@ -1791,12 +1791,13 @@ GetHyphenTextRun(gfxTextRun* aTextRun, gfxContext* aContext, nsTextFrame* aTextF
}
static gfxFont::Metrics
GetFirstFontMetrics(gfxFontGroup* aFontGroup)
GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVertical)
{
if (!aFontGroup)
return gfxFont::Metrics();
gfxFont* font = aFontGroup->GetFirstValidFont();
return font->GetMetrics(gfxFont::eHorizontal); // XXX vertical
return font->GetMetrics(aVertical ? gfxFont::eVertical :
gfxFont::eHorizontal);
}
PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0);
@ -3117,8 +3118,9 @@ ComputeTabWidthAppUnits(nsIFrame* aFrame, gfxTextRun* aTextRun)
// Round the space width when converting to appunits the same way
// textruns do
gfxFloat spaceWidthAppUnits =
NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup()).spaceWidth *
aTextRun->GetAppUnitsPerDevUnit());
NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
aTextRun->IsVertical()).spaceWidth *
aTextRun->GetAppUnitsPerDevUnit());
return textStyle->mTabSize * spaceWidthAppUnits;
}
@ -4890,6 +4892,7 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
nsRect shadowRect =
nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
bool vertical = mTextRun->IsVertical();
if (IsFloatingFirstLetterChild()) {
// The underline/overline drawable area must be contained in the overflow
@ -4909,7 +4912,9 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
nscoord maxAscent = fontMetrics->MaxAscent();
gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
gfxFloat gfxWidth = aVisualOverflowRect->width / appUnitsPerDevUnit;
gfxFloat gfxWidth =
(vertical ? aVisualOverflowRect->height : aVisualOverflowRect->width) /
appUnitsPerDevUnit;
gfxFloat gfxAscent = gfxFloat(mAscent) / appUnitsPerDevUnit;
gfxFloat gfxMaxAscent = maxAscent / appUnitsPerDevUnit;
gfxFloat gfxUnderlineSize = underlineSize / appUnitsPerDevUnit;
@ -4917,11 +4922,11 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
nsRect underlineRect =
nsCSSRendering::GetTextDecorationRect(aPresContext,
gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxUnderlineOffset,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle);
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle, vertical);
nsRect overlineRect =
nsCSSRendering::GetTextDecorationRect(aPresContext,
gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxMaxAscent,
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle);
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle, vertical);
aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect);
aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect);
@ -4941,9 +4946,9 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
nscoord inflationMinFontSize =
nsLayoutUtils::InflationMinFontSizeFor(aBlock);
const nscoord width = GetSize().width;
const nscoord measure = vertical ? GetSize().height : GetSize().width;
const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
gfxWidth = width / appUnitsPerDevUnit,
gfxWidth = measure / appUnitsPerDevUnit,
ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
nscoord top(nscoord_MAX), bottom(nscoord_MIN);
// Below we loop through all text decorations and compute the rectangle
@ -4961,13 +4966,15 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
float inflation =
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
const gfxFont::Metrics metrics =
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
vertical);
const nsRect decorationRect =
nsCSSRendering::GetTextDecorationRect(aPresContext,
gfxSize(gfxWidth, metrics.underlineSize),
ascent, metrics.underlineOffset,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle) +
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle,
vertical) +
nsPoint(0, -dec.mBaselineOffset);
top = std::min(decorationRect.y, top);
@ -4986,13 +4993,15 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
float inflation =
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
const gfxFont::Metrics metrics =
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
vertical);
const nsRect decorationRect =
nsCSSRendering::GetTextDecorationRect(aPresContext,
gfxSize(gfxWidth, metrics.underlineSize),
ascent, metrics.maxAscent,
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle) +
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle,
vertical) +
nsPoint(0, -dec.mBaselineOffset);
top = std::min(decorationRect.y, top);
@ -5011,20 +5020,24 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
float inflation =
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
const gfxFont::Metrics metrics =
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
vertical);
const nsRect decorationRect =
nsCSSRendering::GetTextDecorationRect(aPresContext,
gfxSize(gfxWidth, metrics.strikeoutSize),
ascent, metrics.strikeoutOffset,
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorationStyle) +
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorationStyle,
vertical) +
nsPoint(0, -dec.mBaselineOffset);
top = std::min(decorationRect.y, top);
bottom = std::max(decorationRect.YMost(), bottom);
}
aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
nsRect(0, top, width, bottom - top));
vertical ?
nsRect(top, 0, bottom - top, measure) :
nsRect(0, top, measure, bottom - top));
}
}
// When this frame is not selected, the text-decoration area must be in
@ -5104,7 +5117,7 @@ PaintDecorationLine(nsIFrame* aFrame,
nscolor aColor,
const nscolor* aOverrideColor,
const gfxPoint& aPt,
gfxFloat aXInFrame,
gfxFloat aICoordInFrame,
const gfxSize& aLineSize,
gfxFloat aAscent,
gfxFloat aOffset,
@ -5112,6 +5125,7 @@ PaintDecorationLine(nsIFrame* aFrame,
uint8_t aStyle,
DecorationType aDecorationType,
nsTextFrame::DrawPathCallbacks* aCallbacks,
bool aVertical,
gfxFloat aDescentLimit = -1.0)
{
nscolor lineColor = aOverrideColor ? *aOverrideColor : aColor;
@ -5122,7 +5136,7 @@ PaintDecorationLine(nsIFrame* aFrame,
aCallbacks->NotifyBeforeSelectionDecorationLine(lineColor);
}
nsCSSRendering::DecorationLineToPath(aFrame, aCtx, aDirtyRect, lineColor,
aPt, aXInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
aPt, aICoordInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
aDescentLimit);
if (aDecorationType == eNormalDecoration) {
aCallbacks->NotifyDecorationLinePathEmitted();
@ -5131,8 +5145,8 @@ PaintDecorationLine(nsIFrame* aFrame,
}
} else {
nsCSSRendering::PaintDecorationLine(aFrame, aCtx, aDirtyRect, lineColor,
aPt, aXInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
aDescentLimit);
aPt, aICoordInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
aVertical, aDescentLimit);
}
}
@ -5146,9 +5160,10 @@ static void DrawSelectionDecorations(gfxContext* aContext,
nsTextFrame* aFrame,
nsTextPaintStyle& aTextPaintStyle,
const TextRangeStyle &aRangeStyle,
const gfxPoint& aPt, gfxFloat aXInFrame, gfxFloat aWidth,
const gfxPoint& aPt, gfxFloat aICoordInFrame, gfxFloat aWidth,
gfxFloat aAscent, const gfxFont::Metrics& aFontMetrics,
nsTextFrame::DrawPathCallbacks* aCallbacks)
nsTextFrame::DrawPathCallbacks* aCallbacks,
bool aVertical)
{
gfxPoint pt(aPt);
gfxSize size(aWidth,
@ -5225,9 +5240,10 @@ static void DrawSelectionDecorations(gfxContext* aContext,
}
size.height *= relativeSize;
PaintDecorationLine(aFrame, aContext, aDirtyRect, color, nullptr, pt,
pt.x - aPt.x + aXInFrame, size, aAscent, aFontMetrics.underlineOffset,
(aVertical ? (pt.y - aPt.y) : (pt.x - aPt.x)) + aICoordInFrame,
size, aAscent, aFontMetrics.underlineOffset,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, style, eSelectionDecoration,
aCallbacks, descentLimit);
aCallbacks, aVertical, descentLimit);
}
/**
@ -5680,36 +5696,53 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
}
gfxFont* firstFont = aProvider.GetFontGroup()->GetFirstValidFont();
bool vertical = mTextRun->IsVertical();
gfxFont::Metrics
decorationMetrics(firstFont->GetMetrics(gfxFont::eHorizontal)); // XXX vertical?
decorationMetrics.underlineOffset =
aProvider.GetFontGroup()->GetUnderlineOffset();
decorationMetrics(firstFont->GetMetrics(vertical ?
gfxFont::eVertical : gfxFont::eHorizontal));
if (!vertical) {
// The potential adjustment from using gfxFontGroup::GetUnderlineOffset
// is only valid for horizontal text.
decorationMetrics.underlineOffset =
aProvider.GetFontGroup()->GetUnderlineOffset();
}
gfxFloat startXOffset = aTextBaselinePt.x - aFramePt.x;
gfxFloat startIOffset =
vertical ? aTextBaselinePt.y - aFramePt.y : aTextBaselinePt.x - aFramePt.x;
SelectionIterator iterator(selectedChars, aContentOffset, aContentLength,
aProvider, mTextRun, startXOffset);
gfxFloat xOffset, hyphenWidth;
aProvider, mTextRun, startIOffset);
gfxFloat iOffset, hyphenWidth;
uint32_t offset, length;
int32_t app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
// XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
gfxPoint pt(0.0, (aTextBaselinePt.y - mAscent) / app);
gfxPoint pt;
if (vertical) {
pt.x = (aTextBaselinePt.x - mAscent) / app;
} else {
pt.y = (aTextBaselinePt.y - mAscent) / app;
}
gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
aDirtyRect.width / app, aDirtyRect.height / app);
SelectionType type;
TextRangeStyle selectedStyle;
while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
while (iterator.GetNextSegment(&iOffset, &offset, &length, &hyphenWidth,
&type, &selectedStyle)) {
gfxFloat advance = hyphenWidth +
mTextRun->GetAdvanceWidth(offset, length, &aProvider);
if (type == aSelectionType) {
pt.x = (aFramePt.x + xOffset -
(mTextRun->IsRightToLeft() ? advance : 0)) / app;
if (vertical) {
pt.y = (aFramePt.y + iOffset -
(mTextRun->IsRightToLeft() ? advance : 0)) / app;
} else {
pt.x = (aFramePt.x + iOffset -
(mTextRun->IsRightToLeft() ? advance : 0)) / app;
}
gfxFloat width = Abs(advance) / app;
gfxFloat xInFrame = pt.x - (aFramePt.x / app);
DrawSelectionDecorations(aCtx, dirtyRect, aSelectionType, this,
aTextPaintStyle, selectedStyle, pt, xInFrame,
width, mAscent / app, decorationMetrics,
aCallbacks);
aCallbacks, vertical);
}
iterator.UpdateWithAdvance(advance);
}
@ -6096,10 +6129,11 @@ nsTextFrame::DrawTextRunAndDecorations(
nsTextFrame::DrawPathCallbacks* aCallbacks)
{
const gfxFloat app = aTextStyle.PresContext()->AppUnitsPerDevPixel();
bool vertical = mTextRun->IsVertical();
// XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
nscoord x = NSToCoordRound(aFramePt.x);
nscoord width = GetRect().width;
nscoord width = vertical ? GetRect().height : GetRect().width;
aClipEdges.Intersect(&x, &width);
gfxPoint decPt(x / app, 0);
@ -6123,7 +6157,8 @@ nsTextFrame::DrawTextRunAndDecorations(
float inflation =
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
const gfxFont::Metrics metrics =
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
vertical);
decSize.height = metrics.underlineSize;
decPt.y = (frameTop - dec.mBaselineOffset) / app;
@ -6131,7 +6166,7 @@ nsTextFrame::DrawTextRunAndDecorations(
PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
metrics.underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
dec.mStyle, eNormalDecoration, aCallbacks);
dec.mStyle, eNormalDecoration, aCallbacks, vertical);
}
// Overlines
for (uint32_t i = aDecorations.mOverlines.Length(); i-- > 0; ) {
@ -6143,7 +6178,8 @@ nsTextFrame::DrawTextRunAndDecorations(
float inflation =
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
const gfxFont::Metrics metrics =
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
vertical);
decSize.height = metrics.underlineSize;
decPt.y = (frameTop - dec.mBaselineOffset) / app;
@ -6151,7 +6187,7 @@ nsTextFrame::DrawTextRunAndDecorations(
PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
metrics.maxAscent, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle,
eNormalDecoration, aCallbacks);
eNormalDecoration, aCallbacks, vertical);
}
// CSS 2.1 mandates that text be painted after over/underlines, and *then*
@ -6169,7 +6205,8 @@ nsTextFrame::DrawTextRunAndDecorations(
float inflation =
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
const gfxFont::Metrics metrics =
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
vertical);
decSize.height = metrics.strikeoutSize;
decPt.y = (frameTop - dec.mBaselineOffset) / app;
@ -6177,7 +6214,7 @@ nsTextFrame::DrawTextRunAndDecorations(
PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
metrics.strikeoutOffset, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
dec.mStyle, eNormalDecoration, aCallbacks);
dec.mStyle, eNormalDecoration, aCallbacks, vertical);
}
}
@ -6373,8 +6410,9 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
GetFontSizeInflation());
gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
gfxFont* firstFont = fontGroup->GetFirstValidFont();
bool vertical = GetWritingMode().IsVertical();
const gfxFont::Metrics& metrics =
firstFont->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
firstFont->GetMetrics(vertical ? gfxFont::eVertical : gfxFont::eHorizontal);
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
gfxFloat descentLimit =
@ -6419,7 +6457,7 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
decorationArea =
nsCSSRendering::GetTextDecorationRect(aPresContext, size,
ascent, underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
style, descentLimit);
style, vertical, descentLimit);
aRect.UnionRect(aRect, decorationArea);
}
DestroySelectionDetails(details);

View File

@ -399,6 +399,8 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
// A mask of all possible decorations.
uint8_t decorMask = NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
bool vertical = GetWritingMode().IsVertical();
nsIFrame* f = this;
do { // find decoration colors
nsStyleContext* context = f->StyleContext();
@ -477,14 +479,16 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, underColor,
pt, xInFrame, gfxSize(width, sizePixel),
ascentPixel, offsetPixel,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle);
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle,
vertical);
}
if ((decorations & NS_FONT_DECORATION_OVERLINE) &&
overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, overColor,
pt, xInFrame, gfxSize(width, sizePixel),
ascentPixel, ascentPixel,
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle);
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle,
vertical);
}
}
@ -562,7 +566,7 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, strikeColor,
pt, xInFrame, gfxSize(width, sizePixel), ascentPixel,
offsetPixel, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
strikeStyle);
strikeStyle, vertical);
}
}