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/frame/Settings.h" 29 #include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h" 30 #include "core/rendering/svg/SVGRenderingContext.h" 31 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h" 32 #include "platform/graphics/UnacceleratedImageBufferSurface.h" 33 #include "platform/graphics/filters/SkiaImageFilterBuilder.h" 34 #include "platform/graphics/filters/SourceAlpha.h" 35 #include "platform/graphics/filters/SourceGraphic.h" 36 37 using namespace std; 38 39 namespace WebCore { 40 41 const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType; 42 43 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node) 44 : RenderSVGResourceContainer(node) 45 { 46 } 47 48 RenderSVGResourceFilter::~RenderSVGResourceFilter() 49 { 50 m_filter.clear(); 51 } 52 53 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const 54 { 55 return child->isSVGResourceFilterPrimitive(); 56 } 57 58 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation) 59 { 60 m_filter.clear(); 61 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation); 62 } 63 64 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation) 65 { 66 ASSERT(client); 67 68 if (FilterData* filterData = m_filter.get(client)) { 69 if (filterData->savedContext) 70 filterData->state = FilterData::MarkedForRemoval; 71 else 72 m_filter.remove(client); 73 } 74 75 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation); 76 } 77 78 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter) 79 { 80 SVGFilterElement* filterElement = toSVGFilterElement(element()); 81 FloatRect targetBoundingBox = filter->targetBoundingBox(); 82 83 // Add effects to the builder 84 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter)); 85 for (SVGElement* element = Traversal<SVGElement>::firstChild(*filterElement); element; element = Traversal<SVGElement>::nextSibling(*element)) { 86 if (!element->isFilterEffect() || !element->renderer()) 87 continue; 88 89 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element); 90 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter); 91 if (!effect) { 92 builder->clearEffects(); 93 return nullptr; 94 } 95 builder->appendEffectToEffectReferences(effect, effectElement->renderer()); 96 effectElement->setStandardAttributes(effect.get()); 97 effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits()->currentValue()->enumValue(), targetBoundingBox)); 98 effect->setOperatingColorSpace( 99 effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB); 100 builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect); 101 } 102 return builder.release(); 103 } 104 105 void RenderSVGResourceFilter::adjustScaleForMaximumImageSize(const FloatSize& size, FloatSize& filterScale) 106 { 107 FloatSize scaledSize(size); 108 scaledSize.scale(filterScale.width(), filterScale.height()); 109 float scaledArea = scaledSize.width() * scaledSize.height(); 110 111 if (scaledArea <= FilterEffect::maxFilterArea()) 112 return; 113 114 // If area of scaled size is bigger than the upper limit, adjust the scale 115 // to fit. 116 filterScale.scale(sqrt(FilterEffect::maxFilterArea() / scaledArea)); 117 } 118 119 static bool createImageBuffer(const Filter* filter, OwnPtr<ImageBuffer>& imageBuffer) 120 { 121 IntRect paintRect = filter->sourceImageRect(); 122 // Don't create empty ImageBuffers. 123 if (paintRect.isEmpty()) 124 return false; 125 126 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(paintRect.size())); 127 if (!surface->isValid()) 128 return false; 129 OwnPtr<ImageBuffer> image = ImageBuffer::create(surface.release()); 130 131 GraphicsContext* imageContext = image->context(); 132 ASSERT(imageContext); 133 134 imageContext->translate(-paintRect.x(), -paintRect.y()); 135 imageContext->concatCTM(filter->absoluteTransform()); 136 imageBuffer = image.release(); 137 return true; 138 } 139 140 static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement) 141 { 142 SkiaImageFilterBuilder builder(context); 143 RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB); 144 // FIXME: Remove the cache when impl-size painting is enabled on every platform and the non impl-side painting path is removed 145 if (!context->isRecordingCanvas()) // Recording canvases do not use the cache 146 filterData->filter->enableCache(); 147 FloatRect boundaries = enclosingIntRect(filterData->boundaries); 148 context->save(); 149 150 FloatSize deviceSize = context->getCTM().mapSize(boundaries.size()); 151 float scaledArea = deviceSize.width() * deviceSize.height(); 152 153 // If area of scaled size is bigger than the upper limit, adjust the scale 154 // to fit. Note that this only really matters in the non-impl-side painting 155 // case, since the impl-side case never allocates a full-sized backing 156 // store, only tile-sized. 157 // FIXME: remove this once all platforms are using impl-side painting. 158 // crbug.com/169282. 159 if (scaledArea > FilterEffect::maxFilterArea()) { 160 float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea); 161 context->scale(scale, scale); 162 } 163 // Clip drawing of filtered image to primitive boundaries. 164 context->clipRect(boundaries); 165 if (filterElement->hasAttribute(SVGNames::filterResAttr)) { 166 // Get boundaries in device coords. 167 // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device 168 // zoom or High DPI adjustments? 169 FloatSize size = context->getCTM().mapSize(boundaries.size()); 170 // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels. 171 float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width(); 172 float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height(); 173 // Scale the CTM so the primitive is drawn to filterRes. 174 context->scale(filterResScaleX, filterResScaleY); 175 // Create a resize filter with the inverse scale. 176 AffineTransform resizeMatrix; 177 resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY); 178 imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get()); 179 } 180 // If the CTM contains rotation or shearing, apply the filter to 181 // the unsheared/unrotated matrix, and do the shearing/rotation 182 // as a final pass. 183 AffineTransform ctm = context->getCTM(); 184 if (ctm.b() || ctm.c()) { 185 AffineTransform scaleAndTranslate; 186 scaleAndTranslate.translate(ctm.e(), ctm.f()); 187 scaleAndTranslate.scale(ctm.xScale(), ctm.yScale()); 188 ASSERT(scaleAndTranslate.isInvertible()); 189 AffineTransform shearAndRotate = scaleAndTranslate.inverse(); 190 shearAndRotate.multiply(ctm); 191 context->setCTM(scaleAndTranslate); 192 imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get()); 193 } 194 context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get()); 195 } 196 197 static void endDeferredFilter(GraphicsContext* context, FilterData* filterData) 198 { 199 context->endLayer(); 200 context->restore(); 201 // FIXME: Remove the cache when impl-size painting is enabled on every platform and the non impl-side painting path is removed 202 if (!context->isRecordingCanvas()) // Recording canvases do not use the cache 203 filterData->filter->disableCache(); 204 } 205 206 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 207 { 208 ASSERT(object); 209 ASSERT(context); 210 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 211 212 clearInvalidationMask(); 213 214 bool deferredFiltersEnabled = object->document().settings()->deferredFiltersEnabled(); 215 if (m_filter.contains(object)) { 216 FilterData* filterData = m_filter.get(object); 217 if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying) 218 filterData->state = FilterData::CycleDetected; 219 if (deferredFiltersEnabled && filterData->state == FilterData::Built) { 220 SVGFilterElement* filterElement = toSVGFilterElement(element()); 221 beginDeferredFilter(context, filterData, filterElement); 222 return true; 223 } 224 return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now. 225 } 226 227 OwnPtr<FilterData> filterData(adoptPtr(new FilterData)); 228 FloatRect targetBoundingBox = object->objectBoundingBox(); 229 230 SVGFilterElement* filterElement = toSVGFilterElement(element()); 231 filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox); 232 if (filterData->boundaries.isEmpty()) 233 return false; 234 235 // Determine absolute transformation matrix for filter. 236 AffineTransform absoluteTransform; 237 SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTransform); 238 if (!absoluteTransform.isInvertible()) 239 return false; 240 241 // Filters cannot handle a full transformation, only scales in each direction. 242 FloatSize filterScale; 243 244 // Calculate the scale factor for the filter. 245 // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion 246 if (filterElement->hasAttribute(SVGNames::filterResAttr)) { 247 // If resolution is specified, scale to match it. 248 filterScale = FloatSize( 249 filterElement->filterResX()->currentValue()->value() / filterData->boundaries.width(), 250 filterElement->filterResY()->currentValue()->value() / filterData->boundaries.height()); 251 } else { 252 // Otherwise, use the scale of the absolute transform. 253 filterScale = FloatSize(absoluteTransform.xScale(), absoluteTransform.yScale()); 254 } 255 // The size of the scaled filter boundaries shouldn't be bigger than kMaxFilterSize. 256 // Intermediate filters are limited by the filter boundaries so they can't be bigger than this. 257 adjustScaleForMaximumImageSize(filterData->boundaries.size(), filterScale); 258 259 filterData->drawingRegion = object->strokeBoundingBox(); 260 filterData->drawingRegion.intersect(filterData->boundaries); 261 FloatRect absoluteDrawingRegion = filterData->drawingRegion; 262 if (!deferredFiltersEnabled) 263 absoluteDrawingRegion.scale(filterScale.width(), filterScale.height()); 264 265 IntRect intDrawingRegion = enclosingIntRect(absoluteDrawingRegion); 266 267 // Create the SVGFilter object. 268 bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; 269 filterData->shearFreeAbsoluteTransform = AffineTransform(); 270 if (!deferredFiltersEnabled) 271 filterData->shearFreeAbsoluteTransform.scale(filterScale.width(), filterScale.height()); 272 filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); 273 274 // Create all relevant filter primitives. 275 filterData->builder = buildPrimitives(filterData->filter.get()); 276 if (!filterData->builder) 277 return false; 278 279 FilterEffect* lastEffect = filterData->builder->lastEffect(); 280 if (!lastEffect) 281 return false; 282 283 lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion); 284 285 if (deferredFiltersEnabled) { 286 FilterData* data = filterData.get(); 287 m_filter.set(object, filterData.release()); 288 beginDeferredFilter(context, data, filterElement); 289 return true; 290 } 291 292 // If the drawingRegion is empty, we have something like <g filter=".."/>. 293 // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. 294 if (filterData->drawingRegion.isEmpty()) { 295 ASSERT(!m_filter.contains(object)); 296 filterData->savedContext = context; 297 m_filter.set(object, filterData.release()); 298 return false; 299 } 300 301 OwnPtr<ImageBuffer> sourceGraphic; 302 if (!createImageBuffer(filterData->filter.get(), sourceGraphic)) { 303 ASSERT(!m_filter.contains(object)); 304 filterData->savedContext = context; 305 m_filter.set(object, filterData.release()); 306 return false; 307 } 308 309 GraphicsContext* sourceGraphicContext = sourceGraphic->context(); 310 ASSERT(sourceGraphicContext); 311 312 filterData->sourceGraphicBuffer = sourceGraphic.release(); 313 filterData->savedContext = context; 314 315 context = sourceGraphicContext; 316 317 ASSERT(!m_filter.contains(object)); 318 m_filter.set(object, filterData.release()); 319 320 return true; 321 } 322 323 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*) 324 { 325 ASSERT(object); 326 ASSERT(context); 327 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 328 329 FilterData* filterData = m_filter.get(object); 330 if (!filterData) 331 return; 332 333 if (object->document().settings()->deferredFiltersEnabled() && (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Built)) { 334 endDeferredFilter(context, filterData); 335 filterData->state = FilterData::Built; 336 return; 337 } 338 339 switch (filterData->state) { 340 case FilterData::MarkedForRemoval: 341 m_filter.remove(object); 342 return; 343 344 case FilterData::CycleDetected: 345 case FilterData::Applying: 346 // We have a cycle if we are already applying the data. 347 // This can occur due to FeImage referencing a source that makes use of the FEImage itself. 348 // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack 349 // will continue correctly. 350 filterData->state = FilterData::PaintingSource; 351 return; 352 353 case FilterData::PaintingSource: 354 if (!filterData->savedContext) { 355 removeClientFromCache(object); 356 return; 357 } 358 359 context = filterData->savedContext; 360 filterData->savedContext = 0; 361 break; 362 363 case FilterData::Built: { } // Empty 364 } 365 366 FilterEffect* lastEffect = filterData->builder->lastEffect(); 367 368 if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) { 369 // This is the real filtering of the object. It just needs to be called on the 370 // initial filtering process. We just take the stored filter result on a 371 // second drawing. 372 if (filterData->state != FilterData::Built) 373 filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release()); 374 375 // Always true if filterData is just built (filterData->state == FilterData::Built). 376 if (!lastEffect->hasResult()) { 377 filterData->state = FilterData::Applying; 378 lastEffect->apply(); 379 lastEffect->correctFilterResultIfNeeded(); 380 lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB); 381 } 382 filterData->state = FilterData::Built; 383 384 ImageBuffer* resultImage = lastEffect->asImageBuffer(); 385 if (resultImage) { 386 context->drawImageBuffer(resultImage, filterData->filter->mapAbsoluteRectToLocalRect(lastEffect->absolutePaintRect())); 387 } 388 } 389 filterData->sourceGraphicBuffer.clear(); 390 } 391 392 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object) 393 { 394 if (SVGFilterElement* element = toSVGFilterElement(this->element())) 395 return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits()->currentValue()->enumValue(), object->objectBoundingBox()); 396 397 return FloatRect(); 398 } 399 400 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute) 401 { 402 FilterMap::iterator it = m_filter.begin(); 403 FilterMap::iterator end = m_filter.end(); 404 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node()); 405 406 for (; it != end; ++it) { 407 FilterData* filterData = it->value.get(); 408 if (filterData->state != FilterData::Built) 409 continue; 410 411 SVGFilterBuilder* builder = filterData->builder.get(); 412 FilterEffect* effect = builder->effectByRenderer(object); 413 if (!effect) 414 continue; 415 // Since all effects shares the same attribute value, all 416 // or none of them will be changed. 417 if (!primitve->setFilterEffectAttribute(effect, attribute)) 418 return; 419 builder->clearResultsRecursive(effect); 420 421 // Repaint the image on the screen. 422 markClientForInvalidation(it->key, RepaintInvalidation); 423 } 424 markAllClientLayersForInvalidation(); 425 } 426 427 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const 428 { 429 FilterData* filterData = m_filter.get(object); 430 return filterData ? filterData->drawingRegion : FloatRect(); 431 } 432 433 } 434