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#if ENABLE(SVG) 23#include "RenderSVGResourceMasker.h" 24 25#include "AffineTransform.h" 26#include "Element.h" 27#include "FloatPoint.h" 28#include "FloatRect.h" 29#include "GraphicsContext.h" 30#include "Image.h" 31#include "ImageBuffer.h" 32#include "IntRect.h" 33#include "RenderSVGResource.h" 34#include "SVGElement.h" 35#include "SVGMaskElement.h" 36#include "SVGRenderingContext.h" 37#include "SVGStyledElement.h" 38#include "SVGUnitTypes.h" 39#include <wtf/Vector.h> 40 41namespace WebCore { 42 43RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType; 44 45RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node) 46 : RenderSVGResourceContainer(node) 47{ 48} 49 50RenderSVGResourceMasker::~RenderSVGResourceMasker() 51{ 52 if (m_masker.isEmpty()) 53 return; 54 55 deleteAllValues(m_masker); 56 m_masker.clear(); 57} 58 59void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation) 60{ 61 m_maskContentBoundaries = FloatRect(); 62 if (!m_masker.isEmpty()) { 63 deleteAllValues(m_masker); 64 m_masker.clear(); 65 } 66 67 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation); 68} 69 70void RenderSVGResourceMasker::removeClientFromCache(RenderObject* client, bool markForInvalidation) 71{ 72 ASSERT(client); 73 74 if (m_masker.contains(client)) 75 delete m_masker.take(client); 76 77 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation); 78} 79 80bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 81{ 82 ASSERT(object); 83 ASSERT(context); 84 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 85 86 bool missingMaskerData = !m_masker.contains(object); 87 if (missingMaskerData) 88 m_masker.set(object, new MaskerData); 89 90 MaskerData* maskerData = m_masker.get(object); 91 92 AffineTransform absoluteTransform; 93 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform); 94 95 FloatRect repaintRect = object->repaintRectInLocalCoordinates(); 96 97 if (!maskerData->maskImage && !repaintRect.isEmpty()) { 98 SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node()); 99 if (!maskElement) 100 return false; 101 102 ASSERT(style()); 103 const SVGRenderStyle* svgStyle = style()->svgStyle(); 104 ASSERT(svgStyle); 105 ColorSpace colorSpace = svgStyle->colorInterpolation() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB; 106 if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, maskerData->maskImage, colorSpace, Unaccelerated)) 107 return false; 108 109 if (!drawContentIntoMaskImage(maskerData, colorSpace, maskElement, object)) { 110 maskerData->maskImage.clear(); 111 } 112 } 113 114 if (!maskerData->maskImage) 115 return false; 116 117 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRect, maskerData->maskImage, missingMaskerData); 118 return true; 119} 120 121bool RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, ColorSpace colorSpace, const SVGMaskElement* maskElement, RenderObject* object) 122{ 123 GraphicsContext* maskImageContext = maskerData->maskImage->context(); 124 ASSERT(maskImageContext); 125 126 // Eventually adjust the mask image context according to the target objectBoundingBox. 127 AffineTransform maskContentTransformation; 128 if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 129 FloatRect objectBoundingBox = object->objectBoundingBox(); 130 maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y()); 131 maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); 132 maskImageContext->concatCTM(maskContentTransformation); 133 } 134 135 // Draw the content into the ImageBuffer. 136 for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) { 137 RenderObject* renderer = node->renderer(); 138 if (!node->isSVGElement() || !toSVGElement(node)->isSVGStyledElement() || !renderer) 139 continue; 140 if (renderer->needsLayout()) 141 return false; 142 RenderStyle* style = renderer->style(); 143 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 144 continue; 145 SVGRenderingContext::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation); 146 } 147 148#if !USE(CG) 149 maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, colorSpace); 150#else 151 UNUSED_PARAM(colorSpace); 152#endif 153 154 ASSERT(style()); 155 ASSERT(style()->svgStyle()); 156 // Create the luminance mask. 157 if (style()->svgStyle()->maskType() == MT_LUMINANCE) 158 maskerData->maskImage->convertToLuminanceMask(); 159 160 return true; 161} 162 163void RenderSVGResourceMasker::calculateMaskContentRepaintRect() 164{ 165 for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { 166 RenderObject* renderer = childNode->renderer(); 167 if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGStyledElement() || !renderer) 168 continue; 169 RenderStyle* style = renderer->style(); 170 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 171 continue; 172 m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates())); 173 } 174} 175 176FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object) 177{ 178 SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node()); 179 ASSERT(maskElement); 180 181 FloatRect objectBoundingBox = object->objectBoundingBox(); 182 FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(maskElement, maskElement->maskUnits(), objectBoundingBox); 183 184 // Resource was not layouted yet. Give back clipping rect of the mask. 185 if (selfNeedsLayout()) 186 return maskBoundaries; 187 188 if (m_maskContentBoundaries.isEmpty()) 189 calculateMaskContentRepaintRect(); 190 191 FloatRect maskRect = m_maskContentBoundaries; 192 if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 193 AffineTransform transform; 194 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 195 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); 196 maskRect = transform.mapRect(maskRect); 197 } 198 199 maskRect.intersect(maskBoundaries); 200 return maskRect; 201} 202 203} 204 205#endif // ENABLE(SVG) 206