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/dom/ElementTraversal.h" 25#include "core/rendering/svg/RenderSVGResource.h" 26#include "core/rendering/svg/SVGRenderingContext.h" 27#include "core/svg/SVGElement.h" 28#include "platform/graphics/DisplayList.h" 29#include "platform/graphics/GraphicsContextStateSaver.h" 30#include "platform/transforms/AffineTransform.h" 31 32namespace blink { 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 paintInvalidationRect = object->paintInvalidationRectInLocalCoordinates(); 70 if (paintInvalidationRect.isEmpty() || !element()->hasChildren()) 71 return false; 72 73 // Content layer start. 74 context->beginTransparencyLayer(1, &paintInvalidationRect); 75 76 return true; 77} 78 79void RenderSVGResourceMasker::postApplyResource(RenderObject* object, GraphicsContext*& context) 80{ 81 ASSERT(object); 82 ASSERT(context); 83 ASSERT(style()); 84 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); 85 86 FloatRect paintInvalidationRect = object->paintInvalidationRectInLocalCoordinates(); 87 88 const SVGRenderStyle& svgStyle = style()->svgStyle(); 89 ColorFilter maskLayerFilter = svgStyle.maskType() == MT_LUMINANCE 90 ? ColorFilterLuminanceToAlpha : ColorFilterNone; 91 ColorFilter maskContentFilter = svgStyle.colorInterpolation() == CI_LINEARRGB 92 ? ColorFilterSRGBToLinearRGB : ColorFilterNone; 93 94 // Mask layer start. 95 context->beginLayer(1, CompositeDestinationIn, &paintInvalidationRect, maskLayerFilter); 96 { 97 // Draw the mask with color conversion (when needed). 98 GraphicsContextStateSaver maskContentSaver(*context); 99 context->setColorFilter(maskContentFilter); 100 101 drawMaskForRenderer(context, object->objectBoundingBox()); 102 } 103 104 // Transfer mask layer -> content layer (DstIn) 105 context->endLayer(); 106 // Transfer content layer -> backdrop (SrcOver) 107 context->endLayer(); 108} 109 110void RenderSVGResourceMasker::drawMaskForRenderer(GraphicsContext* context, const FloatRect& targetBoundingBox) 111{ 112 ASSERT(context); 113 114 AffineTransform contentTransformation; 115 SVGUnitTypes::SVGUnitType contentUnits = toSVGMaskElement(element())->maskContentUnits()->currentValue()->enumValue(); 116 if (contentUnits == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 117 contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y()); 118 contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); 119 context->concatCTM(contentTransformation); 120 } 121 122 if (!m_maskContentDisplayList) 123 createDisplayList(context, contentTransformation); 124 ASSERT(m_maskContentDisplayList); 125 context->drawDisplayList(m_maskContentDisplayList.get()); 126} 127 128void RenderSVGResourceMasker::createDisplayList(GraphicsContext* context, 129 const AffineTransform& contentTransform) 130{ 131 ASSERT(context); 132 133 // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection 134 // with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and 135 // userSpaceOnUse units (http://crbug.com/294900). 136 FloatRect bounds = strokeBoundingBox(); 137 context->beginRecording(bounds); 138 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { 139 RenderObject* renderer = childElement->renderer(); 140 if (!renderer) 141 continue; 142 RenderStyle* style = renderer->style(); 143 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 144 continue; 145 146 SVGRenderingContext::renderSubtree(context, renderer, contentTransform); 147 } 148 m_maskContentDisplayList = context->endRecording(); 149} 150 151void RenderSVGResourceMasker::calculateMaskContentPaintInvalidationRect() 152{ 153 for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { 154 RenderObject* renderer = childElement->renderer(); 155 if (!renderer) 156 continue; 157 RenderStyle* style = renderer->style(); 158 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 159 continue; 160 m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->paintInvalidationRectInLocalCoordinates())); 161 } 162} 163 164FloatRect RenderSVGResourceMasker::resourceBoundingBox(const RenderObject* object) 165{ 166 SVGMaskElement* maskElement = toSVGMaskElement(element()); 167 ASSERT(maskElement); 168 169 FloatRect objectBoundingBox = object->objectBoundingBox(); 170 FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(maskElement, maskElement->maskUnits()->currentValue()->enumValue(), objectBoundingBox); 171 172 // Resource was not layouted yet. Give back clipping rect of the mask. 173 if (selfNeedsLayout()) 174 return maskBoundaries; 175 176 if (m_maskContentBoundaries.isEmpty()) 177 calculateMaskContentPaintInvalidationRect(); 178 179 FloatRect maskRect = m_maskContentBoundaries; 180 if (maskElement->maskContentUnits()->currentValue()->value() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 181 AffineTransform transform; 182 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 183 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); 184 maskRect = transform.mapRect(maskRect); 185 } 186 187 maskRect.intersect(maskBoundaries); 188 return maskRect; 189} 190 191} 192