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/NGBoxFragmentPainter.h" |
| 6 |
| 7 #include "core/layout/BackgroundBleedAvoidance.h" |
| 8 #include "core/layout/ng/inline/ng_physical_line_box_fragment.h" |
| 9 #include "core/layout/ng/inline/ng_physical_text_fragment.h" |
| 10 #include "core/layout/ng/ng_physical_box_fragment.h" |
| 11 #include "core/paint/BoxBorderPainter.h" |
| 12 #include "core/paint/BoxDecorationData.h" |
| 13 #include "core/paint/NGTextFragmentPainter.h" |
| 14 #include "core/paint/PaintInfo.h" |
| 15 #include "core/paint/PaintLayer.h" |
| 16 #include "core/paint/RoundedInnerRectClipper.h" |
| 17 #include "core/style/FillLayer.h" |
| 18 #include "platform/geometry/LayoutRectOutsets.h" |
| 19 #include "platform/graphics/GraphicsContextStateSaver.h" |
| 20 #include "platform/graphics/paint/DrawingRecorder.h" |
| 21 |
| 22 namespace blink { |
| 23 |
| 24 void NGBoxFragmentPainter::Paint(const PaintInfo& paint_info, |
| 25 const LayoutPoint& paint_offset) { |
| 26 PaintChildren(paint_info, paint_offset); |
| 27 } |
| 28 |
| 29 void NGBoxFragmentPainter::PaintChildren(const PaintInfo& paint_info, |
| 30 const LayoutPoint& paint_offset) { |
| 31 PaintInfo child_info(paint_info); |
| 32 for (const auto& child : box_fragment_->Children()) { |
| 33 if (child->Type() == NGPhysicalBoxFragment::kFragmentLineBox) { |
| 34 const NGPhysicalLineBoxFragment* line_box_fragment = |
| 35 ToNGPhysicalLineBoxFragment(child.Get()); |
| 36 PaintLineBox(line_box_fragment, child_info, paint_offset); |
| 37 } |
| 38 } |
| 39 } |
| 40 |
| 41 void NGBoxFragmentPainter::PaintLineBox( |
| 42 const NGPhysicalLineBoxFragment* fragment, |
| 43 const PaintInfo& paint_info, |
| 44 const LayoutPoint& paint_offset) { |
| 45 // TODO: Should this check if the line boxes intersects with the dirty rect |
| 46 // like legacy layout or do we want to change the invalidation logic? |
| 47 |
| 48 LayoutRect overflow_rect(box_fragment_->VisualOverflowRect()); |
| 49 overflow_rect.MoveBy(paint_offset); |
| 50 |
| 51 // TODO(eae): Should this go here, in ::Paint or in the NGTextFragmentPainter |
| 52 // class? |
| 53 DrawingRecorder recorder( |
| 54 paint_info.context, *fragment, |
| 55 DisplayItem::PaintPhaseToDrawingType(paint_info.phase), |
| 56 PixelSnappedIntRect(overflow_rect)); |
| 57 |
| 58 for (const auto& child : fragment->Children()) { |
| 59 if (child->Type() == NGPhysicalBoxFragment::kFragmentLineBox) { |
| 60 CHECK(false); |
| 61 } else if (child->Type() == NGPhysicalBoxFragment::kFragmentText) { |
| 62 const NGPhysicalTextFragment* text_fragment = |
| 63 ToNGPhysicalTextFragment(child.Get()); |
| 64 NGTextFragmentPainter(text_fragment) |
| 65 .Paint(GetDocument(), paint_info, paint_offset); |
| 66 } |
| 67 } |
| 68 } |
| 69 |
| 70 bool IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( |
| 71 const NGPhysicalFragment* fragment, |
| 72 const PaintInfo& paint_info) { |
| 73 // TODO(eae): Implement. Will require changing the type of |
| 74 // PaintInfo::paint_container_ or using the NGBoxFragment container object. |
| 75 return false; |
| 76 } |
| 77 |
| 78 void NGBoxFragmentPainter::PaintBoxDecorationBackground( |
| 79 const PaintInfo& paint_info, |
| 80 const LayoutPoint& paint_offset) { |
| 81 LayoutRect paint_rect; |
| 82 if (IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( |
| 83 box_fragment_, paint_info)) { |
| 84 // For the case where we are painting the background into the scrolling |
| 85 // contents layer of a composited scroller we need to include the entire |
| 86 // overflow rect. |
| 87 |
| 88 // The background painting code assumes that the borders are part of the |
| 89 // paintRect so we expand the paintRect by the border size when painting the |
| 90 // background into the scrolling contents layer. |
| 91 } else { |
| 92 // TODO(eae): We need better converters for ng geometry types. Long term we |
| 93 // probably want to change the paint code to take NGPhysical* but that is a |
| 94 // much bigger change. |
| 95 NGPhysicalSize size = box_fragment_->Size(); |
| 96 paint_rect = LayoutRect(LayoutPoint(), LayoutSize(size.width, size.height)); |
| 97 } |
| 98 |
| 99 paint_rect.MoveBy(paint_offset); |
| 100 PaintBoxDecorationBackgroundWithRect(paint_info, paint_offset, paint_rect); |
| 101 } |
| 102 |
| 103 void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect( |
| 104 const PaintInfo& paint_info, |
| 105 const LayoutPoint& paint_offset, |
| 106 const LayoutRect& paint_rect) { |
| 107 bool painting_overflow_contents = |
| 108 IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( |
| 109 box_fragment_, paint_info); |
| 110 const ComputedStyle& style = box_fragment_->Style(); |
| 111 |
| 112 // TODO(eae): Implement support for painting overflow contents. |
| 113 const DisplayItemClient& display_item_client = *box_fragment_; |
| 114 if (DrawingRecorder::UseCachedDrawingIfPossible( |
| 115 paint_info.context, display_item_client, |
| 116 DisplayItem::kBoxDecorationBackground)) |
| 117 return; |
| 118 |
| 119 DrawingRecorder recorder( |
| 120 paint_info.context, display_item_client, |
| 121 DisplayItem::kBoxDecorationBackground, |
| 122 FloatRect(BoundsForDrawingRecorder(paint_info, paint_offset))); |
| 123 BoxDecorationData box_decoration_data(box_fragment_); |
| 124 GraphicsContextStateSaver state_saver(paint_info.context, false); |
| 125 |
| 126 if (!painting_overflow_contents) { |
| 127 // FIXME: Should eventually give the theme control over whether the box |
| 128 // shadow should paint, since controls could have custom shadows of their |
| 129 // own. |
| 130 PaintNormalBoxShadow(paint_info, paint_rect, style); |
| 131 |
| 132 if (BleedAvoidanceIsClipping(box_decoration_data.bleed_avoidance)) { |
| 133 state_saver.Save(); |
| 134 FloatRoundedRect border = style.GetRoundedBorderFor(paint_rect); |
| 135 paint_info.context.ClipRoundedRect(border); |
| 136 |
| 137 if (box_decoration_data.bleed_avoidance == kBackgroundBleedClipLayer) |
| 138 paint_info.context.BeginLayer(); |
| 139 } |
| 140 } |
| 141 |
| 142 // TODO(layout-dev): Support theme painting. |
| 143 |
| 144 // TODO(eae): Support SkipRootBackground painting. |
| 145 bool should_paint_background = true; |
| 146 if (should_paint_background) { |
| 147 PaintBackground(paint_info, paint_rect, |
| 148 box_decoration_data.background_color, |
| 149 box_decoration_data.bleed_avoidance); |
| 150 } |
| 151 |
| 152 if (!painting_overflow_contents) { |
| 153 PaintInsetBoxShadow(paint_info, paint_rect, style); |
| 154 |
| 155 if (box_decoration_data.has_border_decoration) { |
| 156 PaintBorder(box_fragment_, paint_info, paint_rect, style, |
| 157 box_decoration_data.bleed_avoidance); |
| 158 } |
| 159 } |
| 160 |
| 161 if (box_decoration_data.bleed_avoidance == kBackgroundBleedClipLayer) |
| 162 paint_info.context.EndLayer(); |
| 163 } |
| 164 |
| 165 void NGBoxFragmentPainter::PaintBackground( |
| 166 const PaintInfo& paint_info, |
| 167 const LayoutRect& paint_rect, |
| 168 const Color& background_color, |
| 169 BackgroundBleedAvoidance bleed_avoidance) { |
| 170 // if (layout_box_.IsDocumentElement()) |
| 171 // return; |
| 172 // if (layout_box_.BackgroundStolenForBeingBody()) |
| 173 // return; |
| 174 // if (layout_box_.BackgroundIsKnownToBeObscured()) |
| 175 // return; |
| 176 PaintFillLayers(paint_info, background_color, |
| 177 box_fragment_->Style().BackgroundLayers(), paint_rect, |
| 178 bleed_avoidance); |
| 179 } |
| 180 |
| 181 void NGBoxFragmentPainter::PaintFillLayers( |
| 182 const PaintInfo& paint_info, |
| 183 const Color& c, |
| 184 const FillLayer& fill_layer, |
| 185 const LayoutRect& rect, |
| 186 BackgroundBleedAvoidance avoidance, |
| 187 SkBlendMode op, |
| 188 const NGPhysicalFragment* bg_fragment) { |
| 189 FillLayerOcclusionOutputList reversed_paint_list; |
| 190 bool should_draw_background_in_separate_buffer = |
| 191 CalculateFillLayerOcclusionCulling(reversed_paint_list, fill_layer, |
| 192 GetDocument(), box_fragment_->Style()); |
| 193 |
| 194 // TODO(trchen): We can optimize out isolation group if we have a |
| 195 // non-transparent background color and the bottom layer encloses all other |
| 196 // layers. |
| 197 |
| 198 GraphicsContext& context = paint_info.context; |
| 199 |
| 200 if (should_draw_background_in_separate_buffer) |
| 201 context.BeginLayer(); |
| 202 |
| 203 for (auto it = reversed_paint_list.rbegin(); it != reversed_paint_list.rend(); |
| 204 ++it) { |
| 205 PaintFillLayer(box_fragment_, paint_info, c, **it, rect, avoidance, 0, |
| 206 LayoutSize(), op, bg_fragment); |
| 207 } |
| 208 |
| 209 if (should_draw_background_in_separate_buffer) |
| 210 context.EndLayer(); |
| 211 } |
| 212 |
| 213 namespace { |
| 214 |
| 215 FloatRoundedRect GetBackgroundRoundedRect(const ComputedStyle& style, |
| 216 const LayoutRect& border_rect, |
| 217 bool has_line_box_sibling, |
| 218 const LayoutSize& inline_box_size, |
| 219 bool include_logical_left_edge, |
| 220 bool include_logical_right_edge) { |
| 221 FloatRoundedRect border = style.GetRoundedBorderFor( |
| 222 border_rect, include_logical_left_edge, include_logical_right_edge); |
| 223 if (has_line_box_sibling) { |
| 224 FloatRoundedRect segment_border = style.GetRoundedBorderFor( |
| 225 LayoutRect(LayoutPoint(), LayoutSize(FlooredIntSize(inline_box_size))), |
| 226 include_logical_left_edge, include_logical_right_edge); |
| 227 border.SetRadii(segment_border.GetRadii()); |
| 228 } |
| 229 return border; |
| 230 } |
| 231 |
| 232 FloatRoundedRect BackgroundRoundedRectAdjustedForBleedAvoidance( |
| 233 const ComputedStyle& style, |
| 234 const LayoutRect& border_rect, |
| 235 BackgroundBleedAvoidance bleed_avoidance, |
| 236 bool has_line_box_sibling, |
| 237 const LayoutSize& box_size, |
| 238 bool include_logical_left_edge, |
| 239 bool include_logical_right_edge) { |
| 240 if (bleed_avoidance == kBackgroundBleedShrinkBackground) { |
| 241 // Inset the background rect by a "safe" amount: 1/2 border-width for opaque |
| 242 // border styles, 1/6 border-width for double borders. |
| 243 |
| 244 // TODO(fmalita): we should be able to fold these parameters into |
| 245 // BoxBorderInfo or BoxDecorationData and avoid calling getBorderEdgeInfo |
| 246 // redundantly here. |
| 247 BorderEdge edges[4]; |
| 248 style.GetBorderEdgeInfo(edges, include_logical_left_edge, |
| 249 include_logical_right_edge); |
| 250 |
| 251 // Use the most conservative inset to avoid mixed-style corner issues. |
| 252 float fractional_inset = 1.0f / 2; |
| 253 for (auto& edge : edges) { |
| 254 if (edge.BorderStyle() == kBorderStyleDouble) { |
| 255 fractional_inset = 1.0f / 6; |
| 256 break; |
| 257 } |
| 258 } |
| 259 |
| 260 FloatRectOutsets insets(-fractional_inset * edges[kBSTop].Width(), |
| 261 -fractional_inset * edges[kBSRight].Width(), |
| 262 -fractional_inset * edges[kBSBottom].Width(), |
| 263 -fractional_inset * edges[kBSLeft].Width()); |
| 264 |
| 265 FloatRoundedRect background_rounded_rect = GetBackgroundRoundedRect( |
| 266 style, border_rect, has_line_box_sibling, box_size, |
| 267 include_logical_left_edge, include_logical_right_edge); |
| 268 FloatRect inset_rect(background_rounded_rect.Rect()); |
| 269 inset_rect.Expand(insets); |
| 270 FloatRoundedRect::Radii inset_radii(background_rounded_rect.GetRadii()); |
| 271 inset_radii.Shrink(-insets.Top(), -insets.Bottom(), -insets.Left(), |
| 272 -insets.Right()); |
| 273 return FloatRoundedRect(inset_rect, inset_radii); |
| 274 } |
| 275 |
| 276 return GetBackgroundRoundedRect(style, border_rect, has_line_box_sibling, |
| 277 box_size, include_logical_left_edge, |
| 278 include_logical_right_edge); |
| 279 } |
| 280 |
| 281 struct FillLayerInfo { |
| 282 STACK_ALLOCATED(); |
| 283 |
| 284 FillLayerInfo(const Document& doc, |
| 285 const ComputedStyle& style, |
| 286 bool has_overflow_clip, |
| 287 Color bg_color, |
| 288 const FillLayer& layer, |
| 289 BackgroundBleedAvoidance bleed_avoidance, |
| 290 const NGPhysicalFragment* box) |
| 291 : image(layer.GetImage()), |
| 292 color(bg_color), |
| 293 include_left_edge( |
| 294 box ? (box->BorderEdges() & NGPhysicalTextFragment::kLeftBorder) |
| 295 : true), |
| 296 include_right_edge( |
| 297 box ? (box->BorderEdges() & NGPhysicalTextFragment::kRightBorder) |
| 298 : true), |
| 299 is_bottom_layer(!layer.Next()), |
| 300 is_border_fill(layer.Clip() == kBorderFillBox), |
| 301 is_clipped_with_local_scrolling(has_overflow_clip && |
| 302 layer.Attachment() == |
| 303 kLocalBackgroundAttachment) { |
| 304 // When printing backgrounds is disabled or using economy mode, |
| 305 // change existing background colors and images to a solid white background. |
| 306 // If there's no bg color or image, leave it untouched to avoid affecting |
| 307 // transparency. We don't try to avoid loading the background images, |
| 308 // because this style flag is only set when printing, and at that point |
| 309 // we've already loaded the background images anyway. (To avoid loading the |
| 310 // background images we'd have to do this check when applying styles rather |
| 311 // than while layout.) |
| 312 if (BoxPainterBase::ShouldForceWhiteBackgroundForPrintEconomy(doc, style)) { |
| 313 // Note that we can't reuse this variable below because the bgColor might |
| 314 // be changed. |
| 315 bool should_paint_background_color = is_bottom_layer && color.Alpha(); |
| 316 if (image || should_paint_background_color) { |
| 317 color = Color::kWhite; |
| 318 image = nullptr; |
| 319 } |
| 320 } |
| 321 |
| 322 const bool has_rounded_border = |
| 323 style.HasBorderRadius() && (include_left_edge || include_right_edge); |
| 324 // BorderFillBox radius clipping is taken care of by |
| 325 // BackgroundBleedClip{Only,Layer} |
| 326 is_rounded_fill = |
| 327 has_rounded_border && |
| 328 !(is_border_fill && BleedAvoidanceIsClipping(bleed_avoidance)); |
| 329 |
| 330 should_paint_image = image && image->CanRender(); |
| 331 should_paint_color = |
| 332 is_bottom_layer && color.Alpha() && |
| 333 (!should_paint_image || !layer.ImageOccludesNextLayers(doc, style)); |
| 334 } |
| 335 |
| 336 // FillLayerInfo is a temporary, stack-allocated container which cannot |
| 337 // outlive the StyleImage. This would normally be a raw pointer, if not for |
| 338 // the Oilpan tooling complaints. |
| 339 Member<StyleImage> image; |
| 340 Color color; |
| 341 |
| 342 bool include_left_edge; |
| 343 bool include_right_edge; |
| 344 bool is_bottom_layer; |
| 345 bool is_border_fill; |
| 346 bool is_clipped_with_local_scrolling; |
| 347 bool is_rounded_fill; |
| 348 |
| 349 bool should_paint_image; |
| 350 bool should_paint_color; |
| 351 }; |
| 352 |
| 353 inline bool PaintFastBottomLayer( |
| 354 const NGPhysicalBoxFragment* fragment, |
| 355 const PaintInfo& paint_info, |
| 356 const FillLayerInfo& info, |
| 357 const FillLayer& layer, |
| 358 const LayoutRect& rect, |
| 359 BackgroundBleedAvoidance bleed_avoidance, |
| 360 bool has_line_box_sibling, |
| 361 const LayoutSize& box_size, |
| 362 SkBlendMode op, |
| 363 const NGPhysicalFragment* background_fragment |
| 364 /*Optional<BackgroundImageGeometry>& geometry*/) { |
| 365 // Painting a background image from an ancestor onto a cell is a complex case. |
| 366 // if (obj.IsTableCell() && bg_fragment && |
| 367 // !bg_fragment->IsTableCell()) |
| 368 // return false; |
| 369 // Complex cases not handled on the fast path. |
| 370 // if (!info.is_bottom_layer || !info.is_border_fill || |
| 371 // info.is_clipped_with_local_scrolling) |
| 372 // return false; |
| 373 |
| 374 // Transparent layer, nothing to paint. |
| 375 if (!info.should_paint_color && !info.should_paint_image) |
| 376 return true; |
| 377 |
| 378 // When the layer has an image, figure out whether it is covered by a single |
| 379 // tile. |
| 380 FloatRect image_tile; |
| 381 if (info.should_paint_image) { |
| 382 // TODO(layout-dev): Add support for background images. |
| 383 return false; |
| 384 } |
| 385 |
| 386 // At this point we're committed to the fast path: the destination (r)rect |
| 387 // fits within a single tile, and we can paint it using direct draw(R)Rect() |
| 388 // calls. |
| 389 GraphicsContext& context = paint_info.context; |
| 390 FloatRoundedRect border = |
| 391 info.is_rounded_fill |
| 392 ? BackgroundRoundedRectAdjustedForBleedAvoidance( |
| 393 fragment->Style(), rect, bleed_avoidance, has_line_box_sibling, |
| 394 box_size, info.include_left_edge, info.include_right_edge) |
| 395 : FloatRoundedRect(PixelSnappedIntRect(rect)); |
| 396 |
| 397 // Optional<RoundedInnerRectClipper> clipper; |
| 398 // if (info.is_rounded_fill && !border.IsRenderable()) { |
| 399 // When the rrect is not renderable, we resort to clipping. |
| 400 // RoundedInnerRectClipper handles this case via discrete, corner-wise |
| 401 // clipping. |
| 402 // clipper.emplace(obj, paint_info, rect, border, kApplyToContext); |
| 403 // border.SetRadii(FloatRoundedRect::Radii()); |
| 404 //} |
| 405 |
| 406 // Paint the color if needed. |
| 407 if (info.should_paint_color) |
| 408 context.FillRoundedRect(border, info.color); |
| 409 |
| 410 return true; |
| 411 } |
| 412 |
| 413 } // anonymous namespace |
| 414 |
| 415 void NGBoxFragmentPainter::PaintFillLayer( |
| 416 const NGPhysicalBoxFragment* fragment, |
| 417 const PaintInfo& paint_info, |
| 418 const Color& color, |
| 419 const FillLayer& bg_layer, |
| 420 const LayoutRect& rect, |
| 421 BackgroundBleedAvoidance avoidance, |
| 422 const NGPhysicalFragment* box, |
| 423 const LayoutSize& box_size, |
| 424 SkBlendMode op, |
| 425 const NGPhysicalFragment* bg_fragment) { |
| 426 GraphicsContext& context = paint_info.context; |
| 427 const ComputedStyle& style = fragment->Style(); |
| 428 if (rect.IsEmpty()) |
| 429 return; |
| 430 |
| 431 const FillLayerInfo info(fragment->GetLayoutObject()->GetDocument(), style, |
| 432 fragment->HasOverflowClip(), color, bg_layer, |
| 433 avoidance, box); |
| 434 // Optional<BackgroundImageGeometry> geometry; |
| 435 // bool has_line_box_sibling = box && (box->NextLineBox() || |
| 436 // box->PrevLineBox()); |
| 437 bool has_line_box_sibling = false; |
| 438 |
| 439 // Fast path for drawing simple color backgrounds. |
| 440 if (PaintFastBottomLayer(fragment, paint_info, info, bg_layer, rect, |
| 441 avoidance, has_line_box_sibling, box_size, op, |
| 442 bg_fragment)) { |
| 443 return; |
| 444 } |
| 445 |
| 446 NGPixelSnappedPhysicalBoxStrut borders = fragment->BorderWidths(); |
| 447 NGPhysicalBoxStrut paddings; // TODO(layout-dev): Implement |
| 448 |
| 449 Optional<RoundedInnerRectClipper> clip_to_border; |
| 450 if (info.is_rounded_fill) { |
| 451 FloatRoundedRect border = |
| 452 info.is_border_fill |
| 453 ? BackgroundRoundedRectAdjustedForBleedAvoidance( |
| 454 style, rect, avoidance, has_line_box_sibling, box_size, |
| 455 info.include_left_edge, info.include_right_edge) |
| 456 : GetBackgroundRoundedRect(style, rect, has_line_box_sibling, |
| 457 box_size, info.include_left_edge, |
| 458 info.include_right_edge); |
| 459 |
| 460 // Clip to the padding or content boxes as necessary. |
| 461 if (bg_layer.Clip() == kContentFillBox) { |
| 462 // TODO(layout-dev): Do we need to include the padding here? |
| 463 LayoutRectOutsets border_inset( |
| 464 LayoutUnit(borders.top), LayoutUnit(borders.right), |
| 465 LayoutUnit(borders.bottom), LayoutUnit(borders.left)); |
| 466 border = style.GetRoundedInnerBorderFor( |
| 467 LayoutRect(border.Rect()), border_inset, info.include_left_edge, |
| 468 info.include_right_edge); |
| 469 } else if (bg_layer.Clip() == kPaddingFillBox) { |
| 470 border = style.GetRoundedInnerBorderFor(LayoutRect(border.Rect()), |
| 471 info.include_left_edge, |
| 472 info.include_right_edge); |
| 473 } |
| 474 |
| 475 clip_to_border.emplace(*fragment, paint_info, rect, border, |
| 476 kApplyToContext); |
| 477 } |
| 478 |
| 479 GraphicsContextStateSaver clip_with_scrolling_state_saver( |
| 480 context, info.is_clipped_with_local_scrolling); |
| 481 LayoutRect scrolled_paint_rect = rect; |
| 482 if (info.is_clipped_with_local_scrolling && |
| 483 !IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( |
| 484 fragment, paint_info)) { |
| 485 // Clip to the overflow area. |
| 486 // TODO(chrishtr): this should be pixel-snapped. |
| 487 // TODO(layout-dev): We don't know the location... |
| 488 // context.Clip(FloatRect(this_box.OverflowClipRect(rect.Location()))); |
| 489 |
| 490 // Adjust the paint rect to reflect a scrolled content box with borders at |
| 491 // the ends. |
| 492 // TODO(layout_dev): Support scrolling |
| 493 // IntSize offset = this_box.ScrolledContentOffset(); |
| 494 // scrolled_paint_rect.Move(-offset); |
| 495 // scrolled_paint_rect.SetWidth(borders.left + this_box.ScrollWidth() + |
| 496 // borders.right); scrolled_paint_rect.SetHeight(this_box.BorderTop() + |
| 497 // this_box.ScrollHeight() + |
| 498 // this_box.BorderBottom()); |
| 499 } |
| 500 |
| 501 GraphicsContextStateSaver background_clip_state_saver(context, false); |
| 502 IntRect mask_rect; |
| 503 |
| 504 switch (bg_layer.Clip()) { |
| 505 case kPaddingFillBox: |
| 506 case kContentFillBox: { |
| 507 if (info.is_rounded_fill) |
| 508 break; |
| 509 |
| 510 // Clip to the padding or content boxes as necessary. |
| 511 bool include_padding = bg_layer.Clip() == kContentFillBox; |
| 512 LayoutRect clip_rect( |
| 513 scrolled_paint_rect.X() + borders.left, |
| 514 scrolled_paint_rect.Y() + borders.top, |
| 515 scrolled_paint_rect.Width() - borders.left - borders.right, |
| 516 scrolled_paint_rect.Height() - borders.top - borders.bottom); |
| 517 |
| 518 if (include_padding) { |
| 519 clip_rect.ContractEdges(paddings.top, paddings.right, paddings.bottom, |
| 520 paddings.left); |
| 521 } |
| 522 |
| 523 background_clip_state_saver.Save(); |
| 524 // TODO(chrishtr): this should be pixel-snapped. |
| 525 context.Clip(FloatRect(clip_rect)); |
| 526 |
| 527 break; |
| 528 } |
| 529 case kTextFillBox: { |
| 530 // First figure out how big the mask has to be. It should be no bigger |
| 531 // than what we need to actually render, so we should intersect the dirty |
| 532 // rect with the border box of the background. |
| 533 mask_rect = PixelSnappedIntRect(rect); |
| 534 |
| 535 // We draw the background into a separate layer, to be later masked with |
| 536 // yet another layer holding the text content. |
| 537 background_clip_state_saver.Save(); |
| 538 context.Clip(mask_rect); |
| 539 context.BeginLayer(); |
| 540 |
| 541 break; |
| 542 } |
| 543 case kBorderFillBox: |
| 544 break; |
| 545 default: |
| 546 NOTREACHED(); |
| 547 break; |
| 548 } |
| 549 |
| 550 // Paint the color first underneath all images, culled if background image |
| 551 // occludes it. |
| 552 // TODO(trchen): In the !bgLayer.hasRepeatXY() case, we could improve the |
| 553 // culling test by verifying whether the background image covers the entire |
| 554 // painting area. |
| 555 if (info.is_bottom_layer && info.color.Alpha() && info.should_paint_color) { |
| 556 IntRect background_rect(PixelSnappedIntRect(scrolled_paint_rect)); |
| 557 context.FillRect(background_rect, info.color); |
| 558 } |
| 559 |
| 560 // No progressive loading of the background image. |
| 561 if (info.should_paint_image) { |
| 562 // TODO(layout-dev): Add support for image painting. |
| 563 DCHECK(false); |
| 564 } |
| 565 |
| 566 if (bg_layer.Clip() == kTextFillBox) { |
| 567 // Create the text mask layer. |
| 568 context.BeginLayer(1, SkBlendMode::kDstIn); |
| 569 |
| 570 // Now draw the text into the mask. We do this by painting using a special |
| 571 // paint phase that signals to |
| 572 // InlineTextBoxes that they should just add their contents to the clip. |
| 573 PaintInfo info(context, mask_rect, kPaintPhaseTextClip, |
| 574 kGlobalPaintNormalPhase, 0); |
| 575 // if (box) { |
| 576 // const RootInlineBox& root = box->Root(); |
| 577 // box->Paint(info, |
| 578 // LayoutPoint(scrolled_paint_rect.X() - box->X(), |
| 579 // scrolled_paint_rect.Y() - box->Y()), |
| 580 // root.LineTop(), root.LineBottom()); |
| 581 //} else { |
| 582 // // FIXME: this should only have an effect for the line box list within |
| 583 // // |obj|. Change this to create a LineBoxListPainter directly. |
| 584 // LayoutSize local_offset = |
| 585 // obj.IsBox() ? ToLayoutBox(&obj)->LocationOffset() : LayoutSize(); |
| 586 // obj.Paint(info, scrolled_paint_rect.Location() - local_offset); |
| 587 //} |
| 588 |
| 589 context.EndLayer(); |
| 590 context.EndLayer(); |
| 591 } |
| 592 } |
| 593 |
| 594 LayoutRect NGBoxFragmentPainter::BoundsForDrawingRecorder( |
| 595 const PaintInfo& paint_info, |
| 596 const LayoutPoint& adjusted_paint_offset) { |
| 597 LayoutRect bounds = box_fragment_->VisualOverflowRect(); |
| 598 bounds.MoveBy(adjusted_paint_offset); |
| 599 return bounds; |
| 600 } |
| 601 |
| 602 void NGBoxFragmentPainter::PaintBorder(const NGPhysicalBoxFragment* fragment, |
| 603 const PaintInfo& info, |
| 604 const LayoutRect& rect, |
| 605 const ComputedStyle& style, |
| 606 BackgroundBleedAvoidance bleed_avoidance, |
| 607 bool include_logical_left_edge, |
| 608 bool include_logical_right_edge) { |
| 609 // TODO(layout-dev): Add support for image border painting. |
| 610 const BoxBorderPainter border_painter(rect, style, bleed_avoidance, |
| 611 include_logical_left_edge, |
| 612 include_logical_right_edge); |
| 613 border_painter.PaintBorder(info, rect); |
| 614 } |
| 615 |
| 616 const Document& NGBoxFragmentPainter::GetDocument() const { |
| 617 return box_fragment_->GetLayoutObject()->GetDocument(); |
| 618 } |
| 619 |
| 620 } // namespace blink |
OLD | NEW |