| 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
|
|
|