blob: f389fe7c269bd358bdf9fae917661bf54cc63a5c [file] [log] [blame]
// Copyright 2014 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 "third_party/blink/renderer/core/paint/inline_box_painter_base.h"
#include "third_party/blink/renderer/core/paint/background_image_geometry.h"
#include "third_party/blink/renderer/core/paint/box_painter_base.h"
#include "third_party/blink/renderer/core/paint/nine_piece_image_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
namespace blink {
PhysicalRect InlineBoxPainterBase::ClipRectForNinePieceImageStrip(
const ComputedStyle& style,
PhysicalBoxSides sides_to_include,
const NinePieceImage& image,
const PhysicalRect& paint_rect) {
PhysicalRect clip_rect(paint_rect);
LayoutRectOutsets outsets = style.ImageOutsets(image);
if (sides_to_include.left) {
clip_rect.SetX(paint_rect.X() - outsets.Left());
clip_rect.SetWidth(paint_rect.Width() + outsets.Left());
}
if (sides_to_include.right)
clip_rect.SetWidth(clip_rect.Width() + outsets.Right());
if (sides_to_include.top) {
clip_rect.SetY(paint_rect.Y() - outsets.Top());
clip_rect.SetHeight(paint_rect.Height() + outsets.Top());
}
if (sides_to_include.bottom)
clip_rect.SetHeight(clip_rect.Height() + outsets.Bottom());
return clip_rect;
}
void InlineBoxPainterBase::PaintBoxDecorationBackground(
BoxPainterBase& box_painter,
const PaintInfo& paint_info,
const PhysicalOffset& paint_offset,
const PhysicalRect& adjusted_frame_rect,
BackgroundImageGeometry geometry,
bool object_has_multiple_boxes,
PhysicalBoxSides sides_to_include) {
// Shadow comes first and is behind the background and border.
PaintNormalBoxShadow(paint_info, line_style_, adjusted_frame_rect);
Color background_color =
line_style_.VisitedDependentColor(GetCSSPropertyBackgroundColor());
PaintFillLayers(box_painter, paint_info, background_color,
line_style_.BackgroundLayers(), adjusted_frame_rect, geometry,
object_has_multiple_boxes);
PaintInsetBoxShadow(paint_info, line_style_, adjusted_frame_rect);
IntRect adjusted_clip_rect;
BorderPaintingType border_painting_type = GetBorderPaintType(
adjusted_frame_rect, adjusted_clip_rect, object_has_multiple_boxes);
switch (border_painting_type) {
case kDontPaintBorders:
break;
case kPaintBordersWithoutClip:
BoxPainterBase::PaintBorder(image_observer_, *document_, node_,
paint_info, adjusted_frame_rect, line_style_,
kBackgroundBleedNone, sides_to_include);
break;
case kPaintBordersWithClip:
// FIXME: What the heck do we do with RTL here? The math we're using is
// obviously not right, but it isn't even clear how this should work at
// all.
PhysicalRect image_strip_paint_rect =
PaintRectForImageStrip(adjusted_frame_rect, TextDirection::kLtr);
GraphicsContextStateSaver state_saver(paint_info.context);
paint_info.context.Clip(adjusted_clip_rect);
BoxPainterBase::PaintBorder(image_observer_, *document_, node_,
paint_info, image_strip_paint_rect,
line_style_);
break;
}
}
void InlineBoxPainterBase::PaintFillLayers(BoxPainterBase& box_painter,
const PaintInfo& info,
const Color& c,
const FillLayer& layer,
const PhysicalRect& rect,
BackgroundImageGeometry& geometry,
bool object_has_multiple_boxes) {
// FIXME: This should be a for loop or similar. It's a little non-trivial to
// do so, however, since the layers need to be painted in reverse order.
if (layer.Next()) {
PaintFillLayers(box_painter, info, c, *layer.Next(), rect, geometry,
object_has_multiple_boxes);
}
PaintFillLayer(box_painter, info, c, layer, rect, geometry,
object_has_multiple_boxes);
}
void InlineBoxPainterBase::PaintFillLayer(BoxPainterBase& box_painter,
const PaintInfo& paint_info,
const Color& c,
const FillLayer& fill_layer,
const PhysicalRect& paint_rect,
BackgroundImageGeometry& geometry,
bool object_has_multiple_boxes) {
StyleImage* img = fill_layer.GetImage();
bool has_fill_image = img && img->CanRender();
if (!object_has_multiple_boxes ||
(!has_fill_image && !style_.HasBorderRadius())) {
box_painter.PaintFillLayer(paint_info, c, fill_layer, paint_rect,
kBackgroundBleedNone, geometry, false);
return;
}
// Handle fill images that clone or spans multiple lines.
bool multi_line = object_has_multiple_boxes &&
style_.BoxDecorationBreak() != EBoxDecorationBreak::kClone;
PhysicalRect rect =
multi_line ? PaintRectForImageStrip(paint_rect, style_.Direction())
: paint_rect;
GraphicsContextStateSaver state_saver(paint_info.context);
paint_info.context.Clip(PixelSnappedIntRect(paint_rect));
box_painter.PaintFillLayer(paint_info, c, fill_layer, rect,
kBackgroundBleedNone, geometry, multi_line,
paint_rect.size);
}
void InlineBoxPainterBase::PaintMask(BoxPainterBase& box_painter,
const PaintInfo& paint_info,
const PhysicalRect& paint_rect,
BackgroundImageGeometry& geometry,
bool object_has_multiple_boxes,
PhysicalBoxSides sides_to_include) {
// Figure out if we need to push a transparency layer to render our mask.
PaintFillLayers(box_painter, paint_info, Color::kTransparent,
style_.MaskLayers(), paint_rect, geometry,
object_has_multiple_boxes);
const auto& mask_nine_piece_image = style_.MaskBoxImage();
const auto* mask_box_image = mask_nine_piece_image.GetImage();
bool has_box_image = mask_box_image && mask_box_image->CanRender();
if (!has_box_image || !mask_box_image->IsLoaded()) {
// Don't paint anything while we wait for the image to load.
return;
}
// The simple case is where we are the only box for this object. In those
// cases only a single call to draw is required.
PhysicalRect mask_image_paint_rect = paint_rect;
GraphicsContextStateSaver state_saver(paint_info.context, false);
if (object_has_multiple_boxes) {
// We have a mask image that spans multiple lines.
state_saver.Save();
// FIXME: What the heck do we do with RTL here? The math we're using is
// obviously not right, but it isn't even clear how this should work at all.
mask_image_paint_rect =
PaintRectForImageStrip(paint_rect, TextDirection::kLtr);
FloatRect clip_rect(ClipRectForNinePieceImageStrip(
style_, sides_to_include, mask_nine_piece_image, paint_rect));
// TODO(chrishtr): this should be pixel-snapped.
paint_info.context.Clip(clip_rect);
}
NinePieceImagePainter::Paint(paint_info.context, image_observer_, *document_,
node_, mask_image_paint_rect, style_,
mask_nine_piece_image);
}
} // namespace blink