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 "SVGImageBufferTools.h" 36#include "SVGMaskElement.h" 37#include "SVGStyledElement.h" 38#include "SVGUnitTypes.h" 39 40#include <wtf/ByteArray.h> 41#include <wtf/UnusedParam.h> 42#include <wtf/Vector.h> 43 44namespace WebCore { 45 46RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType; 47 48RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node) 49 : RenderSVGResourceContainer(node) 50{ 51} 52 53RenderSVGResourceMasker::~RenderSVGResourceMasker() 54{ 55 if (m_masker.isEmpty()) 56 return; 57 58 deleteAllValues(m_masker); 59 m_masker.clear(); 60} 61 62void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation) 63{ 64 m_maskContentBoundaries = FloatRect(); 65 if (!m_masker.isEmpty()) { 66 deleteAllValues(m_masker); 67 m_masker.clear(); 68 } 69 70 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation); 71} 72 73void RenderSVGResourceMasker::removeClientFromCache(RenderObject* client, bool markForInvalidation) 74{ 75 ASSERT(client); 76 77 if (m_masker.contains(client)) 78 delete m_masker.take(client); 79 80 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation); 81} 82 83bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 84{ 85 ASSERT(object); 86 ASSERT(context); 87#ifndef NDEBUG 88 ASSERT(resourceMode == ApplyToDefaultMode); 89#else 90 UNUSED_PARAM(resourceMode); 91#endif 92 93 if (!m_masker.contains(object)) 94 m_masker.set(object, new MaskerData); 95 96 MaskerData* maskerData = m_masker.get(object); 97 98 AffineTransform absoluteTransform; 99 SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); 100 101 FloatRect absoluteTargetRect = absoluteTransform.mapRect(object->repaintRectInLocalCoordinates()); 102 FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(object, absoluteTargetRect); 103 104 if (!maskerData->maskImage && !clampedAbsoluteTargetRect.isEmpty()) { 105 SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node()); 106 if (!maskElement) 107 return false; 108 109 if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskerData->maskImage, ColorSpaceLinearRGB)) 110 return false; 111 112 GraphicsContext* maskImageContext = maskerData->maskImage->context(); 113 ASSERT(maskImageContext); 114 115 // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms. 116 maskImageContext->save(); 117 maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y()); 118 maskImageContext->concatCTM(absoluteTransform); 119 120 drawContentIntoMaskImage(maskerData, maskElement, object); 121 } 122 123 if (!maskerData->maskImage) 124 return false; 125 126 SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, maskerData->maskImage); 127 return true; 128} 129 130void RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object) 131{ 132 GraphicsContext* maskImageContext = maskerData->maskImage->context(); 133 ASSERT(maskImageContext); 134 135 // Eventually adjust the mask image context according to the target objectBoundingBox. 136 AffineTransform maskContentTransformation; 137 if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 138 FloatRect objectBoundingBox = object->objectBoundingBox(); 139 maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y()); 140 maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); 141 maskImageContext->concatCTM(maskContentTransformation); 142 } 143 144 // Draw the content into the ImageBuffer. 145 for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) { 146 RenderObject* renderer = node->renderer(); 147 if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer) 148 continue; 149 RenderStyle* style = renderer->style(); 150 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 151 continue; 152 SVGImageBufferTools::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation); 153 } 154 155 maskImageContext->restore(); 156 157#if !USE(CG) 158 maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB); 159#endif 160 161 // Create the luminance mask. 162 IntRect maskImageRect(IntPoint(), maskerData->maskImage->size()); 163 RefPtr<ByteArray> srcPixelArray = maskerData->maskImage->getUnmultipliedImageData(maskImageRect); 164 165 unsigned pixelArrayLength = srcPixelArray->length(); 166 for (unsigned pixelOffset = 0; pixelOffset < pixelArrayLength; pixelOffset += 4) { 167 unsigned char a = srcPixelArray->get(pixelOffset + 3); 168 if (!a) 169 continue; 170 unsigned char r = srcPixelArray->get(pixelOffset); 171 unsigned char g = srcPixelArray->get(pixelOffset + 1); 172 unsigned char b = srcPixelArray->get(pixelOffset + 2); 173 174 double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0); 175 srcPixelArray->set(pixelOffset + 3, luma); 176 } 177 178 maskerData->maskImage->putUnmultipliedImageData(srcPixelArray.get(), maskImageRect.size(), maskImageRect, IntPoint()); 179} 180 181void RenderSVGResourceMasker::calculateMaskContentRepaintRect() 182{ 183 for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { 184 RenderObject* renderer = childNode->renderer(); 185 if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) 186 continue; 187 RenderStyle* style = renderer->style(); 188 if (!style || style->display() == NONE || style->visibility() != VISIBLE) 189 continue; 190 m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates())); 191 } 192} 193 194FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object) 195{ 196 SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node()); 197 ASSERT(maskElement); 198 199 FloatRect objectBoundingBox = object->objectBoundingBox(); 200 FloatRect maskBoundaries = maskElement->maskBoundingBox(objectBoundingBox); 201 202 // Resource was not layouted yet. Give back clipping rect of the mask. 203 if (selfNeedsLayout()) 204 return maskBoundaries; 205 206 if (m_maskContentBoundaries.isEmpty()) 207 calculateMaskContentRepaintRect(); 208 209 FloatRect maskRect = m_maskContentBoundaries; 210 if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 211 AffineTransform transform; 212 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 213 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); 214 maskRect = transform.mapRect(maskRect); 215 } 216 217 maskRect.intersect(maskBoundaries); 218 return maskRect; 219} 220 221} 222 223#endif // ENABLE(SVG) 224