1 /*
2  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
21 
22 #include "third_party/blink/renderer/core/dom/element_traversal.h"
23 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
24 #include "third_party/blink/renderer/core/paint/svg_object_painter.h"
25 #include "third_party/blink/renderer/core/svg/svg_element.h"
26 #include "third_party/blink/renderer/core/svg/svg_mask_element.h"
27 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
28 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
29 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
30 #include "third_party/blink/renderer/platform/transforms/affine_transform.h"
31 
32 namespace blink {
33 
LayoutSVGResourceMasker(SVGMaskElement * node)34 LayoutSVGResourceMasker::LayoutSVGResourceMasker(SVGMaskElement* node)
35     : LayoutSVGResourceContainer(node) {}
36 
37 LayoutSVGResourceMasker::~LayoutSVGResourceMasker() = default;
38 
RemoveAllClientsFromCache()39 void LayoutSVGResourceMasker::RemoveAllClientsFromCache() {
40   cached_paint_record_.reset();
41   mask_content_boundaries_ = FloatRect();
42   MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation |
43                                 SVGResourceClient::kBoundariesInvalidation);
44 }
45 
CreatePaintRecord(AffineTransform & content_transformation,const FloatRect & target_bounding_box,GraphicsContext & context)46 sk_sp<const PaintRecord> LayoutSVGResourceMasker::CreatePaintRecord(
47     AffineTransform& content_transformation,
48     const FloatRect& target_bounding_box,
49     GraphicsContext& context) {
50   if (MaskContentUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) {
51     content_transformation.Translate(target_bounding_box.X(),
52                                      target_bounding_box.Y());
53     content_transformation.ScaleNonUniform(target_bounding_box.Width(),
54                                            target_bounding_box.Height());
55   }
56 
57   if (cached_paint_record_)
58     return cached_paint_record_;
59 
60   SubtreeContentTransformScope content_transform_scope(content_transformation);
61   PaintRecordBuilder builder(nullptr, &context);
62 
63   ColorFilter mask_content_filter =
64       StyleRef().SvgStyle().ColorInterpolation() == CI_LINEARRGB
65           ? kColorFilterSRGBToLinearRGB
66           : kColorFilterNone;
67   builder.Context().SetColorFilter(mask_content_filter);
68 
69   for (const SVGElement& child_element :
70        Traversal<SVGElement>::ChildrenOf(*GetElement())) {
71     const LayoutObject* layout_object = child_element.GetLayoutObject();
72     if (!layout_object ||
73         layout_object->StyleRef().Display() == EDisplay::kNone)
74       continue;
75     SVGObjectPainter(*layout_object).PaintResourceSubtree(builder.Context());
76   }
77 
78   cached_paint_record_ = builder.EndRecording();
79   return cached_paint_record_;
80 }
81 
CalculateMaskContentVisualRect()82 void LayoutSVGResourceMasker::CalculateMaskContentVisualRect() {
83   for (const SVGElement& child_element :
84        Traversal<SVGElement>::ChildrenOf(*GetElement())) {
85     const LayoutObject* layout_object = child_element.GetLayoutObject();
86     if (!layout_object ||
87         layout_object->StyleRef().Display() == EDisplay::kNone)
88       continue;
89     mask_content_boundaries_.Unite(
90         layout_object->LocalToSVGParentTransform().MapRect(
91             layout_object->VisualRectInLocalSVGCoordinates()));
92   }
93 }
94 
MaskUnits() const95 SVGUnitTypes::SVGUnitType LayoutSVGResourceMasker::MaskUnits() const {
96   return To<SVGMaskElement>(GetElement())
97       ->maskUnits()
98       ->CurrentValue()
99       ->EnumValue();
100 }
101 
MaskContentUnits() const102 SVGUnitTypes::SVGUnitType LayoutSVGResourceMasker::MaskContentUnits() const {
103   return To<SVGMaskElement>(GetElement())
104       ->maskContentUnits()
105       ->CurrentValue()
106       ->EnumValue();
107 }
108 
ResourceBoundingBox(const FloatRect & reference_box)109 FloatRect LayoutSVGResourceMasker::ResourceBoundingBox(
110     const FloatRect& reference_box) {
111   auto* mask_element = To<SVGMaskElement>(GetElement());
112   DCHECK(mask_element);
113 
114   FloatRect mask_boundaries = SVGLengthContext::ResolveRectangle(
115       mask_element, MaskUnits(), reference_box);
116 
117   // Resource was not layouted yet. Give back clipping rect of the mask.
118   if (SelfNeedsLayout())
119     return mask_boundaries;
120 
121   if (mask_content_boundaries_.IsEmpty())
122     CalculateMaskContentVisualRect();
123 
124   FloatRect mask_rect = mask_content_boundaries_;
125   if (MaskContentUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) {
126     AffineTransform transform;
127     transform.Translate(reference_box.X(), reference_box.Y());
128     transform.ScaleNonUniform(reference_box.Width(), reference_box.Height());
129     mask_rect = transform.MapRect(mask_rect);
130   }
131 
132   mask_rect.Intersect(mask_boundaries);
133   return mask_rect;
134 }
135 
136 }  // namespace blink
137