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 #include "core/rendering/svg/RenderSVGResourceFilter.h" 27 28 #include "core/page/Page.h" 29 #include "core/page/Settings.h" 30 #include "core/platform/graphics/filters/SourceAlpha.h" 31 #include "core/platform/graphics/filters/SourceGraphic.h" 32 #include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h" 33 #include "core/rendering/svg/SVGRenderingContext.h" 34 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h" 35 36 using namespace std; 37 38 namespace WebCore { 39 40 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType; 41 42 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node) 43 : RenderSVGResourceContainer(node) 44 { 45 } 46 47 RenderSVGResourceFilter::~RenderSVGResourceFilter() 48 { 49 if (m_filter.isEmpty()) 50 return; 51 52 deleteAllValues(m_filter); 53 m_filter.clear(); 54 } 55 56 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation) 57 { 58 if (!m_filter.isEmpty()) { 59 deleteAllValues(m_filter); 60 m_filter.clear(); 61 } 62 63 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation); 64 } 65 66 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation) 67 { 68 ASSERT(client); 69 70 if (FilterData* filterData = m_filter.get(client)) { 71 if (filterData->savedContext) 72 filterData->state = FilterData::MarkedForRemoval; 73 else 74 delete m_filter.take(client); 75 } 76 77 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation); 78 } 79 80 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter) 81 { 82 SVGFilterElement* filterElement = toSVGFilterElement(node()); 83 FloatRect targetBoundingBox = filter->targetBoundingBox(); 84 85 // Add effects to the builder 86 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter)); 87 for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) { 88 if (!node->isSVGElement()) 89 continue; 90 91 SVGElement* element = toSVGElement(node); 92 if (!element->isFilterEffect() || !element->renderer()) 93 continue; 94 95 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element); 96 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter); 97 if (!effect) { 98 builder->clearEffects(); 99 return 0; 100 } 101 builder->appendEffectToEffectReferences(effect, effectElement->renderer()); 102 effectElement->setStandardAttributes(effect.get()); 103 effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnitsCurrentValue(), targetBoundingBox)); 104 effect->setOperatingColorSpace( 105 effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB); 106 builder->add(effectElement->resultCurrentValue(), effect); 107 } 108 return builder.release(); 109 } 110 111 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale) 112 { 113 bool matchesFilterSize = true; 114 if (size.width() > kMaxFilterSize) { 115 scale.setWidth(scale.width() * kMaxFilterSize / size.width()); 116 matchesFilterSize = false; 117 } 118 if (size.height() > kMaxFilterSize) { 119 scale.setHeight(scale.height() * kMaxFilterSize / size.height()); 120 matchesFilterSize = false; 121 } 122 123 return matchesFilterSize; 124 } 125 126 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 127 { 128 ASSERT(object); 129 ASSERT(context); 130 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 131 132 if (m_filter.contains(object)) { 133 FilterData* filterData = m_filter.get(object); 134 if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying) 135 filterData->state = FilterData::CycleDetected; 136 return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now. 137 } 138 139 OwnPtr<FilterData> filterData(adoptPtr(new FilterData)); 140 FloatRect targetBoundingBox = object->objectBoundingBox(); 141 142 SVGFilterElement* filterElement = toSVGFilterElement(node()); 143 filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnitsCurrentValue(), targetBoundingBox); 144 if (filterData->boundaries.isEmpty()) 145 return false; 146 147 // Determine absolute transformation matrix for filter. 148 AffineTransform absoluteTransform; 149 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform); 150 if (!absoluteTransform.isInvertible()) 151 return false; 152 153 // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. 154 filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0); 155 156 // Determine absolute boundaries of the filter and the drawing region. 157 FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries); 158 filterData->drawingRegion = object->strokeBoundingBox(); 159 filterData->drawingRegion.intersect(filterData->boundaries); 160 FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion); 161 162 // Create the SVGFilter object. 163 bool primitiveBoundingBoxMode = filterElement->primitiveUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; 164 filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); 165 166 // Create all relevant filter primitives. 167 filterData->builder = buildPrimitives(filterData->filter.get()); 168 if (!filterData->builder) 169 return false; 170 171 // Calculate the scale factor for the use of filterRes. 172 // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion 173 FloatSize scale(1, 1); 174 if (filterElement->hasAttribute(SVGNames::filterResAttr)) { 175 scale.setWidth(filterElement->filterResXCurrentValue() / absoluteFilterBoundaries.width()); 176 scale.setHeight(filterElement->filterResYCurrentValue() / absoluteFilterBoundaries.height()); 177 } 178 179 if (scale.isEmpty()) 180 return false; 181 182 // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. 183 FloatRect tempSourceRect = absoluteDrawingRegion; 184 tempSourceRect.scale(scale.width(), scale.height()); 185 fitsInMaximumImageSize(tempSourceRect.size(), scale); 186 187 // Set the scale level in SVGFilter. 188 filterData->filter->setFilterResolution(scale); 189 190 FilterEffect* lastEffect = filterData->builder->lastEffect(); 191 if (!lastEffect) 192 return false; 193 194 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); 195 FloatRect subRegion = lastEffect->maxEffectRect(); 196 // At least one FilterEffect has a too big image size, 197 // recalculate the effect sizes with new scale factors. 198 if (!fitsInMaximumImageSize(subRegion.size(), scale)) { 199 filterData->filter->setFilterResolution(scale); 200 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); 201 } 202 203 // If the drawingRegion is empty, we have something like <g filter=".."/>. 204 // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. 205 if (filterData->drawingRegion.isEmpty()) { 206 ASSERT(!m_filter.contains(object)); 207 filterData->savedContext = context; 208 m_filter.set(object, filterData.leakPtr()); 209 return false; 210 } 211 212 // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter. 213 AffineTransform effectiveTransform; 214 effectiveTransform.scale(scale.width(), scale.height()); 215 effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform); 216 217 OwnPtr<ImageBuffer> sourceGraphic; 218 RenderingMode renderingMode = object->document()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; 219 if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, renderingMode)) { 220 ASSERT(!m_filter.contains(object)); 221 filterData->savedContext = context; 222 m_filter.set(object, filterData.leakPtr()); 223 return false; 224 } 225 226 // Set the rendering mode from the page's settings. 227 filterData->filter->setRenderingMode(renderingMode); 228 229 GraphicsContext* sourceGraphicContext = sourceGraphic->context(); 230 ASSERT(sourceGraphicContext); 231 232 filterData->sourceGraphicBuffer = sourceGraphic.release(); 233 filterData->savedContext = context; 234 235 context = sourceGraphicContext; 236 237 ASSERT(!m_filter.contains(object)); 238 m_filter.set(object, filterData.leakPtr()); 239 240 return true; 241 } 242 243 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*) 244 { 245 ASSERT(object); 246 ASSERT(context); 247 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 248 249 FilterData* filterData = m_filter.get(object); 250 if (!filterData) 251 return; 252 253 switch (filterData->state) { 254 case FilterData::MarkedForRemoval: 255 delete m_filter.take(object); 256 return; 257 258 case FilterData::CycleDetected: 259 case FilterData::Applying: 260 // We have a cycle if we are already applying the data. 261 // This can occur due to FeImage referencing a source that makes use of the FEImage itself. 262 // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack 263 // will continue correctly. 264 filterData->state = FilterData::PaintingSource; 265 return; 266 267 case FilterData::PaintingSource: 268 if (!filterData->savedContext) { 269 removeClientFromCache(object); 270 return; 271 } 272 273 context = filterData->savedContext; 274 filterData->savedContext = 0; 275 break; 276 277 case FilterData::Built: { } // Empty 278 } 279 280 FilterEffect* lastEffect = filterData->builder->lastEffect(); 281 282 if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) { 283 // This is the real filtering of the object. It just needs to be called on the 284 // initial filtering process. We just take the stored filter result on a 285 // second drawing. 286 if (filterData->state != FilterData::Built) 287 filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release()); 288 289 // Always true if filterData is just built (filterData->state == FilterData::Built). 290 if (!lastEffect->hasResult()) { 291 filterData->state = FilterData::Applying; 292 lastEffect->apply(); 293 lastEffect->correctFilterResultIfNeeded(); 294 lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB); 295 } 296 filterData->state = FilterData::Built; 297 298 ImageBuffer* resultImage = lastEffect->asImageBuffer(); 299 if (resultImage) { 300 context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse()); 301 302 context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height())); 303 context->drawImageBuffer(resultImage, lastEffect->absolutePaintRect()); 304 context->scale(filterData->filter->filterResolution()); 305 306 context->concatCTM(filterData->shearFreeAbsoluteTransform); 307 } 308 } 309 filterData->sourceGraphicBuffer.clear(); 310 } 311 312 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object) 313 { 314 if (SVGFilterElement* element = toSVGFilterElement(node())) 315 return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnitsCurrentValue(), object->objectBoundingBox()); 316 317 return FloatRect(); 318 } 319 320 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute) 321 { 322 HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin(); 323 HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end(); 324 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node()); 325 326 for (; it != end; ++it) { 327 FilterData* filterData = it->value; 328 if (filterData->state != FilterData::Built) 329 continue; 330 331 SVGFilterBuilder* builder = filterData->builder.get(); 332 FilterEffect* effect = builder->effectByRenderer(object); 333 if (!effect) 334 continue; 335 // Since all effects shares the same attribute value, all 336 // or none of them will be changed. 337 if (!primitve->setFilterEffectAttribute(effect, attribute)) 338 return; 339 builder->clearResultsRecursive(effect); 340 341 // Repaint the image on the screen. 342 markClientForInvalidation(it->key, RepaintInvalidation); 343 } 344 markAllClientLayersForInvalidation(); 345 } 346 347 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const 348 { 349 FilterData* filterData = m_filter.get(object); 350 return filterData ? filterData->drawingRegion : FloatRect(); 351 } 352 353 } 354