Index: third_party/WebKit/Source/core/paint/NGTextFragmentPainter.cpp |
diff --git a/third_party/WebKit/Source/core/paint/NGTextFragmentPainter.cpp b/third_party/WebKit/Source/core/paint/NGTextFragmentPainter.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4d9b8e3467caee7e00d067a81ff9357aa89046ce |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/paint/NGTextFragmentPainter.cpp |
@@ -0,0 +1,279 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "core/paint/NGTextFragmentPainter.h" |
+ |
+#include "core/frame/LocalFrame.h" |
+#include "core/layout/ng/inline/ng_physical_text_fragment.h" |
+#include "core/paint/NGTextPainter.h" |
+#include "core/paint/PaintInfo.h" |
+#include "core/paint/TextPainterBase.h" |
+#include "core/style/AppliedTextDecoration.h" |
+#include "platform/graphics/GraphicsContextStateSaver.h" |
+#include "platform/graphics/paint/DrawingRecorder.h" |
+#include "platform/wtf/Optional.h" |
+ |
+namespace blink { |
+ |
+namespace { |
+static void PrepareContextForDecoration( |
+ GraphicsContext& context, |
+ GraphicsContextStateSaver& state_saver, |
+ bool is_horizontal, |
+ const TextPainterBase::Style& text_style, |
+ const LayoutRect& box_rect) { |
+ TextPainterBase::UpdateGraphicsContext(context, text_style, is_horizontal, |
+ state_saver); |
+} |
+ |
+static void RestoreContextFromDecoration(GraphicsContext& context, |
+ const LayoutRect& box_rect) {} |
+ |
+static void PaintDecorationsExceptLineThrough( |
+ NGTextPainter& text_painter, |
+ bool& has_line_through_decoration, |
+ const NGPhysicalTextFragment* fragment, |
+ 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); |
+ |
+ // text-underline-position may flip underline and overline. |
+ ResolvedUnderlinePosition underline_position = |
+ decoration_info.underline_position; |
+ 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 = 0; |
+ // text_painter.PaintDecorationUnderOrOverLine(context, decoration_info, |
+ // decoration, |
+ // underline_offset); |
+ // } |
+ // if (lines & kTextDecorationOverline) { |
+ // const int overline_offset = 0; |
+ // text_painter.PaintDecorationUnderOrOverLine(context, decoration_info, |
+ // decoration, |
+ // overline_offset); |
+ // } |
+ // // 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); |
+ //} |
+} |
+ |
+} // anonymous namespace |
+ |
+void NGTextFragmentPainter::Paint(const Document& document, |
+ const PaintInfo& paint_info, |
+ const LayoutPoint& paint_offset) { |
+ // TODO(eae): Move |
+ static unsigned short kCNoTruncation = USHRT_MAX; |
+ static unsigned short kCFullTruncation = USHRT_MAX - 1; |
+ |
+ const ComputedStyle& style_to_use = text_fragment_->Style(); |
+ |
+ NGPhysicalSize size_; |
+ NGPhysicalOffset offset_; |
+ |
+ // We round the y-axis to ensure consistent line heights. |
+ LayoutPoint adjusted_paint_offset = |
+ LayoutPoint(paint_offset.X(), LayoutUnit(paint_offset.Y().Round())); |
+ |
+ NGPhysicalOffset offset = text_fragment_->Offset(); |
+ LayoutPoint box_origin(offset.left, offset.top); |
+ box_origin.Move(adjusted_paint_offset.X(), adjusted_paint_offset.Y()); |
+ |
+ LayoutRect box_rect(box_origin, LayoutSize(text_fragment_->Size().width, |
+ text_fragment_->Size().height)); |
+ |
+ GraphicsContext& context = paint_info.context; |
+ |
+ bool is_printing = paint_info.IsPrinting(); |
+ |
+ // Determine whether or not we're selected. |
+ // bool have_selection = !is_printing && |
+ // paint_info.phase != kPaintPhaseTextClip && |
+ // inline_text_box_.GetSelectionState() != SelectionNone; |
+ bool have_selection = false; |
+ if (!have_selection && paint_info.phase == kPaintPhaseSelection) { |
+ // When only painting the selection, don't bother to paint if there is none. |
+ return; |
+ } |
+ |
+ // Determine text colors. |
+ TextPainterBase::Style text_style = |
+ TextPainterBase::TextPaintingStyle(document, style_to_use, paint_info); |
+ // TextPainterBase::Style selection_style = |
+ // TextPainter::SelectionPaintingStyle( |
+ // text_fragment_.GetLineLayoutItem(), have_selection, paint_info, |
+ // text_style); |
+ TextPainterBase::Style selection_style = text_style; |
+ bool paint_selected_text_only = (paint_info.phase == kPaintPhaseSelection); |
+ bool paint_selected_text_separately = |
+ !paint_selected_text_only && text_style != selection_style; |
+ |
+ // Set our font. |
+ const Font& font = style_to_use.GetFont(); |
+ const SimpleFontData* font_data = font.PrimaryFont(); |
+ DCHECK(font_data); |
+ |
+ int ascent = font_data ? font_data->GetFontMetrics().Ascent() : 0; |
+ LayoutPoint text_origin(box_origin.X(), box_origin.Y() + ascent); |
+ |
+ // 1. Paint backgrounds behind text if needed. Examples of such backgrounds |
+ // include selection and composition highlights. |
+ if (paint_info.phase != kPaintPhaseSelection && |
+ paint_info.phase != kPaintPhaseTextClip && !is_printing) { |
+ // PaintDocumentMarkers(paint_info, box_origin, style_to_use, font, |
+ // DocumentMarkerPaintPhase::kBackground); |
+ |
+ // const LayoutObject& text_box_layout_object = InlineLayoutObject(); |
+ // if (have_selection && !PaintsCompositionMarkers(text_box_layout_object)) |
+ // { |
+ // if (combined_text) |
+ // PaintSelection<InlineTextBoxPainter::PaintOptions::kCombinedText>( |
+ // context, box_rect, style_to_use, font, selection_style.fill_color, |
+ // combined_text); |
+ // else |
+ // PaintSelection<InlineTextBoxPainter::PaintOptions::kNormal>( |
+ // context, box_rect, style_to_use, font, |
+ // selection_style.fill_color); |
+ //} |
+ } |
+ |
+ // 2. Now paint the foreground, including text and decorations. |
+ int selection_start = 0; |
+ int selection_end = 0; |
+ // if (paint_selected_text_only || paint_selected_text_separately) |
+ // text_fragment_.SelectionStartEnd(selection_start, selection_end); |
+ |
+ // bool respect_hyphen = |
+ // selection_end == static_cast<int>(text_fragment_.Len()) && |
+ // text_fragment_.HasHyphen(); |
+ // if (respect_hyphen) |
+ // selection_end = text_run.length(); |
+ |
+ unsigned length = text_fragment_->Text().length(); |
+ |
+ bool ltr = true; |
+ bool flow_is_ltr = true; |
+ if (text_fragment_->Truncation() != kCNoTruncation) { |
+ // In a mixed-direction flow the ellipsis is at the start of the text |
+ // rather than at the end of it. |
+ selection_start = |
+ ltr == flow_is_ltr |
+ ? std::min<int>(selection_start, text_fragment_->Truncation()) |
+ : std::max<int>(selection_start, text_fragment_->Truncation()); |
+ selection_end = |
+ ltr == flow_is_ltr |
+ ? std::min<int>(selection_end, text_fragment_->Truncation()) |
+ : std::max<int>(selection_end, text_fragment_->Truncation()); |
+ length = ltr == flow_is_ltr ? text_fragment_->Truncation() : length; |
+ } |
+ |
+ NGTextPainter text_painter(context, font, text_fragment_, text_origin, |
+ box_rect, text_fragment_->IsHorizontal()); |
+ TextEmphasisPosition emphasis_mark_position; |
+ // bool has_text_emphasis = text_fragment_->GetEmphasisMarkPosition( |
+ // style_to_use, emphasis_mark_position); |
+ emphasis_mark_position = kTextEmphasisPositionOver; |
+ bool has_text_emphasis = false; |
+ if (has_text_emphasis) { |
+ text_painter.SetEmphasisMark(style_to_use.TextEmphasisMarkString(), |
+ emphasis_mark_position); |
+ } |
+ // if (combined_text) |
+ // text_painter.SetCombinedText(combined_text); |
+ if (text_fragment_->Truncation() != kCNoTruncation && ltr != flow_is_ltr) |
+ text_painter.SetEllipsisOffset(text_fragment_->Truncation()); |
+ |
+ bool should_rotate = false; |
+ |
+ 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() != TextDecoration::kNone && |
+ text_fragment_->Truncation() != kCFullTruncation) { |
+ LayoutPoint local_origin = LayoutPoint(box_origin); |
+ LayoutUnit width = text_fragment_->Size().width; |
+ // const NGPhysicalTextFragment* decorating_box = |
+ // EnclosingUnderlineObject(&text_fragment_); |
+ const ComputedStyle* decorating_box_style = nullptr; |
+ // FontBaseline baseline_type = text_fragment_->Root().BaselineType(); |
+ FontBaseline baseline_type = kAlphabeticBaseline; |
+ |
+ text_painter.ComputeDecorationInfo(decoration_info, box_origin, |
+ local_origin, width, baseline_type, |
+ style_to_use, decorating_box_style); |
+ |
+ GraphicsContextStateSaver state_saver(context, false); |
+ PrepareContextForDecoration(context, state_saver, |
+ text_fragment_->IsHorizontal(), text_style, |
+ box_rect); |
+ PaintDecorationsExceptLineThrough( |
+ text_painter, has_line_through_decoration, text_fragment_, |
+ decoration_info, paint_info, style_to_use.AppliedTextDecorations()); |
+ RestoreContextFromDecoration(context, box_rect); |
+ } |
+ |
+ int start_offset = 0; |
+ int end_offset = length; |
+ // Where the text and its flow have opposite directions then our offset into |
+ // the text given by |truncation| is at the start of the part that will be |
+ // visible. |
+ if (text_fragment_->Truncation() != kCNoTruncation && ltr != flow_is_ltr) { |
+ start_offset = text_fragment_->Truncation(); |
+ end_offset = length; |
+ } |
+ |
+ if (paint_selected_text_separately && selection_start < selection_end) { |
+ start_offset = selection_end; |
+ end_offset = selection_start; |
+ } |
+ |
+ 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, |
+ text_fragment_->IsHorizontal(), text_style, |
+ box_rect); |
+ text_painter.PaintDecorationsOnlyLineThrough( |
+ decoration_info, paint_info, style_to_use.AppliedTextDecorations()); |
+ RestoreContextFromDecoration(context, box_rect); |
+ } |
+ } |
+ |
+ if ((paint_selected_text_only || paint_selected_text_separately) && |
+ selection_start < selection_end) { |
+ // paint only the text that is selected |
+ text_painter.Paint(selection_start, selection_end, length, selection_style); |
+ } |
+ |
+ // if (paint_info.phase == kPaintPhaseForeground) |
+ // PaintDocumentMarkers(paint_info, box_origin, style_to_use, font, |
+ // DocumentMarkerPaintPhase::kForeground); |
+ |
+ if (should_rotate) { |
+ context.ConcatCTM(TextPainterBase::Rotation( |
+ box_rect, TextPainterBase::kCounterclockwise)); |
+ } |
+} |
+ |
+} // namespace blink |