1 /* 2 * Copyright (C) 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 2013 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 29 #include "core/rendering/FilterEffectRenderer.h" 30 31 #include "core/fetch/DocumentResource.h" 32 #include "core/fetch/DocumentResourceReference.h" 33 #include "core/frame/Settings.h" 34 #include "core/page/Page.h" 35 #include "core/rendering/RenderLayer.h" 36 #include "core/rendering/RenderView.h" 37 #include "core/rendering/svg/ReferenceFilterBuilder.h" 38 #include "core/svg/SVGElement.h" 39 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h" 40 #include "platform/FloatConversion.h" 41 #include "platform/LengthFunctions.h" 42 #include "platform/graphics/ColorSpace.h" 43 #include "platform/graphics/UnacceleratedImageBufferSurface.h" 44 #include "platform/graphics/filters/FEColorMatrix.h" 45 #include "platform/graphics/filters/FEComponentTransfer.h" 46 #include "platform/graphics/filters/FEDropShadow.h" 47 #include "platform/graphics/filters/FEGaussianBlur.h" 48 #include "platform/graphics/filters/SkiaImageFilterBuilder.h" 49 #include "wtf/MathExtras.h" 50 #include <algorithm> 51 52 namespace blink { 53 54 static inline void endMatrixRow(Vector<float>& parameters) 55 { 56 parameters.append(0); 57 parameters.append(0); 58 } 59 60 static inline void lastMatrixRow(Vector<float>& parameters) 61 { 62 parameters.append(0); 63 parameters.append(0); 64 parameters.append(0); 65 parameters.append(1); 66 parameters.append(0); 67 } 68 69 FilterEffectRenderer::FilterEffectRenderer() 70 : Filter(AffineTransform()) 71 , m_graphicsBufferAttached(false) 72 , m_hasFilterThatMovesPixels(false) 73 { 74 m_sourceGraphic = SourceGraphic::create(this); 75 } 76 77 FilterEffectRenderer::~FilterEffectRenderer() 78 { 79 } 80 81 GraphicsContext* FilterEffectRenderer::inputContext() 82 { 83 return sourceImage() ? sourceImage()->context() : 0; 84 } 85 86 bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations) 87 { 88 m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels(); 89 90 // Inverse zoom the pre-zoomed CSS shorthand filters, so that they are in the same zoom as the unzoomed reference filters. 91 const RenderStyle* style = renderer->style(); 92 float invZoom = style ? 1.0f / style->effectiveZoom() : 1.0f; 93 94 RefPtr<FilterEffect> previousEffect = m_sourceGraphic; 95 for (size_t i = 0; i < operations.operations().size(); ++i) { 96 RefPtr<FilterEffect> effect; 97 FilterOperation* filterOperation = operations.operations().at(i).get(); 98 switch (filterOperation->type()) { 99 case FilterOperation::REFERENCE: { 100 effect = ReferenceFilterBuilder::build(this, renderer, previousEffect.get(), toReferenceFilterOperation(filterOperation)); 101 break; 102 } 103 case FilterOperation::GRAYSCALE: { 104 Vector<float> inputParameters; 105 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0); 106 107 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#grayscaleEquivalent 108 // for information on parameters. 109 110 inputParameters.append(narrowPrecisionToFloat(0.2126 + 0.7874 * oneMinusAmount)); 111 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount)); 112 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount)); 113 endMatrixRow(inputParameters); 114 115 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount)); 116 inputParameters.append(narrowPrecisionToFloat(0.7152 + 0.2848 * oneMinusAmount)); 117 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount)); 118 endMatrixRow(inputParameters); 119 120 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount)); 121 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount)); 122 inputParameters.append(narrowPrecisionToFloat(0.0722 + 0.9278 * oneMinusAmount)); 123 endMatrixRow(inputParameters); 124 125 lastMatrixRow(inputParameters); 126 127 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters); 128 break; 129 } 130 case FilterOperation::SEPIA: { 131 Vector<float> inputParameters; 132 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0); 133 134 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent 135 // for information on parameters. 136 137 inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount)); 138 inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount)); 139 inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount)); 140 endMatrixRow(inputParameters); 141 142 inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount)); 143 inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount)); 144 inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount)); 145 endMatrixRow(inputParameters); 146 147 inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount)); 148 inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount)); 149 inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount)); 150 endMatrixRow(inputParameters); 151 152 lastMatrixRow(inputParameters); 153 154 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters); 155 break; 156 } 157 case FilterOperation::SATURATE: { 158 Vector<float> inputParameters; 159 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount())); 160 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters); 161 break; 162 } 163 case FilterOperation::HUE_ROTATE: { 164 Vector<float> inputParameters; 165 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount())); 166 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters); 167 break; 168 } 169 case FilterOperation::INVERT: { 170 BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation); 171 ComponentTransferFunction transferFunction; 172 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 173 Vector<float> transferParameters; 174 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount())); 175 transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount())); 176 transferFunction.tableValues = transferParameters; 177 178 ComponentTransferFunction nullFunction; 179 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 180 break; 181 } 182 case FilterOperation::OPACITY: { 183 ComponentTransferFunction transferFunction; 184 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 185 Vector<float> transferParameters; 186 transferParameters.append(0); 187 transferParameters.append(narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount())); 188 transferFunction.tableValues = transferParameters; 189 190 ComponentTransferFunction nullFunction; 191 effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction); 192 break; 193 } 194 case FilterOperation::BRIGHTNESS: { 195 ComponentTransferFunction transferFunction; 196 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 197 transferFunction.slope = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()); 198 transferFunction.intercept = 0; 199 200 ComponentTransferFunction nullFunction; 201 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 202 break; 203 } 204 case FilterOperation::CONTRAST: { 205 ComponentTransferFunction transferFunction; 206 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 207 float amount = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()); 208 transferFunction.slope = amount; 209 transferFunction.intercept = -0.5 * amount + 0.5; 210 211 ComponentTransferFunction nullFunction; 212 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 213 break; 214 } 215 case FilterOperation::BLUR: { 216 float stdDeviation = floatValueForLength(toBlurFilterOperation(filterOperation)->stdDeviation(), 0) * invZoom; 217 effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation); 218 break; 219 } 220 case FilterOperation::DROP_SHADOW: { 221 DropShadowFilterOperation* dropShadowOperation = toDropShadowFilterOperation(filterOperation); 222 float stdDeviation = dropShadowOperation->stdDeviation() * invZoom; 223 float x = dropShadowOperation->x() * invZoom; 224 float y = dropShadowOperation->y() * invZoom; 225 effect = FEDropShadow::create(this, stdDeviation, stdDeviation, x, y, dropShadowOperation->color(), 1); 226 break; 227 } 228 default: 229 break; 230 } 231 232 if (effect) { 233 if (filterOperation->type() != FilterOperation::REFERENCE) { 234 // Unlike SVG, filters applied here should not clip to their primitive subregions. 235 effect->setClipsToBounds(false); 236 effect->setOperatingColorSpace(ColorSpaceDeviceRGB); 237 effect->inputEffects().append(previousEffect); 238 } 239 previousEffect = effect.release(); 240 } 241 } 242 243 // We need to keep the old effects alive until this point, so that SVG reference filters 244 // can share cached resources across frames. 245 m_lastEffect = previousEffect; 246 247 // If we didn't make any effects, tell our caller we are not valid 248 if (!m_lastEffect.get()) 249 return false; 250 251 return true; 252 } 253 254 bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& floatFilterRect) 255 { 256 IntRect filterRect = enclosingIntRect(floatFilterRect); 257 if (!filterRect.isEmpty() && FilterEffect::isFilterSizeValid(filterRect)) { 258 FloatRect currentSourceRect = sourceImageRect(); 259 if (filterRect != currentSourceRect) { 260 setSourceImageRect(filterRect); 261 return true; 262 } 263 } 264 return false; 265 } 266 267 void FilterEffectRenderer::allocateBackingStoreIfNeeded() 268 { 269 // At this point the effect chain has been built, and the 270 // source image sizes set. We just need to attach the graphic 271 // buffer if we have not yet done so. 272 if (!m_graphicsBufferAttached) { 273 IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height()); 274 if (!sourceImage() || sourceImage()->size() != logicalSize) { 275 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(logicalSize)); 276 setSourceImage(ImageBuffer::create(surface.release())); 277 } 278 m_graphicsBufferAttached = true; 279 } 280 } 281 282 void FilterEffectRenderer::clearIntermediateResults() 283 { 284 if (m_lastEffect.get()) 285 m_lastEffect->clearResultsRecursive(); 286 } 287 288 void FilterEffectRenderer::apply() 289 { 290 RefPtr<FilterEffect> effect = lastEffect(); 291 effect->apply(); 292 effect->transformResultColorSpace(ColorSpaceDeviceRGB); 293 } 294 295 LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect) 296 { 297 // The result of this function is the area in the "filterBoxRect" that needs paint invalidation, so that we fully cover the "dirtyRect". 298 FloatRect rectForPaintInvalidation = dirtyRect; 299 float inf = std::numeric_limits<float>::infinity(); 300 FloatRect clipRect = FloatRect(FloatPoint(-inf, -inf), FloatSize(inf, inf)); 301 rectForPaintInvalidation = lastEffect()->getSourceRect(rectForPaintInvalidation, clipRect); 302 rectForPaintInvalidation.intersect(filterBoxRect); 303 return LayoutRect(rectForPaintInvalidation); 304 } 305 306 bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect) 307 { 308 ASSERT(m_haveFilterEffect && renderLayer->filterRenderer()); 309 m_renderLayer = renderLayer; 310 m_paintInvalidationRect = dirtyRect; 311 312 // Get the zoom factor to scale the filterSourceRect input 313 const RenderLayerModelObject* renderer = renderLayer->renderer(); 314 const RenderStyle* style = renderer ? renderer->style() : 0; 315 float zoom = style ? style->effectiveZoom() : 1.0f; 316 317 // Prepare a transformation that brings the coordinates into the space 318 // filter coordinates are defined in. 319 AffineTransform absoluteTransform; 320 // FIXME: Should these really be upconverted to doubles and not rounded? crbug.com/350474 321 absoluteTransform.translate(filterBoxRect.x().toDouble(), filterBoxRect.y().toDouble()); 322 absoluteTransform.scale(zoom, zoom); 323 324 FilterEffectRenderer* filter = renderLayer->filterRenderer(); 325 filter->setAbsoluteTransform(absoluteTransform); 326 327 IntRect filterSourceRect = pixelSnappedIntRect(filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect)); 328 329 if (filterSourceRect.isEmpty()) { 330 // The dirty rect is not in view, just bail out. 331 m_haveFilterEffect = false; 332 return false; 333 } 334 335 m_filterBoxRect = filterBoxRect; 336 filter->setFilterRegion(filter->mapAbsoluteRectToLocalRect(filterSourceRect)); 337 filter->lastEffect()->determineFilterPrimitiveSubregion(MapRectForward); 338 339 bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect); 340 if (filter->hasFilterThatMovesPixels()) { 341 if (hasUpdatedBackingStore) 342 m_paintInvalidationRect = filterSourceRect; 343 else 344 m_paintInvalidationRect.intersect(filterSourceRect); 345 } 346 return true; 347 } 348 349 GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* context) 350 { 351 ASSERT(m_renderLayer); 352 353 FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); 354 if (m_renderLayer->renderer()->document().settings()->deferredFiltersEnabled()) { 355 SkiaImageFilterBuilder builder(context); 356 RefPtr<ImageFilter> imageFilter = builder.build(filter->lastEffect().get(), ColorSpaceDeviceRGB); 357 if (!imageFilter) { 358 m_haveFilterEffect = false; 359 return context; 360 } 361 m_savedGraphicsContext = context; 362 context->save(); 363 FloatRect boundaries = mapImageFilterRect(imageFilter.get(), m_filterBoxRect); 364 context->translate(m_filterBoxRect.x(), m_filterBoxRect.y()); 365 boundaries.move(-m_filterBoxRect.x(), -m_filterBoxRect.y()); 366 context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get()); 367 context->translate(-m_filterBoxRect.x(), -m_filterBoxRect.y()); 368 return context; 369 } 370 filter->allocateBackingStoreIfNeeded(); 371 // Paint into the context that represents the SourceGraphic of the filter. 372 GraphicsContext* sourceGraphicsContext = filter->inputContext(); 373 if (!sourceGraphicsContext || !FilterEffect::isFilterSizeValid(filter->absoluteFilterRegion())) { 374 // Disable the filters and continue. 375 m_haveFilterEffect = false; 376 return context; 377 } 378 379 m_savedGraphicsContext = context; 380 381 // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer. 382 sourceGraphicsContext->save(); 383 // FIXME: can we just use sourceImageRect for everything, and get rid of 384 // m_paintInvalidationRect? 385 FloatPoint offset = filter->sourceImageRect().location(); 386 sourceGraphicsContext->translate(-offset.x(), -offset.y()); 387 sourceGraphicsContext->clearRect(m_paintInvalidationRect); 388 sourceGraphicsContext->clip(m_paintInvalidationRect); 389 390 return sourceGraphicsContext; 391 } 392 393 GraphicsContext* FilterEffectRendererHelper::applyFilterEffect() 394 { 395 ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer()); 396 FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); 397 398 if (m_renderLayer->renderer()->document().settings()->deferredFiltersEnabled()) { 399 GraphicsContext* context = m_savedGraphicsContext; 400 context->endLayer(); 401 context->restore(); 402 return context; 403 } 404 405 filter->inputContext()->restore(); 406 407 filter->apply(); 408 409 // Get the filtered output and draw it in place. 410 m_savedGraphicsContext->drawImageBuffer(filter->output(), filter->outputRect()); 411 412 filter->clearIntermediateResults(); 413 414 return m_savedGraphicsContext; 415 } 416 417 } // namespace blink 418 419