1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2005 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org> 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 */ 23 24 #include "config.h" 25 26 #if ENABLE(SVG) && ENABLE(FILTERS) 27 #include "RenderSVGResourceFilter.h" 28 29 #include "AffineTransform.h" 30 #include "FilterEffect.h" 31 #include "FloatPoint.h" 32 #include "FloatRect.h" 33 #include "GraphicsContext.h" 34 #include "Image.h" 35 #include "ImageBuffer.h" 36 #include "ImageData.h" 37 #include "IntRect.h" 38 #include "RenderSVGResource.h" 39 #include "RenderSVGResourceFilterPrimitive.h" 40 #include "SVGElement.h" 41 #include "SVGFilter.h" 42 #include "SVGFilterElement.h" 43 #include "SVGFilterPrimitiveStandardAttributes.h" 44 #include "SVGImageBufferTools.h" 45 #include "SVGNames.h" 46 #include "SVGStyledElement.h" 47 #include "SVGUnitTypes.h" 48 49 #include <wtf/UnusedParam.h> 50 #include <wtf/Vector.h> 51 52 using namespace std; 53 54 namespace WebCore { 55 56 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType; 57 58 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node) 59 : RenderSVGResourceContainer(node) 60 { 61 } 62 63 RenderSVGResourceFilter::~RenderSVGResourceFilter() 64 { 65 if (m_filter.isEmpty()) 66 return; 67 68 deleteAllValues(m_filter); 69 m_filter.clear(); 70 } 71 72 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation) 73 { 74 if (!m_filter.isEmpty()) { 75 deleteAllValues(m_filter); 76 m_filter.clear(); 77 } 78 79 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation); 80 } 81 82 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation) 83 { 84 ASSERT(client); 85 86 if (FilterData* filterData = m_filter.get(client)) { 87 if (filterData->savedContext) 88 filterData->markedForRemoval = true; 89 else 90 delete m_filter.take(client); 91 } 92 93 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation); 94 } 95 96 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(Filter* filter) 97 { 98 SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); 99 bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; 100 101 // Add effects to the builder 102 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(filter); 103 for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) { 104 if (!node->isSVGElement()) 105 continue; 106 107 SVGElement* element = static_cast<SVGElement*>(node); 108 if (!element->isFilterEffect()) 109 continue; 110 111 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element); 112 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter); 113 if (!effect) { 114 builder->clearEffects(); 115 return 0; 116 } 117 builder->appendEffectToEffectReferences(effect, effectElement->renderer()); 118 effectElement->setStandardAttributes(primitiveBoundingBoxMode, effect.get()); 119 builder->add(effectElement->result(), effect); 120 } 121 return builder.release(); 122 } 123 124 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale) 125 { 126 bool matchesFilterSize = true; 127 if (size.width() > kMaxFilterSize) { 128 scale.setWidth(scale.width() * kMaxFilterSize / size.width()); 129 matchesFilterSize = false; 130 } 131 if (size.height() > kMaxFilterSize) { 132 scale.setHeight(scale.height() * kMaxFilterSize / size.height()); 133 matchesFilterSize = false; 134 } 135 136 return matchesFilterSize; 137 } 138 139 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 140 { 141 ASSERT(object); 142 ASSERT(context); 143 #ifndef NDEBUG 144 ASSERT(resourceMode == ApplyToDefaultMode); 145 #else 146 UNUSED_PARAM(resourceMode); 147 #endif 148 149 // Returning false here, to avoid drawings onto the context. We just want to 150 // draw the stored filter output, not the unfiltered object as well. 151 if (m_filter.contains(object)) { 152 FilterData* filterData = m_filter.get(object); 153 if (filterData->builded) 154 return false; 155 156 delete m_filter.take(object); // Oops, have to rebuild, go through normal code path 157 } 158 159 OwnPtr<FilterData> filterData(new FilterData); 160 FloatRect targetBoundingBox = object->objectBoundingBox(); 161 162 SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); 163 filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox); 164 if (filterData->boundaries.isEmpty()) 165 return false; 166 167 // Determine absolute transformation matrix for filter. 168 AffineTransform absoluteTransform; 169 SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); 170 if (!absoluteTransform.isInvertible()) 171 return false; 172 173 // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. 174 filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), absoluteTransform.e(), absoluteTransform.f()); 175 176 // Determine absolute boundaries of the filter and the drawing region. 177 FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries); 178 FloatRect drawingRegion = object->strokeBoundingBox(); 179 drawingRegion.intersect(filterData->boundaries); 180 FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion); 181 182 // Create the SVGFilter object. 183 bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; 184 filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); 185 186 // Create all relevant filter primitives. 187 filterData->builder = buildPrimitives(filterData->filter.get()); 188 if (!filterData->builder) 189 return false; 190 191 // Calculate the scale factor for the use of filterRes. 192 // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion 193 FloatSize scale(1, 1); 194 if (filterElement->hasAttribute(SVGNames::filterResAttr)) { 195 scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width()); 196 scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height()); 197 } 198 199 if (scale.isEmpty()) 200 return false; 201 202 // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. 203 FloatRect tempSourceRect = absoluteDrawingRegion; 204 tempSourceRect.scale(scale.width(), scale.height()); 205 fitsInMaximumImageSize(tempSourceRect.size(), scale); 206 207 // Set the scale level in SVGFilter. 208 filterData->filter->setFilterResolution(scale); 209 210 FilterEffect* lastEffect = filterData->builder->lastEffect(); 211 if (!lastEffect) 212 return false; 213 214 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); 215 FloatRect subRegion = lastEffect->maxEffectRect(); 216 // At least one FilterEffect has a too big image size, 217 // recalculate the effect sizes with new scale factors. 218 if (!fitsInMaximumImageSize(subRegion.size(), scale)) { 219 filterData->filter->setFilterResolution(scale); 220 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); 221 } 222 223 // If the drawingRegion is empty, we have something like <g filter=".."/>. 224 // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. 225 if (drawingRegion.isEmpty()) { 226 ASSERT(!m_filter.contains(object)); 227 filterData->savedContext = context; 228 m_filter.set(object, filterData.leakPtr()); 229 return false; 230 } 231 232 absoluteDrawingRegion.scale(scale.width(), scale.height()); 233 234 OwnPtr<ImageBuffer> sourceGraphic; 235 if (!SVGImageBufferTools::createImageBuffer(absoluteDrawingRegion, absoluteDrawingRegion, sourceGraphic, ColorSpaceLinearRGB)) { 236 ASSERT(!m_filter.contains(object)); 237 filterData->savedContext = context; 238 m_filter.set(object, filterData.leakPtr()); 239 return false; 240 } 241 242 GraphicsContext* sourceGraphicContext = sourceGraphic->context(); 243 ASSERT(sourceGraphicContext); 244 245 sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y()); 246 if (scale.width() != 1 || scale.height() != 1) 247 sourceGraphicContext->scale(scale); 248 249 sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform); 250 sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size())); 251 filterData->sourceGraphicBuffer = sourceGraphic.release(); 252 filterData->savedContext = context; 253 254 context = sourceGraphicContext; 255 256 ASSERT(!m_filter.contains(object)); 257 m_filter.set(object, filterData.leakPtr()); 258 259 return true; 260 } 261 262 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*) 263 { 264 ASSERT(object); 265 ASSERT(context); 266 #ifndef NDEBUG 267 ASSERT(resourceMode == ApplyToDefaultMode); 268 #else 269 UNUSED_PARAM(resourceMode); 270 #endif 271 272 FilterData* filterData = m_filter.get(object); 273 if (!filterData) 274 return; 275 276 if (filterData->markedForRemoval) { 277 delete m_filter.take(object); 278 return; 279 } 280 281 if (!filterData->builded) { 282 if (!filterData->savedContext) { 283 removeClientFromCache(object); 284 return; 285 } 286 287 context = filterData->savedContext; 288 filterData->savedContext = 0; 289 #if !USE(CG) 290 if (filterData->sourceGraphicBuffer) 291 filterData->sourceGraphicBuffer->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB); 292 #endif 293 } 294 295 FilterEffect* lastEffect = filterData->builder->lastEffect(); 296 297 if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) { 298 // This is the real filtering of the object. It just needs to be called on the 299 // initial filtering process. We just take the stored filter result on a 300 // second drawing. 301 if (!filterData->builded) 302 filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release()); 303 304 // Always true if filterData is just built (filterData->builded is false). 305 if (!lastEffect->hasResult()) { 306 lastEffect->apply(); 307 #if !USE(CG) 308 ImageBuffer* resultImage = lastEffect->asImageBuffer(); 309 if (resultImage) 310 resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB); 311 #endif 312 } 313 filterData->builded = true; 314 315 ImageBuffer* resultImage = lastEffect->asImageBuffer(); 316 if (resultImage) { 317 context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse()); 318 319 context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height())); 320 context->clip(lastEffect->maxEffectRect()); 321 context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect()); 322 context->scale(filterData->filter->filterResolution()); 323 324 context->concatCTM(filterData->shearFreeAbsoluteTransform); 325 } 326 } 327 filterData->sourceGraphicBuffer.clear(); 328 } 329 330 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object) 331 { 332 if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node())) 333 return element->filterBoundingBox(object->objectBoundingBox()); 334 335 return FloatRect(); 336 } 337 338 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute) 339 { 340 HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin(); 341 HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end(); 342 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node()); 343 344 for (; it != end; ++it) { 345 FilterData* filterData = it->second; 346 if (!filterData->builded) 347 continue; 348 349 SVGFilterBuilder* builder = filterData->builder.get(); 350 FilterEffect* effect = builder->effectByRenderer(object); 351 if (!effect) 352 continue; 353 // Since all effects shares the same attribute value, all 354 // or none of them will be changed. 355 if (!primitve->setFilterEffectAttribute(effect, attribute)) 356 return; 357 builder->clearResultsRecursive(effect); 358 359 // Repaint the image on the screen. 360 markClientForInvalidation(it->first, RepaintInvalidation); 361 } 362 } 363 364 } 365 #endif 366