Applying a filter on an SVG element, which is larger than 4096 pixels, causes this...
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGResourceMasker.cpp
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 "config.h"
21 #include "RenderSVGResourceMasker.h"
22
23 #include "Element.h"
24 #include "ElementIterator.h"
25 #include "FloatPoint.h"
26 #include "Image.h"
27 #include "IntRect.h"
28 #include "RenderBoxModelObject.h"
29 #include "SVGRenderingContext.h"
30
31 namespace WebCore {
32
33 RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement& element, Ref<RenderStyle>&& style)
34     : RenderSVGResourceContainer(element, WTF::move(style))
35 {
36 }
37
38 RenderSVGResourceMasker::~RenderSVGResourceMasker()
39 {
40 }
41
42 void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation)
43 {
44     m_maskContentBoundaries = FloatRect();
45     m_masker.clear();
46
47     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
48 }
49
50 void RenderSVGResourceMasker::removeClientFromCache(RenderElement& client, bool markForInvalidation)
51 {
52     m_masker.remove(&client);
53
54     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
55 }
56
57 bool RenderSVGResourceMasker::applySVGMask(RenderElement& renderer, GraphicsContext*& context, bool applyClip)
58 {
59     ASSERT(context);
60
61     bool missingMaskerData = !m_masker.contains(&renderer);
62     if (missingMaskerData)
63         m_masker.set(&renderer, std::make_unique<MaskerData>());
64
65     MaskerData* maskerData = m_masker.get(&renderer);
66     AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer);
67     FloatRect repaintRect = renderer.repaintRectInLocalCoordinates();
68
69     if (!maskerData->maskImage && !repaintRect.isEmpty()) {
70         const SVGRenderStyle& svgStyle = style().svgStyle();
71         ColorSpace colorSpace = svgStyle.colorInterpolation() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB;
72         maskerData->maskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, colorSpace, Unaccelerated);
73         if (!maskerData->maskImage)
74             return false;
75
76         if (!drawContentIntoMaskImage(maskerData, colorSpace, &renderer))
77             maskerData->maskImage.reset();
78     }
79
80     if (!maskerData->maskImage)
81         return false;
82
83     if (applyClip)
84         SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRect, maskerData->maskImage, missingMaskerData);
85
86     return true;
87 }
88
89 bool RenderSVGResourceMasker::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode)
90 {
91     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
92     return applySVGMask(renderer, context, true);
93 }
94
95 bool RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, ColorSpace colorSpace, RenderObject* object)
96 {
97     GraphicsContext* maskImageContext = maskerData->maskImage->context();
98     ASSERT(maskImageContext);
99
100     // Eventually adjust the mask image context according to the target objectBoundingBox.
101     AffineTransform maskContentTransformation;
102     if (maskElement().maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
103         FloatRect objectBoundingBox = object->objectBoundingBox();
104         maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
105         maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
106         maskImageContext->concatCTM(maskContentTransformation);
107     }
108
109     // Draw the content into the ImageBuffer.
110     for (auto& child : childrenOfType<SVGElement>(maskElement())) {
111         auto renderer = child.renderer();
112         if (!renderer)
113             continue;
114         if (renderer->needsLayout())
115             return false;
116         const RenderStyle& style = renderer->style();
117         if (style.display() == NONE || style.visibility() != VISIBLE)
118             continue;
119         SVGRenderingContext::renderSubtreeToImageBuffer(maskerData->maskImage.get(), *renderer, maskContentTransformation);
120     }
121
122 #if !USE(CG)
123     maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, colorSpace);
124 #else
125     UNUSED_PARAM(colorSpace);
126 #endif
127
128     // Create the luminance mask.
129     if (style().svgStyle().maskType() == MT_LUMINANCE)
130         maskerData->maskImage->convertToLuminanceMask();
131
132     return true;
133 }
134
135 void RenderSVGResourceMasker::drawMaskForRenderer(RenderElement& renderer, const BackgroundImageGeometry& geometry, GraphicsContext* context, CompositeOperator compositeOp)
136 {
137     if (context->paintingDisabled())
138         return;
139
140     if (!applySVGMask(renderer, context, false))
141         return;
142
143     MaskerData* maskerData = maskerDataForRenderer(renderer);
144     ASSERT(maskerData);
145
146     FloatRect oneTileRect;
147     FloatSize actualTileSize(geometry.tileSize().width() + geometry.spaceSize().width(), geometry.tileSize().height() + geometry.spaceSize().height());
148     oneTileRect.setX(geometry.destRect().x() + fmodf(fmodf(-geometry.phase().width(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width()));
149     oneTileRect.setY(geometry.destRect().y() + fmodf(fmodf(-geometry.phase().height(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height()));
150     oneTileRect.setSize(geometry.tileSize());
151     
152     FloatSize intrinsicTileSize = maskerData->maskImage->logicalSize();
153     FloatSize scale(geometry.tileSize().width() / intrinsicTileSize.width(), geometry.tileSize().height() / intrinsicTileSize.height());
154     
155     FloatRect visibleSrcRect;
156     visibleSrcRect.setX((geometry.destRect().x() - oneTileRect.x()) / scale.width());
157     visibleSrcRect.setY((geometry.destRect().y() - oneTileRect.y()) / scale.height());
158     visibleSrcRect.setWidth(geometry.destRect().width() / scale.width());
159     visibleSrcRect.setHeight(geometry.destRect().height() / scale.height());
160     context->drawImageBuffer(maskerData->maskImage.get(), ColorSpaceDeviceRGB, geometry.destRect(), visibleSrcRect, compositeOp);
161 }
162
163 void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
164 {
165     for (Node* childNode = maskElement().firstChild(); childNode; childNode = childNode->nextSibling()) {
166         RenderObject* renderer = childNode->renderer();
167         if (!childNode->isSVGElement() || !renderer)
168             continue;
169         const RenderStyle& style = renderer->style();
170         if (style.display() == NONE || style.visibility() != VISIBLE)
171              continue;
172         m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
173     }
174 }
175
176 FloatRect RenderSVGResourceMasker::resourceBoundingBox(const RenderObject& object)
177 {
178     FloatRect objectBoundingBox = object.objectBoundingBox();
179     FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(&maskElement(), maskElement().maskUnits(), objectBoundingBox);
180
181     // Resource was not layouted yet. Give back clipping rect of the mask.
182     if (selfNeedsLayout())
183         return maskBoundaries;
184
185     if (m_maskContentBoundaries.isEmpty())
186         calculateMaskContentRepaintRect();
187
188     FloatRect maskRect = m_maskContentBoundaries;
189     if (maskElement().maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
190         AffineTransform transform;
191         transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
192         transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
193         maskRect = transform.mapRect(maskRect);
194     }
195
196     maskRect.intersect(maskBoundaries);
197     return maskRect;
198 }
199
200 }