Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1083)

Unified Diff: third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp

Issue 2820743003: Paint text underline/overline decorations before painting text. (Closed)
Patch Set: Sync to head. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/Source/core/paint/InlineTextBoxPainter.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
index 184f9aed920ff27e9f87cfc922135cc25a73eec8..80a7b90c9b77fd15493499141bc206560a440f0a 100644
--- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
@@ -216,26 +216,39 @@ static void AdjustStepToDecorationLength(float& step,
control_point_distance += adjustment;
}
+// Holds text decoration painting values to be computed once and subsequently
+// use multiple times to handle decoration paint order correctly. See also
+// https://www.w3.org/TR/css-text-decor-3/#painting-order
+struct DecorationInfo final {
+ STACK_ALLOCATED();
+
+ LayoutUnit width;
+ FloatPoint local_origin;
+ bool antialias;
+ float baseline;
+ const ComputedStyle* style;
+ const SimpleFontData* font_data;
+ float thickness;
+ float double_offset;
+};
+
class AppliedDecorationPainter final {
STACK_ALLOCATED();
public:
AppliedDecorationPainter(GraphicsContext& context,
- FloatPoint start_point,
- float width,
+ const DecorationInfo& decoration_info,
+ float start_point_y_offset,
const AppliedTextDecoration& decoration,
- float thickness,
float double_offset,
- int wavy_offset_factor,
- bool antialias_decoration)
+ int wavy_offset_factor)
: context_(context),
- start_point_(start_point),
- width_(width),
+ start_point_(decoration_info.local_origin +
+ FloatPoint(0, start_point_y_offset)),
+ decoration_info_(decoration_info),
decoration_(decoration),
- thickness_(thickness),
double_offset_(double_offset),
- wavy_offset_factor_(wavy_offset_factor),
- should_antialias_(antialias_decoration){};
+ wavy_offset_factor_(wavy_offset_factor){};
void Paint();
FloatRect DecorationBounds();
@@ -247,24 +260,24 @@ class AppliedDecorationPainter final {
Path PrepareDottedDashedStrokePath();
GraphicsContext& context_;
- FloatPoint start_point_;
- float width_;
+ const FloatPoint start_point_;
+ const DecorationInfo& decoration_info_;
const AppliedTextDecoration& decoration_;
- float thickness_;
const float double_offset_;
const int wavy_offset_factor_;
- bool should_antialias_;
};
Path AppliedDecorationPainter::PrepareDottedDashedStrokePath() {
// These coordinate transforms need to match what's happening in
// GraphicsContext's drawLineForText and drawLine.
- int y = floorf(start_point_.Y() + std::max<float>(thickness_ / 2.0f, 0.5f));
+ int y = floorf(start_point_.Y() +
+ std::max<float>(decoration_info_.thickness / 2.0f, 0.5f));
Path stroke_path;
FloatPoint rounded_start_point(start_point_.X(), y);
- FloatPoint rounded_end_point(rounded_start_point + FloatPoint(width_, 0));
+ FloatPoint rounded_end_point(rounded_start_point +
+ FloatPoint(decoration_info_.width, 0));
context_.AdjustLineToPixelBoundaries(rounded_start_point, rounded_end_point,
- roundf(thickness_),
+ roundf(decoration_info_.thickness),
context_.GetStrokeStyle());
stroke_path.MoveTo(rounded_start_point);
stroke_path.AddLineTo(rounded_end_point);
@@ -273,7 +286,7 @@ Path AppliedDecorationPainter::PrepareDottedDashedStrokePath() {
FloatRect AppliedDecorationPainter::DecorationBounds() {
StrokeData stroke_data;
- stroke_data.SetThickness(thickness_);
+ stroke_data.SetThickness(decoration_info_.thickness);
switch (decoration_.Style()) {
case kTextDecorationStyleDotted:
@@ -289,14 +302,17 @@ FloatRect AppliedDecorationPainter::DecorationBounds() {
break;
case kTextDecorationStyleDouble:
if (double_offset_ > 0) {
- return FloatRect(start_point_.X(), start_point_.Y(), width_,
- double_offset_ + thickness_);
+ return FloatRect(start_point_.X(), start_point_.Y(),
+ decoration_info_.width,
+ double_offset_ + decoration_info_.thickness);
}
return FloatRect(start_point_.X(), start_point_.Y() + double_offset_,
- width_, -double_offset_ + thickness_);
+ decoration_info_.width,
+ -double_offset_ + decoration_info_.thickness);
break;
case kTextDecorationStyleSolid:
- return FloatRect(start_point_.X(), start_point_.Y(), width_, thickness_);
+ return FloatRect(start_point_.X(), start_point_.Y(),
+ decoration_info_.width, decoration_info_.thickness);
default:
break;
}
@@ -315,14 +331,14 @@ void AppliedDecorationPainter::Paint() {
break;
case kTextDecorationStyleDotted:
case kTextDecorationStyleDashed:
- context_.SetShouldAntialias(should_antialias_);
+ context_.SetShouldAntialias(decoration_info_.antialias);
// Fall through
default:
- context_.DrawLineForText(start_point_, width_);
+ context_.DrawLineForText(start_point_, decoration_info_.width);
if (decoration_.Style() == kTextDecorationStyleDouble) {
context_.DrawLineForText(start_point_ + FloatPoint(0, double_offset_),
- width_);
+ decoration_info_.width);
}
}
}
@@ -362,10 +378,11 @@ void AppliedDecorationPainter::StrokeWavyTextDecoration() {
Path AppliedDecorationPainter::PrepareWavyStrokePath() {
FloatPoint p1(start_point_ +
FloatPoint(0, double_offset_ * wavy_offset_factor_));
- FloatPoint p2(start_point_ +
- FloatPoint(width_, double_offset_ * wavy_offset_factor_));
+ FloatPoint p2(
+ start_point_ +
+ FloatPoint(decoration_info_.width, double_offset_ * wavy_offset_factor_));
- context_.AdjustLineToPixelBoundaries(p1, p2, thickness_,
+ context_.AdjustLineToPixelBoundaries(p1, p2, decoration_info_.thickness,
context_.GetStrokeStyle());
Path path;
@@ -379,12 +396,13 @@ Path AppliedDecorationPainter::PrepareWavyStrokePath() {
// The minimum height of the curve is also approximately 3 pixels. Increases
// the curve's height
// as strockThickness increases to make the curve looks better.
- float control_point_distance = 3 * std::max<float>(2, thickness_);
+ float control_point_distance =
+ 3 * std::max<float>(2, decoration_info_.thickness);
// Increment used to form the diamond shape between start point (p1), control
// points and end point (p2) along the axis of the decoration. Makes the
// curve wider as strockThickness increases to make the curve looks better.
- float step = 2 * std::max<float>(2, thickness_);
+ float step = 2 * std::max<float>(2, decoration_info_.thickness);
bool is_vertical_line = (p1.X() == p2.X());
@@ -467,6 +485,177 @@ static bool PaintsCompositionMarkers(const LayoutObject& layout_object) {
.size() > 0;
}
+static void PrepareContextForDecoration(GraphicsContext& context,
+ GraphicsContextStateSaver& state_saver,
+ bool is_horizontal,
+ const TextPainter::Style& text_style,
+ const LayoutTextCombine* combined_text,
+ const LayoutRect& box_rect) {
+ TextPainter::UpdateGraphicsContext(context, text_style, is_horizontal,
+ state_saver);
+ if (combined_text)
+ context.ConcatCTM(TextPainter::Rotation(box_rect, TextPainter::kClockwise));
+}
+
+static void RestoreContextFromDecoration(GraphicsContext& context,
+ const LayoutTextCombine* combined_text,
+ const LayoutRect& box_rect) {
+ if (combined_text) {
+ context.ConcatCTM(
+ TextPainter::Rotation(box_rect, TextPainter::kCounterclockwise));
+ }
+}
+
+static void ComputeDecorationInfo(
+ DecorationInfo& decoration_info,
+ const InlineTextBox& box,
+ const LayoutPoint& box_origin,
+ const Vector<AppliedTextDecoration>& decorations) {
+ LayoutPoint local_origin = LayoutPoint(box_origin);
+ LayoutUnit width = box.LogicalWidth();
+ if (box.Truncation() != kCNoTruncation) {
+ bool ltr = box.IsLeftToRightDirection();
+ bool flow_is_ltr =
+ box.GetLineLayoutItem().Style()->IsLeftToRightDirection();
+ width = LayoutUnit(box.GetLineLayoutItem().Width(
+ ltr == flow_is_ltr ? box.Start() : box.Start() + box.Truncation(),
+ ltr == flow_is_ltr ? box.Truncation() : box.Len() - box.Truncation(),
+ box.TextPos(), flow_is_ltr ? TextDirection::kLtr : TextDirection::kRtl,
+ box.IsFirstLineStyle()));
+ if (!flow_is_ltr) {
+ local_origin.Move(box.LogicalWidth() - width, LayoutUnit());
+ }
+ }
+ decoration_info.width = width;
+ decoration_info.local_origin = FloatPoint(local_origin);
+
+ decoration_info.antialias = ShouldSetDecorationAntialias(decorations);
+
+ decoration_info.style =
+ LineLayoutAPIShim::LayoutObjectFrom(box.GetLineLayoutItem())
+ ->Style(box.IsFirstLineStyle());
+ decoration_info.font_data = decoration_info.style->GetFont().PrimaryFont();
+ DCHECK(decoration_info.font_data);
+ decoration_info.baseline =
+ decoration_info.font_data
+ ? decoration_info.font_data->GetFontMetrics().FloatAscent()
+ : 0;
+
+ // Set the thick of the line to be 10% (or something else ?)of the computed
+ // font size and not less than 1px. Using computedFontSize should take care
+ // of zoom as well.
+
+ // Update Underline thickness, in case we have Faulty Font Metrics calculating
+ // underline thickness by old method.
+ float text_decoration_thickness = 0.0;
+ int font_height_int = 0;
+ if (decoration_info.font_data) {
+ text_decoration_thickness =
+ decoration_info.font_data->GetFontMetrics().UnderlineThickness();
+ font_height_int =
+ (int)(decoration_info.font_data->GetFontMetrics().FloatHeight() + 0.5);
+ }
+ if ((text_decoration_thickness == 0.f) ||
+ (text_decoration_thickness >= (font_height_int >> 1))) {
+ text_decoration_thickness =
+ std::max(1.f, decoration_info.style->ComputedFontSize() / 10.f);
+ }
+ decoration_info.thickness = text_decoration_thickness;
+
+ // Offset between lines - always non-zero, so lines never cross each other.
+ decoration_info.double_offset = text_decoration_thickness + 1.f;
+}
+
+static void PaintDecorationsExceptLineThrough(
+ TextPainter& text_painter,
+ bool& has_line_through_decoration,
+ const InlineTextBox& box,
+ const DecorationInfo& decoration_info,
+ const PaintInfo& paint_info,
+ const Vector<AppliedTextDecoration>& decorations) {
+ GraphicsContext& context = paint_info.context;
+ GraphicsContextStateSaver state_saver(context);
+ context.SetStrokeThickness(decoration_info.thickness);
+ bool skip_intercepts =
+ decoration_info.style->GetTextDecorationSkip() & kTextDecorationSkipInk;
+
+ // text-underline-position may flip underline and overline.
+ ResolvedUnderlinePosition underline_position =
+ ResolveUnderlinePosition(*decoration_info.style, &box);
+ bool flip_underline_and_overline = false;
+ if (underline_position == ResolvedUnderlinePosition::kOver) {
+ flip_underline_and_overline = true;
+ underline_position = ResolvedUnderlinePosition::kUnder;
+ }
+
+ for (const AppliedTextDecoration& decoration : decorations) {
+ TextDecoration lines = decoration.Lines();
+ if (flip_underline_and_overline) {
+ lines = static_cast<TextDecoration>(
+ lines ^ (kTextDecorationUnderline | kTextDecorationOverline));
+ }
+ if ((lines & kTextDecorationUnderline) && decoration_info.font_data) {
+ const int underline_offset =
+ ComputeUnderlineOffset(underline_position, *decoration_info.style,
+ decoration_info.font_data->GetFontMetrics(),
+ &box, decoration_info.thickness);
+ AppliedDecorationPainter decoration_painter(
+ context, decoration_info, underline_offset, decoration,
+ decoration_info.double_offset, 1);
+ if (skip_intercepts) {
+ text_painter.ClipDecorationsStripe(
+ -decoration_info.baseline +
+ decoration_painter.DecorationBounds().Y() -
+ decoration_info.local_origin.Y(),
+ decoration_painter.DecorationBounds().Height(),
+ decoration_info.thickness);
+ }
+ decoration_painter.Paint();
+ }
+ if (lines & kTextDecorationOverline) {
+ const int overline_offset =
+ ComputeOverlineOffset(*decoration_info.style, &box);
+ AppliedDecorationPainter decoration_painter(
+ context, decoration_info, overline_offset, decoration,
+ -decoration_info.double_offset, 1);
+ if (skip_intercepts) {
+ text_painter.ClipDecorationsStripe(
+ -decoration_info.baseline +
+ decoration_painter.DecorationBounds().Y() -
+ decoration_info.local_origin.Y(),
+ decoration_painter.DecorationBounds().Height(),
+ decoration_info.thickness);
+ }
+ decoration_painter.Paint();
+ }
+ // We could instead build a vector of the TextDecoration instances needing
+ // line-through but this is a rare case so better to avoid vector overhead.
+ has_line_through_decoration |= ((lines & kTextDecorationLineThrough) != 0);
+ }
+}
+
+static void PaintDecorationsOnlyLineThrough(
+ TextPainter& text_painter,
+ const DecorationInfo& decoration_info,
+ const PaintInfo& paint_info,
+ const Vector<AppliedTextDecoration>& decorations) {
+ GraphicsContext& context = paint_info.context;
+ GraphicsContextStateSaver state_saver(context);
+ context.SetStrokeThickness(decoration_info.thickness);
+ for (const AppliedTextDecoration& decoration : decorations) {
+ TextDecoration lines = decoration.Lines();
+ if (lines & kTextDecorationLineThrough) {
+ const float line_through_offset = 2 * decoration_info.baseline / 3;
+ AppliedDecorationPainter decoration_painter(
+ context, decoration_info, line_through_offset, decoration,
+ decoration_info.double_offset, 0);
+ // No skip: ink for line-through,
+ // compare https://github.com/w3c/csswg-drafts/issues/711
+ decoration_painter.Paint();
+ }
+ }
+}
+
void InlineTextBoxPainter::Paint(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
if (!ShouldPaintTextBox(paint_info))
@@ -668,6 +857,23 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info,
text_painter.SetEllipsisOffset(inline_text_box_.Truncation());
if (!paint_selected_text_only) {
+ // Paint text decorations except line-through.
+ DecorationInfo decoration_info;
+ bool has_line_through_decoration = false;
+ if (style_to_use.TextDecorationsInEffect() != kTextDecorationNone &&
+ inline_text_box_.Truncation() != kCFullTruncation) {
+ ComputeDecorationInfo(decoration_info, inline_text_box_, box_origin,
+ style_to_use.AppliedTextDecorations());
+ GraphicsContextStateSaver state_saver(context, false);
+ PrepareContextForDecoration(context, state_saver,
+ inline_text_box_.IsHorizontal(), text_style,
+ combined_text, box_rect);
+ PaintDecorationsExceptLineThrough(
+ text_painter, has_line_through_decoration, inline_text_box_,
+ decoration_info, paint_info, style_to_use.AppliedTextDecorations());
+ RestoreContextFromDecoration(context, combined_text, box_rect);
+ }
+
int start_offset = 0;
int end_offset = length;
// Where the text and its flow have opposite directions then our offset into
@@ -684,6 +890,17 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info,
}
text_painter.Paint(start_offset, end_offset, length, text_style);
+
+ // Paint line-through decoration if needed.
+ if (has_line_through_decoration) {
+ GraphicsContextStateSaver state_saver(context, false);
+ PrepareContextForDecoration(context, state_saver,
+ inline_text_box_.IsHorizontal(), text_style,
+ combined_text, box_rect);
+ PaintDecorationsOnlyLineThrough(text_painter, decoration_info, paint_info,
+ style_to_use.AppliedTextDecorations());
+ RestoreContextFromDecoration(context, combined_text, box_rect);
+ }
}
if ((paint_selected_text_only || paint_selected_text_separately) &&
@@ -692,24 +909,6 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info,
text_painter.Paint(selection_start, selection_end, length, selection_style);
}
- // Paint decorations
- if (style_to_use.TextDecorationsInEffect() != kTextDecorationNone &&
- !paint_selected_text_only) {
- GraphicsContextStateSaver state_saver(context, false);
-
- TextPainter::UpdateGraphicsContext(
- context, text_style, inline_text_box_.IsHorizontal(), state_saver);
-
- if (combined_text)
- context.ConcatCTM(
- TextPainter::Rotation(box_rect, TextPainter::kClockwise));
- PaintDecorations(text_painter, paint_info, box_origin,
- style_to_use.AppliedTextDecorations());
- if (combined_text)
- context.ConcatCTM(
- TextPainter::Rotation(box_rect, TextPainter::kCounterclockwise));
- }
-
if (paint_info.phase == kPaintPhaseForeground)
PaintDocumentMarkers(paint_info, box_origin, style_to_use, font,
DocumentMarkerPaintPhase::kForeground);
@@ -1106,135 +1305,6 @@ void InlineTextBoxPainter::ExpandToIncludeNewlineForSelection(
rect.Expand(outsets);
}
-void InlineTextBoxPainter::PaintDecorations(
- TextPainter& text_painter,
- const PaintInfo& paint_info,
- const LayoutPoint& box_origin,
- const Vector<AppliedTextDecoration>& decorations) {
- if (inline_text_box_.Truncation() == kCFullTruncation)
- return;
-
- GraphicsContext& context = paint_info.context;
- GraphicsContextStateSaver state_saver(context);
-
- LayoutPoint local_origin(box_origin);
-
- LayoutUnit width = inline_text_box_.LogicalWidth();
- if (inline_text_box_.Truncation() != kCNoTruncation) {
- bool ltr = inline_text_box_.IsLeftToRightDirection();
- bool flow_is_ltr =
- inline_text_box_.GetLineLayoutItem().Style()->IsLeftToRightDirection();
- width = LayoutUnit(inline_text_box_.GetLineLayoutItem().Width(
- ltr == flow_is_ltr
- ? inline_text_box_.Start()
- : inline_text_box_.Start() + inline_text_box_.Truncation(),
- ltr == flow_is_ltr
- ? inline_text_box_.Truncation()
- : inline_text_box_.Len() - inline_text_box_.Truncation(),
- inline_text_box_.TextPos(),
- flow_is_ltr ? TextDirection::kLtr : TextDirection::kRtl,
- inline_text_box_.IsFirstLineStyle()));
- if (!flow_is_ltr)
- local_origin.Move(inline_text_box_.LogicalWidth() - width, LayoutUnit());
- }
-
- LayoutObject& text_box_layout_object = InlineLayoutObject();
-
- const ComputedStyle& style_to_use =
- text_box_layout_object.StyleRef(inline_text_box_.IsFirstLineStyle());
- const SimpleFontData* font_data = style_to_use.GetFont().PrimaryFont();
- DCHECK(font_data);
- float baseline = font_data ? font_data->GetFontMetrics().FloatAscent() : 0;
-
- // Set the thick of the line to be 10% (or something else ?)of the computed
- // font size and not less than 1px. Using computedFontSize should take care
- // of zoom as well.
-
- // Update Underline thickness, in case we have Faulty Font Metrics calculating
- // underline thickness by old method.
- float text_decoration_thickness = 0.0;
- int font_height_int = 0;
- if (font_data) {
- text_decoration_thickness =
- font_data->GetFontMetrics().UnderlineThickness();
- font_height_int = (int)(font_data->GetFontMetrics().FloatHeight() + 0.5);
- }
- if ((text_decoration_thickness == 0.f) ||
- (text_decoration_thickness >= (font_height_int >> 1)))
- text_decoration_thickness =
- std::max(1.f, style_to_use.ComputedFontSize() / 10.f);
-
- context.SetStrokeThickness(text_decoration_thickness);
-
- bool antialias_decoration = ShouldSetDecorationAntialias(decorations);
-
- // Offset between lines - always non-zero, so lines never cross each other.
- float double_offset = text_decoration_thickness + 1.f;
- bool skip_intercepts =
- style_to_use.GetTextDecorationSkip() & kTextDecorationSkipInk;
-
- // text-underline-position may flip underline and overline.
- ResolvedUnderlinePosition underline_position =
- ResolveUnderlinePosition(style_to_use, &inline_text_box_);
- bool flip_underline_and_overline = false;
- if (underline_position == ResolvedUnderlinePosition::kOver) {
- flip_underline_and_overline = true;
- underline_position = ResolvedUnderlinePosition::kUnder;
- }
-
- for (const AppliedTextDecoration& decoration : decorations) {
- TextDecoration lines = decoration.Lines();
- if (flip_underline_and_overline) {
- lines = static_cast<TextDecoration>(
- lines ^ (kTextDecorationUnderline | kTextDecorationOverline));
- }
- if ((lines & kTextDecorationUnderline) && font_data) {
- const int underline_offset = ComputeUnderlineOffset(
- underline_position, style_to_use, font_data->GetFontMetrics(),
- &inline_text_box_, text_decoration_thickness);
- AppliedDecorationPainter decoration_painter(
- context, FloatPoint(local_origin) + FloatPoint(0, underline_offset),
- width.ToFloat(), decoration, text_decoration_thickness, double_offset,
- 1, antialias_decoration);
- if (skip_intercepts) {
- text_painter.ClipDecorationsStripe(
- -baseline + decoration_painter.DecorationBounds().Y() -
- FloatPoint(local_origin).Y(),
- decoration_painter.DecorationBounds().Height(),
- text_decoration_thickness);
- }
- decoration_painter.Paint();
- }
- if (lines & kTextDecorationOverline) {
- const int overline_offset =
- ComputeOverlineOffset(style_to_use, &inline_text_box_);
- AppliedDecorationPainter decoration_painter(
- context, FloatPoint(local_origin) + FloatPoint(0, overline_offset),
- width.ToFloat(), decoration, text_decoration_thickness,
- -double_offset, 1, antialias_decoration);
- if (skip_intercepts) {
- text_painter.ClipDecorationsStripe(
- -baseline + decoration_painter.DecorationBounds().Y() -
- FloatPoint(local_origin).Y(),
- decoration_painter.DecorationBounds().Height(),
- text_decoration_thickness);
- }
- decoration_painter.Paint();
- }
- if (lines & kTextDecorationLineThrough) {
- const float line_through_offset = 2 * baseline / 3;
- AppliedDecorationPainter decoration_painter(
- context,
- FloatPoint(local_origin) + FloatPoint(0, line_through_offset),
- width.ToFloat(), decoration, text_decoration_thickness, double_offset,
- 0, antialias_decoration);
- // No skip: ink for line-through,
- // compare https://github.com/w3c/csswg-drafts/issues/711
- decoration_painter.Paint();
- }
- }
-}
-
void InlineTextBoxPainter::PaintCompositionUnderline(
GraphicsContext& context,
const LayoutPoint& box_origin,
« no previous file with comments | « third_party/WebKit/Source/core/paint/InlineTextBoxPainter.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698