blob: d6bb5f606138f7d9c5b566841081ca6f9482b7bb [file] [log] [blame]
/*
* Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
* Copyright (C) 2006 Apple Computer, Inc.
* Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2007, 2008, 2009 Rob Buis <buis@kde.org>
* Copyright (C) 2009 Google, Inc.
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
#include "third_party/blink/renderer/core/html/media/media_element_parser_helpers.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/layout_image_resource.h"
#include "third_party/blink/renderer/core/layout/layout_replaced.h"
#include "third_party/blink/renderer/core/layout/pointer_events_hit_rules.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include "third_party/blink/renderer/core/layout/svg/transform_helper.h"
#include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h"
#include "third_party/blink/renderer/core/paint/image_element_timing.h"
#include "third_party/blink/renderer/core/paint/svg_image_painter.h"
#include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
#include "third_party/blink/renderer/core/svg/svg_image_element.h"
#include "third_party/blink/renderer/core/svg/svg_length_context.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
namespace blink {
LayoutSVGImage::LayoutSVGImage(SVGImageElement* impl)
: LayoutSVGModelObject(impl),
needs_transform_update_(true),
transform_uses_reference_box_(false),
image_resource_(MakeGarbageCollected<LayoutImageResource>()) {
image_resource_->Initialize(this);
}
LayoutSVGImage::~LayoutSVGImage() = default;
void LayoutSVGImage::StyleDidChange(StyleDifference diff,
const ComputedStyle* old_style) {
NOT_DESTROYED();
transform_uses_reference_box_ =
TransformHelper::DependsOnReferenceBox(StyleRef());
LayoutSVGModelObject::StyleDidChange(diff, old_style);
}
void LayoutSVGImage::WillBeDestroyed() {
NOT_DESTROYED();
image_resource_->Shutdown();
LayoutSVGModelObject::WillBeDestroyed();
}
static float ResolveWidthForRatio(float height,
const FloatSize& intrinsic_ratio) {
return height * intrinsic_ratio.Width() / intrinsic_ratio.Height();
}
static float ResolveHeightForRatio(float width,
const FloatSize& intrinsic_ratio) {
return width * intrinsic_ratio.Height() / intrinsic_ratio.Width();
}
bool LayoutSVGImage::HasOverriddenIntrinsicSize() const {
NOT_DESTROYED();
if (!RuntimeEnabledFeatures::ExperimentalPoliciesEnabled())
return false;
auto* svg_image_element = DynamicTo<SVGImageElement>(GetElement());
return svg_image_element && svg_image_element->IsDefaultIntrinsicSize();
}
FloatSize LayoutSVGImage::CalculateObjectSize() const {
NOT_DESTROYED();
FloatSize intrinsic_size;
ImageResourceContent* cached_image = image_resource_->CachedImage();
bool has_intrinsic_ratio = true;
if (HasOverriddenIntrinsicSize()) {
intrinsic_size = FloatSize(LayoutReplaced::kDefaultWidth,
LayoutReplaced::kDefaultHeight);
} else {
if (!cached_image || cached_image->ErrorOccurred() ||
!cached_image->IsSizeAvailable())
return object_bounding_box_.Size();
RespectImageOrientationEnum respect_orientation =
LayoutObject::ShouldRespectImageOrientation(this);
intrinsic_size = cached_image->GetImage()->SizeAsFloat(respect_orientation);
if (auto* svg_image = DynamicTo<SVGImage>(cached_image->GetImage())) {
IntrinsicSizingInfo intrinsic_sizing_info;
has_intrinsic_ratio &= svg_image->GetIntrinsicSizingInfo(intrinsic_sizing_info);
has_intrinsic_ratio &= !intrinsic_sizing_info.aspect_ratio.IsEmpty();
}
}
if (StyleRef().Width().IsAuto() && StyleRef().Height().IsAuto())
return intrinsic_size;
if (StyleRef().Height().IsAuto()) {
if (has_intrinsic_ratio) {
return FloatSize(
object_bounding_box_.Width(),
ResolveHeightForRatio(object_bounding_box_.Width(), intrinsic_size));
}
return FloatSize(object_bounding_box_.Width(), intrinsic_size.Height());
}
DCHECK(StyleRef().Width().IsAuto());
if (has_intrinsic_ratio) {
return FloatSize(
ResolveWidthForRatio(object_bounding_box_.Height(), intrinsic_size),
object_bounding_box_.Height());
}
return FloatSize(intrinsic_size.Width(), object_bounding_box_.Height());
}
bool LayoutSVGImage::UpdateBoundingBox() {
NOT_DESTROYED();
FloatRect old_object_bounding_box = object_bounding_box_;
SVGLengthContext length_context(GetElement());
const ComputedStyle& style = StyleRef();
object_bounding_box_ =
FloatRect(length_context.ResolveLengthPair(style.X(), style.Y(), style),
ToFloatSize(length_context.ResolveLengthPair(
style.Width(), style.Height(), style)));
if (style.Width().IsAuto() || style.Height().IsAuto())
object_bounding_box_.SetSize(CalculateObjectSize());
return old_object_bounding_box != object_bounding_box_;
}
void LayoutSVGImage::UpdateLayout() {
NOT_DESTROYED();
DCHECK(NeedsLayout());
LayoutAnalyzer::Scope analyzer(*this);
const bool bbox_changed = UpdateBoundingBox();
if (bbox_changed) {
SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kImage);
// Invalidate all resources of this client if our reference box changed.
if (EverHadLayout())
SVGResourceInvalidator(*this).InvalidateEffects();
}
if (!needs_transform_update_ && transform_uses_reference_box_) {
needs_transform_update_ = CheckForImplicitTransformChange(bbox_changed);
if (needs_transform_update_)
SetNeedsPaintPropertyUpdate();
}
bool update_parent_boundaries = bbox_changed;
if (needs_transform_update_) {
local_transform_ = CalculateLocalTransform();
needs_transform_update_ = false;
update_parent_boundaries = true;
}
// If our bounds changed, notify the parents.
if (update_parent_boundaries)
LayoutSVGModelObject::SetNeedsBoundariesUpdate();
DCHECK(!needs_transform_update_);
if (auto* svg_image_element = DynamicTo<SVGImageElement>(GetElement())) {
media_element_parser_helpers::CheckUnsizedMediaViolation(
this, svg_image_element->IsDefaultIntrinsicSize());
}
ClearNeedsLayout();
}
void LayoutSVGImage::Paint(const PaintInfo& paint_info) const {
NOT_DESTROYED();
SVGImagePainter(*this).Paint(paint_info);
}
bool LayoutSVGImage::NodeAtPoint(HitTestResult& result,
const HitTestLocation& hit_test_location,
const PhysicalOffset& accumulated_offset,
HitTestAction hit_test_action) {
NOT_DESTROYED();
DCHECK_EQ(accumulated_offset, PhysicalOffset());
// We only draw in the forground phase, so we only hit-test then.
if (hit_test_action != kHitTestForeground)
return false;
const ComputedStyle& style = StyleRef();
PointerEventsHitRules hit_rules(PointerEventsHitRules::SVG_IMAGE_HITTESTING,
result.GetHitTestRequest(),
style.PointerEvents());
if (hit_rules.require_visible && style.Visibility() != EVisibility::kVisible)
return false;
TransformedHitTestLocation local_location(hit_test_location,
LocalToSVGParentTransform());
if (!local_location)
return false;
if (!SVGLayoutSupport::IntersectsClipPath(*this, object_bounding_box_,
*local_location))
return false;
if (hit_rules.can_hit_fill || hit_rules.can_hit_bounding_box) {
if (local_location->Intersects(object_bounding_box_)) {
UpdateHitTestResult(result, PhysicalOffset::FromFloatPointRound(
local_location->TransformedPoint()));
if (result.AddNodeToListBasedTestResult(GetElement(), *local_location) ==
kStopHitTesting)
return true;
}
}
return false;
}
void LayoutSVGImage::ImageChanged(WrappedImagePtr, CanDeferInvalidation defer) {
NOT_DESTROYED();
// Notify parent resources that we've changed. This also invalidates
// references from resources (filters) that may have a cached
// representation of this image/layout object.
LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(*this,
false);
if (StyleRef().Width().IsAuto() || StyleRef().Height().IsAuto()) {
if (CalculateObjectSize() != object_bounding_box_.Size())
SetNeedsLayout(layout_invalidation_reason::kSizeChanged);
}
SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kImage);
}
} // namespace blink