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/dom/Document.h" 32 #include "core/fetch/DocumentResource.h" 33 #include "core/fetch/DocumentResourceReference.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 "wtf/MathExtras.h" 49 #include <algorithm> 50 51 namespace WebCore { 52 53 static inline void endMatrixRow(Vector<float>& parameters) 54 { 55 parameters.append(0); 56 parameters.append(0); 57 } 58 59 static inline void lastMatrixRow(Vector<float>& parameters) 60 { 61 parameters.append(0); 62 parameters.append(0); 63 parameters.append(0); 64 parameters.append(1); 65 parameters.append(0); 66 } 67 68 FilterEffectRenderer::FilterEffectRenderer() 69 : Filter(AffineTransform()) 70 , m_graphicsBufferAttached(false) 71 , m_hasFilterThatMovesPixels(false) 72 { 73 m_sourceGraphic = SourceGraphic::create(this); 74 } 75 76 FilterEffectRenderer::~FilterEffectRenderer() 77 { 78 } 79 80 GraphicsContext* FilterEffectRenderer::inputContext() 81 { 82 return sourceImage() ? sourceImage()->context() : 0; 83 } 84 85 bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations) 86 { 87 m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels(); 88 89 // Inverse zoom the pre-zoomed CSS shorthand filters, so that they are in the same zoom as the unzoomed reference filters. 90 const RenderStyle* style = renderer->style(); 91 #ifdef BLINK_SCALE_FILTERS_AT_RECORD_TIME 92 float invZoom = 1.0f / ((style ? style->effectiveZoom() : 1.0f) * deviceScaleFactor(renderer->frame())); 93 #else 94 float invZoom = style ? 1.0f / style->effectiveZoom() : 1.0f; 95 #endif 96 97 RefPtr<FilterEffect> previousEffect = m_sourceGraphic; 98 for (size_t i = 0; i < operations.operations().size(); ++i) { 99 RefPtr<FilterEffect> effect; 100 FilterOperation* filterOperation = operations.operations().at(i).get(); 101 switch (filterOperation->type()) { 102 case FilterOperation::REFERENCE: { 103 effect = ReferenceFilterBuilder::build(this, renderer, previousEffect.get(), toReferenceFilterOperation(filterOperation)); 104 break; 105 } 106 case FilterOperation::GRAYSCALE: { 107 Vector<float> inputParameters; 108 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0); 109 110 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#grayscaleEquivalent 111 // for information on parameters. 112 113 inputParameters.append(narrowPrecisionToFloat(0.2126 + 0.7874 * oneMinusAmount)); 114 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount)); 115 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount)); 116 endMatrixRow(inputParameters); 117 118 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount)); 119 inputParameters.append(narrowPrecisionToFloat(0.7152 + 0.2848 * oneMinusAmount)); 120 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount)); 121 endMatrixRow(inputParameters); 122 123 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount)); 124 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount)); 125 inputParameters.append(narrowPrecisionToFloat(0.0722 + 0.9278 * oneMinusAmount)); 126 endMatrixRow(inputParameters); 127 128 lastMatrixRow(inputParameters); 129 130 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters); 131 break; 132 } 133 case FilterOperation::SEPIA: { 134 Vector<float> inputParameters; 135 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0); 136 137 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent 138 // for information on parameters. 139 140 inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount)); 141 inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount)); 142 inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount)); 143 endMatrixRow(inputParameters); 144 145 inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount)); 146 inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount)); 147 inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount)); 148 endMatrixRow(inputParameters); 149 150 inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount)); 151 inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount)); 152 inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount)); 153 endMatrixRow(inputParameters); 154 155 lastMatrixRow(inputParameters); 156 157 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters); 158 break; 159 } 160 case FilterOperation::SATURATE: { 161 Vector<float> inputParameters; 162 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount())); 163 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters); 164 break; 165 } 166 case FilterOperation::HUE_ROTATE: { 167 Vector<float> inputParameters; 168 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount())); 169 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters); 170 break; 171 } 172 case FilterOperation::INVERT: { 173 BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation); 174 ComponentTransferFunction transferFunction; 175 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 176 Vector<float> transferParameters; 177 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount())); 178 transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount())); 179 transferFunction.tableValues = transferParameters; 180 181 ComponentTransferFunction nullFunction; 182 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 183 break; 184 } 185 case FilterOperation::OPACITY: { 186 ComponentTransferFunction transferFunction; 187 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 188 Vector<float> transferParameters; 189 transferParameters.append(0); 190 transferParameters.append(narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount())); 191 transferFunction.tableValues = transferParameters; 192 193 ComponentTransferFunction nullFunction; 194 effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction); 195 break; 196 } 197 case FilterOperation::BRIGHTNESS: { 198 ComponentTransferFunction transferFunction; 199 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 200 transferFunction.slope = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()); 201 transferFunction.intercept = 0; 202 203 ComponentTransferFunction nullFunction; 204 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 205 break; 206 } 207 case FilterOperation::CONTRAST: { 208 ComponentTransferFunction transferFunction; 209 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 210 float amount = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()); 211 transferFunction.slope = amount; 212 transferFunction.intercept = -0.5 * amount + 0.5; 213 214 ComponentTransferFunction nullFunction; 215 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 216 break; 217 } 218 case FilterOperation::BLUR: { 219 float stdDeviation = floatValueForLength(toBlurFilterOperation(filterOperation)->stdDeviation(), 0) * invZoom; 220 effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation); 221 break; 222 } 223 case FilterOperation::DROP_SHADOW: { 224 DropShadowFilterOperation* dropShadowOperation = toDropShadowFilterOperation(filterOperation); 225 float stdDeviation = dropShadowOperation->stdDeviation() * invZoom; 226 float x = dropShadowOperation->x() * invZoom; 227 float y = dropShadowOperation->y() * invZoom; 228 effect = FEDropShadow::create(this, stdDeviation, stdDeviation, x, y, dropShadowOperation->color(), 1); 229 break; 230 } 231 default: 232 break; 233 } 234 235 if (effect) { 236 if (filterOperation->type() != FilterOperation::REFERENCE) { 237 // Unlike SVG, filters applied here should not clip to their primitive subregions. 238 effect->setClipsToBounds(false); 239 effect->setOperatingColorSpace(ColorSpaceDeviceRGB); 240 effect->inputEffects().append(previousEffect); 241 } 242 previousEffect = effect.release(); 243 } 244 } 245 246 // We need to keep the old effects alive until this point, so that SVG reference filters 247 // can share cached resources across frames. 248 m_lastEffect = previousEffect; 249 250 // If we didn't make any effects, tell our caller we are not valid 251 if (!m_lastEffect.get()) 252 return false; 253 254 return true; 255 } 256 257 bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& floatFilterRect) 258 { 259 IntRect filterRect = enclosingIntRect(floatFilterRect); 260 if (!filterRect.isEmpty() && FilterEffect::isFilterSizeValid(filterRect)) { 261 FloatRect currentSourceRect = sourceImageRect(); 262 if (filterRect != currentSourceRect) { 263 setSourceImageRect(filterRect); 264 return true; 265 } 266 } 267 return false; 268 } 269 270 void FilterEffectRenderer::allocateBackingStoreIfNeeded() 271 { 272 // At this point the effect chain has been built, and the 273 // source image sizes set. We just need to attach the graphic 274 // buffer if we have not yet done so. 275 if (!m_graphicsBufferAttached) { 276 IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height()); 277 if (!sourceImage() || sourceImage()->size() != logicalSize) { 278 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(logicalSize)); 279 setSourceImage(ImageBuffer::create(surface.release())); 280 } 281 m_graphicsBufferAttached = true; 282 } 283 } 284 285 void FilterEffectRenderer::clearIntermediateResults() 286 { 287 if (m_lastEffect.get()) 288 m_lastEffect->clearResultsRecursive(); 289 } 290 291 void FilterEffectRenderer::apply() 292 { 293 RefPtr<FilterEffect> effect = lastEffect(); 294 effect->apply(); 295 effect->transformResultColorSpace(ColorSpaceDeviceRGB); 296 } 297 298 LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect) 299 { 300 // The result of this function is the area in the "filterBoxRect" that needs to be repainted, so that we fully cover the "dirtyRect". 301 FloatRect rectForRepaint = dirtyRect; 302 float inf = std::numeric_limits<float>::infinity(); 303 FloatRect clipRect = FloatRect(FloatPoint(-inf, -inf), FloatSize(inf, inf)); 304 rectForRepaint = lastEffect()->getSourceRect(rectForRepaint, clipRect); 305 rectForRepaint.intersect(filterBoxRect); 306 return LayoutRect(rectForRepaint); 307 } 308 309 bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect) 310 { 311 ASSERT(m_haveFilterEffect && renderLayer->filterRenderer()); 312 m_renderLayer = renderLayer; 313 m_repaintRect = dirtyRect; 314 315 // Get the zoom factor to scale the filterSourceRect input 316 const RenderLayerModelObject* renderer = renderLayer->renderer(); 317 const RenderStyle* style = renderer ? renderer->style() : 0; 318 float zoom = style ? style->effectiveZoom() : 1.0f; 319 320 // Prepare a transformation that brings the coordinates into the space 321 // filter coordinates are defined in. 322 AffineTransform absoluteTransform; 323 // FIXME: Should these really be upconverted to doubles and not rounded? crbug.com/350474 324 absoluteTransform.translate(filterBoxRect.x().toDouble(), filterBoxRect.y().toDouble()); 325 absoluteTransform.scale(zoom, zoom); 326 327 FilterEffectRenderer* filter = renderLayer->filterRenderer(); 328 filter->setAbsoluteTransform(absoluteTransform); 329 330 IntRect filterSourceRect = pixelSnappedIntRect(filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect)); 331 332 if (filterSourceRect.isEmpty()) { 333 // The dirty rect is not in view, just bail out. 334 m_haveFilterEffect = false; 335 return false; 336 } 337 338 filter->setFilterRegion(filter->mapAbsoluteRectToLocalRect(filterSourceRect)); 339 filter->lastEffect()->determineFilterPrimitiveSubregion(MapRectForward); 340 341 bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect); 342 if (filter->hasFilterThatMovesPixels()) { 343 if (hasUpdatedBackingStore) 344 m_repaintRect = filterSourceRect; 345 else { 346 m_repaintRect.unite(layerRepaintRect); 347 m_repaintRect.intersect(filterSourceRect); 348 } 349 } 350 return true; 351 } 352 353 GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* oldContext) 354 { 355 ASSERT(m_renderLayer); 356 357 FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); 358 filter->allocateBackingStoreIfNeeded(); 359 // Paint into the context that represents the SourceGraphic of the filter. 360 GraphicsContext* sourceGraphicsContext = filter->inputContext(); 361 if (!sourceGraphicsContext || !FilterEffect::isFilterSizeValid(filter->absoluteFilterRegion())) { 362 // Disable the filters and continue. 363 m_haveFilterEffect = false; 364 return oldContext; 365 } 366 367 m_savedGraphicsContext = oldContext; 368 369 // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer. 370 sourceGraphicsContext->save(); 371 // FIXME: can we just use sourceImageRect for everything, and get rid of 372 // m_repaintRect? 373 FloatPoint offset = filter->sourceImageRect().location(); 374 sourceGraphicsContext->translate(-offset.x(), -offset.y()); 375 sourceGraphicsContext->clearRect(m_repaintRect); 376 sourceGraphicsContext->clip(m_repaintRect); 377 378 return sourceGraphicsContext; 379 } 380 381 GraphicsContext* FilterEffectRendererHelper::applyFilterEffect() 382 { 383 ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer()); 384 FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); 385 filter->inputContext()->restore(); 386 387 filter->apply(); 388 389 // Get the filtered output and draw it in place. 390 m_savedGraphicsContext->drawImageBuffer(filter->output(), filter->outputRect()); 391 392 filter->clearIntermediateResults(); 393 394 return m_savedGraphicsContext; 395 } 396 397 } // namespace WebCore 398 399