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 22#include "core/rendering/svg/RenderSVGResourceMasker.h" 23 24#include "core/rendering/svg/RenderSVGResource.h" 25#include "core/rendering/svg/SVGRenderingContext.h" 26#include "core/svg/SVGElement.h" 27#include "platform/graphics/DisplayList.h" 28#include "platform/graphics/GraphicsContextStateSaver.h" 29#include "platform/transforms/AffineTransform.h" 30#include "wtf/Vector.h" 31 32namespace WebCore { 33 34const RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType; 35 36RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node) 37 : RenderSVGResourceContainer(node) 38{ 39} 40 41RenderSVGResourceMasker::~RenderSVGResourceMasker() 42{ 43} 44 45void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation) 46{ 47 m_maskContentDisplayList.clear(); 48 m_maskContentBoundaries = FloatRect(); 49 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation); 50} 51 52void RenderSVGResourceMasker::removeClientFromCache(RenderObject* client, bool markForInvalidation) 53{ 54 ASSERT(client); 55 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation); 56} 57 58bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, 59 GraphicsContext*& context, unsigned short resourceMode) 60{ 61 ASSERT(object); 62 ASSERT(context); 63 ASSERT(style()); 64 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 65 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); 66 67 clearInvalidationMask(); 68 69 FloatRect repaintRect = object->paintInvalidationRectInLocalCoordinates(); 70 if (repaintRect.isEmpty() || !element()->hasChildren()) 71 return false; 72 73 // Content layer start. 74 context->beginTransparencyLayer(1, &repaintRect); 75 76 return true; 77} 78 79void RenderSVGResourceMasker::postApplyResource(RenderObject* object, GraphicsContext*& context, 80 unsigned short resourceMode, const Path*, const RenderSVGShape*) 81{ 82 ASSERT(object); 83 ASSERT(context); 84 ASSERT(style()); 85 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 86 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); 87 88 FloatRect repaintRect = object->paintInvalidationRectInLocalCoordinates(); 89 90 const SVGRenderStyle* svgStyle = style()->svgStyle(); 91 ASSERT(svgStyle); 92 ColorFilter maskLayerFilter = svgStyle->maskType() == MT_LUMINANCE 93 ? ColorFilterLuminanceToAlpha : ColorFilterNone; 94 ColorFilter maskContentFilter = svgStyle->colorInterpolation() == CI_LINEARRGB 95 ? ColorFilterSRGBToLinearRGB : ColorFilterNone; 96 97 // Mask layer start. 98 context->beginLayer(1, CompositeDestinationIn, &repaintRect, maskLayerFilter); 99 { 100 // Draw the mask with color conversion (when needed). 101 GraphicsContextStateSaver maskContentSaver(*context); 102 context->setColorFilter(maskContentFilter); 103 104 drawMaskForRenderer(context, object->objectBoundingBox()); 105 } 106 107 // Transfer mask layer -> content layer (DstIn) 108 context->endLayer(); 109 // Transfer content layer -> backdrop (SrcOver) 110 context->endLayer(); 111} 112 113void RenderSVGResourceMasker::drawMaskForRenderer(GraphicsContext* context, const FloatRect& targetBoundingBox) 114{ 115 ASSERT(context); 116 117 AffineTransform contentTransformation; 118 SVGUnitTypes::SVGUnitType contentUnits = toSVGMaskElement(element())->maskContentUnits()->currentValue()->enumValue(); 119 if (contentUnits == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 120 contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y()); 121 contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); 122 context->concatCTM(contentTransformation); 123 } 124 125 if (!m_maskContentDisplayList) 126 m_maskContentDisplayList = asDisplayList(context, contentTransformation); 127 ASSERT(m_maskContentDisplayList); 128 context->drawDisplayList(m_maskContentDisplayList.get()); 129} 130 131PassRefPtr<DisplayList> RenderSVGResourceMasker::asDisplayList(GraphicsContext* context, 132 const AffineTransform& contentTransform) 133{ 134 ASSERT(context); 135 136 // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection 137 // with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and 138 // userSpaceOnUse units (http://crbug.com/294900). 139 context->beginRecording(strokeBoundingBox()); 140 for (Element* childElement = ElementTraversal::firstWithin(*element()); childElement; childElement = ElementTraversal::nextSibling(*childElement)) { 141 RenderObject* renderer = childElement->renderer(); 142 if (!childElement->isSVGElement() || !renderer) 143 continue; 144 RenderStyle* style = renderer->style(); 145 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 146 continue; 147 148 SVGRenderingContext::renderSubtree(context, renderer, contentTransform); 149 } 150 151 return context->endRecording(); 152} 153 154void RenderSVGResourceMasker::calculateMaskContentRepaintRect() 155{ 156 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { 157 RenderObject* renderer = childElement->renderer(); 158 if (!renderer) 159 continue; 160 RenderStyle* style = renderer->style(); 161 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 162 continue; 163 m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->paintInvalidationRectInLocalCoordinates())); 164 } 165} 166 167FloatRect RenderSVGResourceMasker::resourceBoundingBox(const RenderObject* object) 168{ 169 SVGMaskElement* maskElement = toSVGMaskElement(element()); 170 ASSERT(maskElement); 171 172 FloatRect objectBoundingBox = object->objectBoundingBox(); 173 FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(maskElement, maskElement->maskUnits()->currentValue()->enumValue(), objectBoundingBox); 174 175 // Resource was not layouted yet. Give back clipping rect of the mask. 176 if (selfNeedsLayout()) 177 return maskBoundaries; 178 179 if (m_maskContentBoundaries.isEmpty()) 180 calculateMaskContentRepaintRect(); 181 182 FloatRect maskRect = m_maskContentBoundaries; 183 if (maskElement->maskContentUnits()->currentValue()->value() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 184 AffineTransform transform; 185 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 186 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); 187 maskRect = transform.mapRect(maskRect); 188 } 189 190 maskRect.intersect(maskBoundaries); 191 return maskRect; 192} 193 194} 195