Bug 486708 Should lift up selection underline as far as possible if it overflows from the descent space r+sr=roc

This commit is contained in:
Masayuki Nakano 2009-04-06 14:53:00 +09:00
parent 52d3713040
commit 45600f33a1
5 changed files with 120 additions and 20 deletions

View File

@ -2507,13 +2507,14 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
const gfxFloat aAscent,
const gfxFloat aOffset,
const PRUint8 aDecoration,
const PRUint8 aStyle)
const PRUint8 aStyle,
const gfxFloat aDescentLimit)
{
NS_ASSERTION(aStyle != DECORATION_STYLE_NONE, "aStyle is none");
gfxRect rect =
GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
aDecoration, aStyle);
aDecoration, aStyle, aDescentLimit);
if (rect.IsEmpty())
return;
@ -2706,14 +2707,15 @@ nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
const gfxFloat aAscent,
const gfxFloat aOffset,
const PRUint8 aDecoration,
const PRUint8 aStyle)
const PRUint8 aStyle,
const gfxFloat aDescentLimit)
{
NS_ASSERTION(aPresContext, "aPresContext is null");
NS_ASSERTION(aStyle != DECORATION_STYLE_NONE, "aStyle is none");
gfxRect rect =
GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
aDecoration, aStyle);
aDecoration, aStyle, aDescentLimit);
// The rect values are already rounded to nearest device pixels.
nsRect r;
r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
@ -2729,19 +2731,27 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
const gfxFloat aAscent,
const gfxFloat aOffset,
const PRUint8 aDecoration,
const PRUint8 aStyle)
const PRUint8 aStyle,
const gfxFloat aDescentLimit)
{
NS_ASSERTION(aStyle <= DECORATION_STYLE_WAVY, "Invalid aStyle value");
if (aStyle == DECORATION_STYLE_NONE)
return gfxRect(0, 0, 0, 0);
PRBool canLiftUnderline = aDescentLimit >= 0.0;
gfxRect r;
r.pos.x = NS_floor(aPt.x + 0.5);
r.size.width = NS_round(aLineSize.width);
gfxFloat lineHeight = NS_round(aLineSize.height);
lineHeight = PR_MAX(lineHeight, 1.0);
gfxFloat ascent = NS_round(aAscent);
gfxFloat descentLimit = NS_round(aDescentLimit);
gfxFloat suggestedMaxRectHeight = PR_MAX(PR_MIN(ascent, descentLimit), 1.0);
gfxFloat underlineOffsetAdjust = 0.0;
r.size.height = lineHeight;
if (aStyle == DECORATION_STYLE_DOUBLE) {
@ -2763,6 +2773,13 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
gfxFloat gap = NS_round(lineHeight / 2.0);
gap = PR_MAX(gap, 1.0);
r.size.height = lineHeight * 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.size.height = PR_MAX(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
}
}
} else if (aStyle == DECORATION_STYLE_WAVY) {
/**
* We will draw wavy line as:
@ -2778,6 +2795,21 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
* +-------------------------------------------+
*/
r.size.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
if (canLiftUnderline) {
// Wavy line's top edge can overlap to the baseline, because even if the
// wavy line overlaps the baseline of the text, that shouldn't cause
// unreadability.
descentLimit += lineHeight;
// Recompute suggestedMaxRectHeight with new descentLimit value.
suggestedMaxRectHeight = PR_MAX(PR_MIN(ascent, descentLimit), 1.0);
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.size.height = PR_MAX(suggestedMaxRectHeight, lineHeight * 2.0);
}
}
// If this is underline, the middle of the rect should be aligned to the
// specified underline offset. So, wavy line's top edge can overlap to
// baseline. Because even if the wavy line overlaps the baseline of the
@ -2790,6 +2822,17 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
switch (aDecoration) {
case NS_STYLE_TEXT_DECORATION_UNDERLINE:
offset = aOffset + underlineOffsetAdjust;
if (canLiftUnderline) {
if (descentLimit < -offset + r.Height()) {
// If we can ignore the offset and the decoration line is overflowing,
// we should align the bottom edge of the decoration line rect if it's
// possible. Otherwise, we should lift up the top edge of the rect as
// far as possible.
gfxFloat offsetBottomAligned = -descentLimit + r.Height();
gfxFloat offsetTopAligned = underlineOffsetAdjust;
offset = PR_MIN(offsetBottomAligned, offsetTopAligned);
}
}
break;
case NS_STYLE_TEXT_DECORATION_OVERLINE:
offset = aOffset - lineHeight + r.Height();

View File

@ -231,6 +231,17 @@ struct nsCSSRendering {
* NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
* @param aStyle the style of the decoration line (See above
* enum names).
* @param aDescentLimit If aDescentLimit is zero or larger and the
* underline overflows from the descent space,
* the underline should be lifted up as far as
* possible. Note that this does not mean the
* underline never overflows from this
* limitation. Because if the underline is
* positioned to the baseline or upper, it causes
* unreadability. Note that if this is zero
* or larger, the underline rect may be shrunken
* if it's possible. Therefore, this value is
* used for strikeout line and overline too.
*/
static void PaintDecorationLine(gfxContext* aGfxContext,
const nscolor aColor,
@ -239,7 +250,8 @@ struct nsCSSRendering {
const gfxFloat aAscent,
const gfxFloat aOffset,
const PRUint8 aDecoration,
const PRUint8 aStyle);
const PRUint8 aStyle,
const gfxFloat aDescentLimit = -1.0);
/**
* Function for getting the decoration line rect for the text.
@ -259,6 +271,17 @@ struct nsCSSRendering {
* NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
* @param aStyle the style of the decoration line (See above
* enum names).
* @param aDescentLimit If aDescentLimit is zero or larger and the
* underline overflows from the descent space,
* the underline should be lifted up as far as
* possible. Note that this does not mean the
* underline never overflows from this
* limitation. Because if the underline is
* positioned to the baseline or upper, it causes
* unreadability. Note that if this is zero
* or larger, the underline rect may be shrunken
* if it's possible. Therefore, this value is
* used for strikeout line and overline too.
* output:
* @return the decoration line rect for the input,
* the each values are app units.
@ -268,7 +291,8 @@ struct nsCSSRendering {
const gfxFloat aAscent,
const gfxFloat aOffset,
const PRUint8 aDecoration,
const PRUint8 aStyle);
const PRUint8 aStyle,
const gfxFloat aDescentLimit = -1.0);
protected:
static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt,
@ -276,7 +300,8 @@ protected:
const gfxFloat aAscent,
const gfxFloat aOffset,
const PRUint8 aDecoration,
const PRUint8 aStyle);
const PRUint8 aStyle,
const gfxFloat aDscentLimit);
};
/*

View File

@ -24,9 +24,11 @@ const kSVGNS = "http://www.w3.org/2000/svg";
// XXX following functions only support to draw underline now.
function drawDecorationLine(aDocument, aColor, aPt, aLineSize, aAscent, aOffset, aStyle)
function drawDecorationLine(aDocument, aColor, aPt, aLineSize, aAscent, aOffset,
aStyle, aDescentLimit)
{
var rect = getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle);
var rect = getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle,
aDescentLimit);
if (rect.width == 0 || rect.height == 0)
return;
@ -146,25 +148,45 @@ function drawDecorationLine(aDocument, aColor, aPt, aLineSize, aAscent, aOffset,
}
}
function getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle)
function getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle,
aDescentLimit)
{
if (aStyle == kDecorationStyleNone)
return { x: 0, y: 0, width: 0, height: 0 };
var liftupUnderline = aDescentLimit >= 0.0;
var r = {};
r.x = Math.floor(aPt.x + 0.5);
r.width = round(aLineSize.width);
var lineHeight = round(aLineSize.height);
lineHeight = Math.max(lineHeight, 1.0);
var ascent = round(aAscent);
var descentLimit = round(aDescentLimit);
var suggestedMaxRectHeight = Math.max(Math.min(ascent, descentLimit), 1.0);
var underlineOffsetAdjust = 0.0;
r.height = lineHeight;
if (aStyle == kDecorationStyleDouble) {
var gap = round(lineHeight / 2.0);
gap = Math.max(gap, 1.0);
r.height = lineHeight * 2.0 + gap;
if (liftupUnderline) {
if (r.height > suggestedMaxRectHeight) {
r.height = Math.max(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
}
}
} else if (aStyle == kDecorationStyleWavy) {
r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
if (liftupUnderline) {
descentLimit += lineHeight;
suggestedMaxRectHeight = Math.max(Math.min(ascent, descentLimit), 1.0);
if (r.height > suggestedMaxRectHeight) {
r.height = Math.max(suggestedMaxRectHeight, lineHeight * 2.0);
}
}
underlineOffsetAdjust = r.height / 2.0;
}
@ -172,6 +194,13 @@ function getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle)
var offset = 0.0;
offset = aOffset + underlineOffsetAdjust;
if (liftupUnderline) {
if (descentLimit < -offset + r.height) {
var offsetBottomAligned = -descentLimit + r.height;
var offsetTopAligned = underlineOffsetAdjust;
offset = Math.min(offsetBottomAligned, offsetTopAligned);
}
}
r.y = baseline - Math.floor(offset + 0.5);

View File

@ -4098,6 +4098,7 @@ static void DrawSelectionDecorations(gfxContext* aContext, SelectionType aType,
{
gfxPoint pt(aPt);
gfxSize size(aWidth, aFontMetrics.underlineSize);
gfxFloat descentLimit = aFontMetrics.maxDescent;
switch (aType) {
case nsISelectionController::SELECTION_IME_RAWINPUT:
@ -4131,7 +4132,7 @@ static void DrawSelectionDecorations(gfxContext* aContext, SelectionType aType,
size.height *= relativeSize;
nsCSSRendering::PaintDecorationLine(
aContext, color, pt, size, aAscent, aFontMetrics.underlineOffset,
NS_STYLE_TEXT_DECORATION_UNDERLINE, style);
NS_STYLE_TEXT_DECORATION_UNDERLINE, style, descentLimit);
}
break;
}
@ -4785,6 +4786,7 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
const gfxFont::Metrics& metrics = firstFont->GetMetrics();
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
gfxFloat descentLimit = metrics.maxDescent;
SelectionDetails *details = GetSelectionDetails();
for (SelectionDetails *sd = details; sd; sd = sd->mNext) {
@ -4808,7 +4810,7 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
nsCSSRendering::GetTextDecorationRect(aPresContext, size,
ascent, underlineOffset,
NS_STYLE_TEXT_DECORATION_UNDERLINE,
style);
style, descentLimit);
aRect.UnionRect(aRect, decorationArea);
}
DestroySelectionDetails(details);

View File

@ -65,12 +65,12 @@ const kIsLinux = navigator.platform.indexOf("Linux") == 0;
var gFontMetrics = [];
if (kIsWin) {
gFontMetrics = [
{ ascent: 13, offset: -2, lineHeight: 1 },
{ ascent: 17, offset: -2, lineHeight: 1 },
{ ascent: 26, offset: -4, lineHeight: 1 },
{ ascent: 34, offset: -4, lineHeight: 2 },
{ ascent: 42, offset: -7, lineHeight: 1 },
{ ascent: 56, offset: -7, lineHeight: 3 }
{ ascent: 13, offset: -2, lineHeight: 1, descentLimit: 3 },
{ ascent: 17, offset: -2, lineHeight: 1, descentLimit: 5 },
{ ascent: 26, offset: -4, lineHeight: 1, descentLimit: 6 },
{ ascent: 34, offset: -4, lineHeight: 2, descentLimit: 10 },
{ ascent: 42, offset: -7, lineHeight: 1, descentLimit: 10 },
{ ascent: 56, offset: -7, lineHeight: 3, descentLimit: 17 }
];
}
@ -132,7 +132,8 @@ function drawSelectionDecorationLines(aDocument, aStyle, aRelativeSize)
}
drawDecorationLine(aDocument, underlineStyle.color, pt,
{ width: width, height: fontMetrics.lineHeight * aRelativeSize },
fontMetrics.ascent, fontMetrics.offset, aStyle);
fontMetrics.ascent, fontMetrics.offset, aStyle,
fontMetrics.descentLimit);
}
}
}