OLD | NEW |
| (Empty) |
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 "core/rendering/svg/RenderSVGResourceMasker.h" | |
22 | |
23 #include "core/dom/ElementTraversal.h" | |
24 #include "core/layout/svg/SVGLayoutSupport.h" | |
25 #include "core/paint/SVGPaintContext.h" | |
26 #include "core/svg/SVGElement.h" | |
27 #include "platform/graphics/GraphicsContextStateSaver.h" | |
28 #include "platform/transforms/AffineTransform.h" | |
29 #include "third_party/skia/include/core/SkPicture.h" | |
30 | |
31 namespace blink { | |
32 | |
33 RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node) | |
34 : RenderSVGResourceContainer(node) | |
35 { | |
36 } | |
37 | |
38 RenderSVGResourceMasker::~RenderSVGResourceMasker() | |
39 { | |
40 } | |
41 | |
42 void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation
) | |
43 { | |
44 m_maskContentPicture.clear(); | |
45 m_maskContentBoundaries = FloatRect(); | |
46 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval
idation : ParentOnlyInvalidation); | |
47 } | |
48 | |
49 void RenderSVGResourceMasker::removeClientFromCache(LayoutObject* client, bool m
arkForInvalidation) | |
50 { | |
51 ASSERT(client); | |
52 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati
on : ParentOnlyInvalidation); | |
53 } | |
54 | |
55 bool RenderSVGResourceMasker::prepareEffect(LayoutObject* object, GraphicsContex
t* context) | |
56 { | |
57 ASSERT(object); | |
58 ASSERT(context); | |
59 ASSERT(style()); | |
60 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); | |
61 | |
62 clearInvalidationMask(); | |
63 | |
64 FloatRect paintInvalidationRect = object->paintInvalidationRectInLocalCoordi
nates(); | |
65 if (paintInvalidationRect.isEmpty() || !element()->hasChildren()) | |
66 return false; | |
67 | |
68 // Content layer start. | |
69 context->beginTransparencyLayer(1, &paintInvalidationRect); | |
70 | |
71 return true; | |
72 } | |
73 | |
74 void RenderSVGResourceMasker::finishEffect(LayoutObject* object, GraphicsContext
* context) | |
75 { | |
76 ASSERT(object); | |
77 ASSERT(context); | |
78 ASSERT(style()); | |
79 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); | |
80 | |
81 FloatRect paintInvalidationRect = object->paintInvalidationRectInLocalCoordi
nates(); | |
82 | |
83 const SVGLayoutStyle& svgStyle = style()->svgStyle(); | |
84 ColorFilter maskLayerFilter = svgStyle.maskType() == MT_LUMINANCE | |
85 ? ColorFilterLuminanceToAlpha : ColorFilterNone; | |
86 ColorFilter maskContentFilter = svgStyle.colorInterpolation() == CI_LINEARRG
B | |
87 ? ColorFilterSRGBToLinearRGB : ColorFilterNone; | |
88 | |
89 // Mask layer start. | |
90 context->beginLayer(1, SkXfermode::kDstIn_Mode, &paintInvalidationRect, mask
LayerFilter); | |
91 { | |
92 // Draw the mask with color conversion (when needed). | |
93 GraphicsContextStateSaver maskContentSaver(*context); | |
94 context->setColorFilter(maskContentFilter); | |
95 | |
96 drawMaskForRenderer(context, object->objectBoundingBox()); | |
97 } | |
98 | |
99 // Transfer mask layer -> content layer (DstIn) | |
100 context->endLayer(); | |
101 // Transfer content layer -> backdrop (SrcOver) | |
102 context->endLayer(); | |
103 } | |
104 | |
105 void RenderSVGResourceMasker::drawMaskForRenderer(GraphicsContext* context, cons
t FloatRect& targetBoundingBox) | |
106 { | |
107 ASSERT(context); | |
108 | |
109 AffineTransform contentTransformation; | |
110 SVGUnitTypes::SVGUnitType contentUnits = toSVGMaskElement(element())->maskCo
ntentUnits()->currentValue()->enumValue(); | |
111 if (contentUnits == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { | |
112 contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox
.y()); | |
113 contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetB
oundingBox.height()); | |
114 context->concatCTM(contentTransformation); | |
115 } | |
116 | |
117 if (!m_maskContentPicture) { | |
118 SubtreeContentTransformScope contentTransformScope(contentTransformation
); | |
119 createPicture(context); | |
120 } | |
121 | |
122 context->drawPicture(m_maskContentPicture.get()); | |
123 } | |
124 | |
125 void RenderSVGResourceMasker::createPicture(GraphicsContext* context) | |
126 { | |
127 ASSERT(context); | |
128 | |
129 // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinat
es) to avoid the intersection | |
130 // with local clips/mask, which may yield incorrect results when mixing obje
ctBoundingBox and | |
131 // userSpaceOnUse units (http://crbug.com/294900). | |
132 FloatRect bounds = strokeBoundingBox(); | |
133 context->beginRecording(bounds); | |
134 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()
); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement
)) { | |
135 LayoutObject* renderer = childElement->renderer(); | |
136 if (!renderer) | |
137 continue; | |
138 const LayoutStyle* style = renderer->style(); | |
139 if (!style || style->display() == NONE || style->visibility() != VISIBLE
) | |
140 continue; | |
141 | |
142 SVGPaintContext::paintSubtree(context, renderer); | |
143 } | |
144 m_maskContentPicture = context->endRecording(); | |
145 } | |
146 | |
147 void RenderSVGResourceMasker::calculateMaskContentPaintInvalidationRect() | |
148 { | |
149 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()
); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement
)) { | |
150 LayoutObject* renderer = childElement->renderer(); | |
151 if (!renderer) | |
152 continue; | |
153 const LayoutStyle* style = renderer->style(); | |
154 if (!style || style->display() == NONE || style->visibility() != VISIBLE
) | |
155 continue; | |
156 m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect
(renderer->paintInvalidationRectInLocalCoordinates())); | |
157 } | |
158 } | |
159 | |
160 FloatRect RenderSVGResourceMasker::resourceBoundingBox(const LayoutObject* objec
t) | |
161 { | |
162 SVGMaskElement* maskElement = toSVGMaskElement(element()); | |
163 ASSERT(maskElement); | |
164 | |
165 FloatRect objectBoundingBox = object->objectBoundingBox(); | |
166 FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement
>(maskElement, maskElement->maskUnits()->currentValue()->enumValue(), objectBoun
dingBox); | |
167 | |
168 // Resource was not layouted yet. Give back clipping rect of the mask. | |
169 if (selfNeedsLayout()) | |
170 return maskBoundaries; | |
171 | |
172 if (m_maskContentBoundaries.isEmpty()) | |
173 calculateMaskContentPaintInvalidationRect(); | |
174 | |
175 FloatRect maskRect = m_maskContentBoundaries; | |
176 if (maskElement->maskContentUnits()->currentValue()->value() == SVGUnitTypes
::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { | |
177 AffineTransform transform; | |
178 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); | |
179 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h
eight()); | |
180 maskRect = transform.mapRect(maskRect); | |
181 } | |
182 | |
183 maskRect.intersect(maskBoundaries); | |
184 return maskRect; | |
185 } | |
186 | |
187 } | |
OLD | NEW |