OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/paint/NGTextFragmentPainter.h" |
| 6 |
| 7 #include "core/frame/LocalFrame.h" |
| 8 #include "core/layout/ng/inline/ng_physical_text_fragment.h" |
| 9 #include "core/paint/NGTextPainter.h" |
| 10 #include "core/paint/PaintInfo.h" |
| 11 #include "core/paint/TextPainterBase.h" |
| 12 #include "core/style/AppliedTextDecoration.h" |
| 13 #include "platform/graphics/GraphicsContextStateSaver.h" |
| 14 #include "platform/graphics/paint/DrawingRecorder.h" |
| 15 #include "platform/wtf/Optional.h" |
| 16 |
| 17 namespace blink { |
| 18 |
| 19 namespace { |
| 20 static void PrepareContextForDecoration( |
| 21 GraphicsContext& context, |
| 22 GraphicsContextStateSaver& state_saver, |
| 23 bool is_horizontal, |
| 24 const TextPainterBase::Style& text_style, |
| 25 const LayoutRect& box_rect) { |
| 26 TextPainterBase::UpdateGraphicsContext(context, text_style, is_horizontal, |
| 27 state_saver); |
| 28 } |
| 29 |
| 30 static void RestoreContextFromDecoration(GraphicsContext& context, |
| 31 const LayoutRect& box_rect) {} |
| 32 |
| 33 static void PaintDecorationsExceptLineThrough( |
| 34 NGTextPainter& text_painter, |
| 35 bool& has_line_through_decoration, |
| 36 const NGPhysicalTextFragment* fragment, |
| 37 const DecorationInfo& decoration_info, |
| 38 const PaintInfo& paint_info, |
| 39 const Vector<AppliedTextDecoration>& decorations) { |
| 40 GraphicsContext& context = paint_info.context; |
| 41 GraphicsContextStateSaver state_saver(context); |
| 42 context.SetStrokeThickness(decoration_info.thickness); |
| 43 |
| 44 // text-underline-position may flip underline and overline. |
| 45 ResolvedUnderlinePosition underline_position = |
| 46 decoration_info.underline_position; |
| 47 bool flip_underline_and_overline = false; |
| 48 if (underline_position == ResolvedUnderlinePosition::kOver) { |
| 49 flip_underline_and_overline = true; |
| 50 underline_position = ResolvedUnderlinePosition::kUnder; |
| 51 } |
| 52 |
| 53 // for (const AppliedTextDecoration& decoration : decorations) { |
| 54 // TextDecoration lines = decoration.Lines(); |
| 55 // if (flip_underline_and_overline) { |
| 56 // lines = static_cast<TextDecoration>( |
| 57 // lines ^ (kTextDecorationUnderline | kTextDecorationOverline)); |
| 58 // } |
| 59 // if ((lines & kTextDecorationUnderline) && decoration_info.font_data) { |
| 60 // const int underline_offset = 0; |
| 61 // text_painter.PaintDecorationUnderOrOverLine(context, decoration_info, |
| 62 // decoration, |
| 63 // underline_offset); |
| 64 // } |
| 65 // if (lines & kTextDecorationOverline) { |
| 66 // const int overline_offset = 0; |
| 67 // text_painter.PaintDecorationUnderOrOverLine(context, decoration_info, |
| 68 // decoration, |
| 69 // overline_offset); |
| 70 // } |
| 71 // // We could instead build a vector of the TextDecoration instances needing |
| 72 // // line-through but this is a rare case so better to avoid vector |
| 73 // overhead. has_line_through_decoration |= ((lines & |
| 74 // kTextDecorationLineThrough) != 0); |
| 75 //} |
| 76 } |
| 77 |
| 78 } // anonymous namespace |
| 79 |
| 80 void NGTextFragmentPainter::Paint(const Document& document, |
| 81 const PaintInfo& paint_info, |
| 82 const LayoutPoint& paint_offset) { |
| 83 // TODO(eae): Move |
| 84 static unsigned short kCNoTruncation = USHRT_MAX; |
| 85 static unsigned short kCFullTruncation = USHRT_MAX - 1; |
| 86 |
| 87 const ComputedStyle& style_to_use = text_fragment_->Style(); |
| 88 |
| 89 NGPhysicalSize size_; |
| 90 NGPhysicalOffset offset_; |
| 91 |
| 92 // We round the y-axis to ensure consistent line heights. |
| 93 LayoutPoint adjusted_paint_offset = |
| 94 LayoutPoint(paint_offset.X(), LayoutUnit(paint_offset.Y().Round())); |
| 95 |
| 96 NGPhysicalOffset offset = text_fragment_->Offset(); |
| 97 LayoutPoint box_origin(offset.left, offset.top); |
| 98 box_origin.Move(adjusted_paint_offset.X(), adjusted_paint_offset.Y()); |
| 99 |
| 100 LayoutRect box_rect(box_origin, LayoutSize(text_fragment_->Size().width, |
| 101 text_fragment_->Size().height)); |
| 102 |
| 103 GraphicsContext& context = paint_info.context; |
| 104 |
| 105 bool is_printing = paint_info.IsPrinting(); |
| 106 |
| 107 // Determine whether or not we're selected. |
| 108 // bool have_selection = !is_printing && |
| 109 // paint_info.phase != kPaintPhaseTextClip && |
| 110 // inline_text_box_.GetSelectionState() != SelectionNone; |
| 111 bool have_selection = false; |
| 112 if (!have_selection && paint_info.phase == kPaintPhaseSelection) { |
| 113 // When only painting the selection, don't bother to paint if there is none. |
| 114 return; |
| 115 } |
| 116 |
| 117 // Determine text colors. |
| 118 TextPainterBase::Style text_style = |
| 119 TextPainterBase::TextPaintingStyle(document, style_to_use, paint_info); |
| 120 // TextPainterBase::Style selection_style = |
| 121 // TextPainter::SelectionPaintingStyle( |
| 122 // text_fragment_.GetLineLayoutItem(), have_selection, paint_info, |
| 123 // text_style); |
| 124 TextPainterBase::Style selection_style = text_style; |
| 125 bool paint_selected_text_only = (paint_info.phase == kPaintPhaseSelection); |
| 126 bool paint_selected_text_separately = |
| 127 !paint_selected_text_only && text_style != selection_style; |
| 128 |
| 129 // Set our font. |
| 130 const Font& font = style_to_use.GetFont(); |
| 131 const SimpleFontData* font_data = font.PrimaryFont(); |
| 132 DCHECK(font_data); |
| 133 |
| 134 int ascent = font_data ? font_data->GetFontMetrics().Ascent() : 0; |
| 135 LayoutPoint text_origin(box_origin.X(), box_origin.Y() + ascent); |
| 136 |
| 137 // 1. Paint backgrounds behind text if needed. Examples of such backgrounds |
| 138 // include selection and composition highlights. |
| 139 if (paint_info.phase != kPaintPhaseSelection && |
| 140 paint_info.phase != kPaintPhaseTextClip && !is_printing) { |
| 141 // PaintDocumentMarkers(paint_info, box_origin, style_to_use, font, |
| 142 // DocumentMarkerPaintPhase::kBackground); |
| 143 |
| 144 // const LayoutObject& text_box_layout_object = InlineLayoutObject(); |
| 145 // if (have_selection && !PaintsCompositionMarkers(text_box_layout_object)) |
| 146 // { |
| 147 // if (combined_text) |
| 148 // PaintSelection<InlineTextBoxPainter::PaintOptions::kCombinedText>( |
| 149 // context, box_rect, style_to_use, font, selection_style.fill_color, |
| 150 // combined_text); |
| 151 // else |
| 152 // PaintSelection<InlineTextBoxPainter::PaintOptions::kNormal>( |
| 153 // context, box_rect, style_to_use, font, |
| 154 // selection_style.fill_color); |
| 155 //} |
| 156 } |
| 157 |
| 158 // 2. Now paint the foreground, including text and decorations. |
| 159 int selection_start = 0; |
| 160 int selection_end = 0; |
| 161 // if (paint_selected_text_only || paint_selected_text_separately) |
| 162 // text_fragment_.SelectionStartEnd(selection_start, selection_end); |
| 163 |
| 164 // bool respect_hyphen = |
| 165 // selection_end == static_cast<int>(text_fragment_.Len()) && |
| 166 // text_fragment_.HasHyphen(); |
| 167 // if (respect_hyphen) |
| 168 // selection_end = text_run.length(); |
| 169 |
| 170 unsigned length = text_fragment_->Text().length(); |
| 171 |
| 172 bool ltr = true; |
| 173 bool flow_is_ltr = true; |
| 174 if (text_fragment_->Truncation() != kCNoTruncation) { |
| 175 // In a mixed-direction flow the ellipsis is at the start of the text |
| 176 // rather than at the end of it. |
| 177 selection_start = |
| 178 ltr == flow_is_ltr |
| 179 ? std::min<int>(selection_start, text_fragment_->Truncation()) |
| 180 : std::max<int>(selection_start, text_fragment_->Truncation()); |
| 181 selection_end = |
| 182 ltr == flow_is_ltr |
| 183 ? std::min<int>(selection_end, text_fragment_->Truncation()) |
| 184 : std::max<int>(selection_end, text_fragment_->Truncation()); |
| 185 length = ltr == flow_is_ltr ? text_fragment_->Truncation() : length; |
| 186 } |
| 187 |
| 188 NGTextPainter text_painter(context, font, text_fragment_, text_origin, |
| 189 box_rect, text_fragment_->IsHorizontal()); |
| 190 TextEmphasisPosition emphasis_mark_position; |
| 191 // bool has_text_emphasis = text_fragment_->GetEmphasisMarkPosition( |
| 192 // style_to_use, emphasis_mark_position); |
| 193 emphasis_mark_position = kTextEmphasisPositionOver; |
| 194 bool has_text_emphasis = false; |
| 195 if (has_text_emphasis) { |
| 196 text_painter.SetEmphasisMark(style_to_use.TextEmphasisMarkString(), |
| 197 emphasis_mark_position); |
| 198 } |
| 199 // if (combined_text) |
| 200 // text_painter.SetCombinedText(combined_text); |
| 201 if (text_fragment_->Truncation() != kCNoTruncation && ltr != flow_is_ltr) |
| 202 text_painter.SetEllipsisOffset(text_fragment_->Truncation()); |
| 203 |
| 204 bool should_rotate = false; |
| 205 |
| 206 if (!paint_selected_text_only) { |
| 207 // Paint text decorations except line-through. |
| 208 DecorationInfo decoration_info; |
| 209 bool has_line_through_decoration = false; |
| 210 if (style_to_use.TextDecorationsInEffect() != TextDecoration::kNone && |
| 211 text_fragment_->Truncation() != kCFullTruncation) { |
| 212 LayoutPoint local_origin = LayoutPoint(box_origin); |
| 213 LayoutUnit width = text_fragment_->Size().width; |
| 214 // const NGPhysicalTextFragment* decorating_box = |
| 215 // EnclosingUnderlineObject(&text_fragment_); |
| 216 const ComputedStyle* decorating_box_style = nullptr; |
| 217 // FontBaseline baseline_type = text_fragment_->Root().BaselineType(); |
| 218 FontBaseline baseline_type = kAlphabeticBaseline; |
| 219 |
| 220 text_painter.ComputeDecorationInfo(decoration_info, box_origin, |
| 221 local_origin, width, baseline_type, |
| 222 style_to_use, decorating_box_style); |
| 223 |
| 224 GraphicsContextStateSaver state_saver(context, false); |
| 225 PrepareContextForDecoration(context, state_saver, |
| 226 text_fragment_->IsHorizontal(), text_style, |
| 227 box_rect); |
| 228 PaintDecorationsExceptLineThrough( |
| 229 text_painter, has_line_through_decoration, text_fragment_, |
| 230 decoration_info, paint_info, style_to_use.AppliedTextDecorations()); |
| 231 RestoreContextFromDecoration(context, box_rect); |
| 232 } |
| 233 |
| 234 int start_offset = 0; |
| 235 int end_offset = length; |
| 236 // Where the text and its flow have opposite directions then our offset into |
| 237 // the text given by |truncation| is at the start of the part that will be |
| 238 // visible. |
| 239 if (text_fragment_->Truncation() != kCNoTruncation && ltr != flow_is_ltr) { |
| 240 start_offset = text_fragment_->Truncation(); |
| 241 end_offset = length; |
| 242 } |
| 243 |
| 244 if (paint_selected_text_separately && selection_start < selection_end) { |
| 245 start_offset = selection_end; |
| 246 end_offset = selection_start; |
| 247 } |
| 248 |
| 249 text_painter.Paint(start_offset, end_offset, length, text_style); |
| 250 |
| 251 // Paint line-through decoration if needed. |
| 252 if (has_line_through_decoration) { |
| 253 GraphicsContextStateSaver state_saver(context, false); |
| 254 PrepareContextForDecoration(context, state_saver, |
| 255 text_fragment_->IsHorizontal(), text_style, |
| 256 box_rect); |
| 257 text_painter.PaintDecorationsOnlyLineThrough( |
| 258 decoration_info, paint_info, style_to_use.AppliedTextDecorations()); |
| 259 RestoreContextFromDecoration(context, box_rect); |
| 260 } |
| 261 } |
| 262 |
| 263 if ((paint_selected_text_only || paint_selected_text_separately) && |
| 264 selection_start < selection_end) { |
| 265 // paint only the text that is selected |
| 266 text_painter.Paint(selection_start, selection_end, length, selection_style); |
| 267 } |
| 268 |
| 269 // if (paint_info.phase == kPaintPhaseForeground) |
| 270 // PaintDocumentMarkers(paint_info, box_origin, style_to_use, font, |
| 271 // DocumentMarkerPaintPhase::kForeground); |
| 272 |
| 273 if (should_rotate) { |
| 274 context.ConcatCTM(TextPainterBase::Rotation( |
| 275 box_rect, TextPainterBase::kCounterclockwise)); |
| 276 } |
| 277 } |
| 278 |
| 279 } // namespace blink |
OLD | NEW |