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/loader/cache/DocumentResource.h" 33 #include "core/loader/cache/DocumentResourceReference.h" 34 #include "core/page/Page.h" 35 #include "core/platform/FloatConversion.h" 36 #include "core/platform/graphics/ColorSpace.h" 37 #include "core/platform/graphics/filters/FEColorMatrix.h" 38 #include "core/platform/graphics/filters/FEComponentTransfer.h" 39 #include "core/platform/graphics/filters/FEDropShadow.h" 40 #include "core/platform/graphics/filters/FEGaussianBlur.h" 41 #include "core/platform/graphics/filters/custom/CustomFilterGlobalContext.h" 42 #include "core/platform/graphics/filters/custom/CustomFilterValidatedProgram.h" 43 #include "core/platform/graphics/filters/custom/FECustomFilter.h" 44 #include "core/platform/graphics/filters/custom/ValidatedCustomFilterOperation.h" 45 #include "core/rendering/RenderLayer.h" 46 #include "core/rendering/RenderView.h" 47 #include "core/rendering/svg/ReferenceFilterBuilder.h" 48 49 #include "core/svg/SVGElement.h" 50 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h" 51 #include "wtf/MathExtras.h" 52 #include <algorithm> 53 54 namespace WebCore { 55 56 static inline void endMatrixRow(Vector<float>& parameters) 57 { 58 parameters.append(0); 59 parameters.append(0); 60 } 61 62 static inline void lastMatrixRow(Vector<float>& parameters) 63 { 64 parameters.append(0); 65 parameters.append(0); 66 parameters.append(0); 67 parameters.append(1); 68 parameters.append(0); 69 } 70 71 inline bool isFilterSizeValid(FloatRect rect) 72 { 73 if (rect.width() < 0 || rect.width() > kMaxFilterSize 74 || rect.height() < 0 || rect.height() > kMaxFilterSize) 75 return false; 76 return true; 77 } 78 79 static PassRefPtr<FECustomFilter> createCustomFilterEffect(Filter* filter, Document* document, ValidatedCustomFilterOperation* operation) 80 { 81 if (!document) 82 return 0; 83 84 CustomFilterGlobalContext* globalContext = document->renderView()->customFilterGlobalContext(); 85 globalContext->prepareContextIfNeeded(); 86 if (!globalContext->context()) 87 return 0; 88 89 return FECustomFilter::create(filter, globalContext->context(), operation->validatedProgram(), operation->parameters(), 90 operation->meshRows(), operation->meshColumns(), operation->meshType()); 91 } 92 93 FilterEffectRenderer::FilterEffectRenderer() 94 : Filter(AffineTransform()) 95 , m_graphicsBufferAttached(false) 96 , m_hasFilterThatMovesPixels(false) 97 , m_hasCustomShaderFilter(false) 98 { 99 setFilterResolution(FloatSize(1, 1)); 100 m_sourceGraphic = SourceGraphic::create(this); 101 } 102 103 FilterEffectRenderer::~FilterEffectRenderer() 104 { 105 } 106 107 GraphicsContext* FilterEffectRenderer::inputContext() 108 { 109 return sourceImage() ? sourceImage()->context() : 0; 110 } 111 112 bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations) 113 { 114 m_hasCustomShaderFilter = false; 115 m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels(); 116 117 // Inverse zoom the pre-zoomed CSS shorthand filters, so that they are in the same zoom as the unzoomed reference filters. 118 const RenderStyle* style = renderer->style(); 119 // FIXME: The effects now contain high dpi information, but the software path doesn't (yet) scale its backing. 120 // When the proper dpi dependant backing size is allocated, we should remove deviceScaleFactor(...) here. 121 float invZoom = 1.0f / ((style ? style->effectiveZoom() : 1.0f) * deviceScaleFactor(renderer->frame())); 122 123 RefPtr<FilterEffect> previousEffect = m_sourceGraphic; 124 for (size_t i = 0; i < operations.operations().size(); ++i) { 125 RefPtr<FilterEffect> effect; 126 FilterOperation* filterOperation = operations.operations().at(i).get(); 127 switch (filterOperation->getOperationType()) { 128 case FilterOperation::REFERENCE: { 129 ReferenceFilterOperation* referenceOperation = static_cast<ReferenceFilterOperation*>(filterOperation); 130 effect = ReferenceFilterBuilder::build(this, renderer, previousEffect.get(), referenceOperation); 131 break; 132 } 133 case FilterOperation::GRAYSCALE: { 134 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation); 135 Vector<float> inputParameters; 136 double oneMinusAmount = clampTo(1 - colorMatrixOperation->amount(), 0.0, 1.0); 137 138 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#grayscaleEquivalent 139 // for information on parameters. 140 141 inputParameters.append(narrowPrecisionToFloat(0.2126 + 0.7874 * oneMinusAmount)); 142 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount)); 143 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount)); 144 endMatrixRow(inputParameters); 145 146 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount)); 147 inputParameters.append(narrowPrecisionToFloat(0.7152 + 0.2848 * oneMinusAmount)); 148 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount)); 149 endMatrixRow(inputParameters); 150 151 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount)); 152 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount)); 153 inputParameters.append(narrowPrecisionToFloat(0.0722 + 0.9278 * oneMinusAmount)); 154 endMatrixRow(inputParameters); 155 156 lastMatrixRow(inputParameters); 157 158 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters); 159 break; 160 } 161 case FilterOperation::SEPIA: { 162 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation); 163 Vector<float> inputParameters; 164 double oneMinusAmount = clampTo(1 - colorMatrixOperation->amount(), 0.0, 1.0); 165 166 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent 167 // for information on parameters. 168 169 inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount)); 170 inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount)); 171 inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount)); 172 endMatrixRow(inputParameters); 173 174 inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount)); 175 inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount)); 176 inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount)); 177 endMatrixRow(inputParameters); 178 179 inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount)); 180 inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount)); 181 inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount)); 182 endMatrixRow(inputParameters); 183 184 lastMatrixRow(inputParameters); 185 186 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters); 187 break; 188 } 189 case FilterOperation::SATURATE: { 190 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation); 191 Vector<float> inputParameters; 192 inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount())); 193 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters); 194 break; 195 } 196 case FilterOperation::HUE_ROTATE: { 197 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation); 198 Vector<float> inputParameters; 199 inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount())); 200 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters); 201 break; 202 } 203 case FilterOperation::INVERT: { 204 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation); 205 ComponentTransferFunction transferFunction; 206 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 207 Vector<float> transferParameters; 208 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount())); 209 transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount())); 210 transferFunction.tableValues = transferParameters; 211 212 ComponentTransferFunction nullFunction; 213 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 214 break; 215 } 216 case FilterOperation::OPACITY: { 217 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation); 218 ComponentTransferFunction transferFunction; 219 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 220 Vector<float> transferParameters; 221 transferParameters.append(0); 222 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount())); 223 transferFunction.tableValues = transferParameters; 224 225 ComponentTransferFunction nullFunction; 226 effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction); 227 break; 228 } 229 case FilterOperation::BRIGHTNESS: { 230 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation); 231 ComponentTransferFunction transferFunction; 232 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 233 transferFunction.slope = narrowPrecisionToFloat(componentTransferOperation->amount()); 234 transferFunction.intercept = 0; 235 236 ComponentTransferFunction nullFunction; 237 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 238 break; 239 } 240 case FilterOperation::CONTRAST: { 241 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation); 242 ComponentTransferFunction transferFunction; 243 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 244 float amount = narrowPrecisionToFloat(componentTransferOperation->amount()); 245 transferFunction.slope = amount; 246 transferFunction.intercept = -0.5 * amount + 0.5; 247 248 ComponentTransferFunction nullFunction; 249 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 250 break; 251 } 252 case FilterOperation::BLUR: { 253 BlurFilterOperation* blurOperation = static_cast<BlurFilterOperation*>(filterOperation); 254 float stdDeviation = floatValueForLength(blurOperation->stdDeviation(), 0) * invZoom; 255 effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation); 256 break; 257 } 258 case FilterOperation::DROP_SHADOW: { 259 DropShadowFilterOperation* dropShadowOperation = static_cast<DropShadowFilterOperation*>(filterOperation); 260 float stdDeviation = dropShadowOperation->stdDeviation() * invZoom; 261 float x = dropShadowOperation->x() * invZoom; 262 float y = dropShadowOperation->y() * invZoom; 263 effect = FEDropShadow::create(this, stdDeviation, stdDeviation, x, y, dropShadowOperation->color(), 1); 264 break; 265 } 266 case FilterOperation::CUSTOM: 267 // CUSTOM operations are always converted to VALIDATED_CUSTOM before getting here. 268 // The conversion happens in RenderLayer::computeFilterOperations. 269 ASSERT_NOT_REACHED(); 270 break; 271 case FilterOperation::VALIDATED_CUSTOM: { 272 ValidatedCustomFilterOperation* customFilterOperation = static_cast<ValidatedCustomFilterOperation*>(filterOperation); 273 Document* document = renderer ? renderer->document() : 0; 274 effect = createCustomFilterEffect(this, document, customFilterOperation); 275 if (effect) 276 m_hasCustomShaderFilter = true; 277 break; 278 } 279 default: 280 break; 281 } 282 283 if (effect) { 284 if (filterOperation->getOperationType() != FilterOperation::REFERENCE) { 285 // Unlike SVG, filters applied here should not clip to their primitive subregions. 286 effect->setClipsToBounds(false); 287 effect->setOperatingColorSpace(ColorSpaceDeviceRGB); 288 effect->inputEffects().append(previousEffect); 289 } 290 previousEffect = effect.release(); 291 } 292 } 293 294 // We need to keep the old effects alive until this point, so that filters like FECustomFilter 295 // can share cached resources across frames. 296 m_lastEffect = previousEffect; 297 298 // If we didn't make any effects, tell our caller we are not valid 299 if (!m_lastEffect.get()) 300 return false; 301 302 return true; 303 } 304 305 bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& filterRect) 306 { 307 if (!filterRect.isZero() && isFilterSizeValid(filterRect)) { 308 FloatRect currentSourceRect = sourceImageRect(); 309 if (filterRect != currentSourceRect) { 310 setSourceImageRect(filterRect); 311 return true; 312 } 313 } 314 return false; 315 } 316 317 void FilterEffectRenderer::allocateBackingStoreIfNeeded() 318 { 319 // At this point the effect chain has been built, and the 320 // source image sizes set. We just need to attach the graphic 321 // buffer if we have not yet done so. 322 if (!m_graphicsBufferAttached) { 323 IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height()); 324 if (!sourceImage() || sourceImage()->logicalSize() != logicalSize) 325 setSourceImage(ImageBuffer::create(logicalSize, 1, renderingMode())); 326 m_graphicsBufferAttached = true; 327 } 328 } 329 330 void FilterEffectRenderer::clearIntermediateResults() 331 { 332 if (m_lastEffect.get()) 333 m_lastEffect->clearResultsRecursive(); 334 } 335 336 void FilterEffectRenderer::apply() 337 { 338 RefPtr<FilterEffect> effect = lastEffect(); 339 effect->apply(); 340 effect->transformResultColorSpace(ColorSpaceDeviceRGB); 341 } 342 343 LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect) 344 { 345 if (hasCustomShaderFilter()) { 346 // When we have at least a custom shader in the chain, we need to compute the whole source image, because the shader can 347 // reference any pixel and we cannot control that. 348 return filterBoxRect; 349 } 350 // The result of this function is the area in the "filterBoxRect" that needs to be repainted, so that we fully cover the "dirtyRect". 351 FloatRect rectForRepaint = dirtyRect; 352 rectForRepaint.move(-filterBoxRect.location().x(), -filterBoxRect.location().y()); 353 float inf = std::numeric_limits<float>::infinity(); 354 FloatRect clipRect = FloatRect(FloatPoint(-inf, -inf), FloatSize(inf, inf)); 355 rectForRepaint = lastEffect()->getSourceRect(rectForRepaint, clipRect); 356 rectForRepaint.move(filterBoxRect.location().x(), filterBoxRect.location().y()); 357 rectForRepaint.intersect(filterBoxRect); 358 return LayoutRect(rectForRepaint); 359 } 360 361 bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect) 362 { 363 ASSERT(m_haveFilterEffect && renderLayer->filterRenderer()); 364 m_renderLayer = renderLayer; 365 m_repaintRect = dirtyRect; 366 367 FilterEffectRenderer* filter = renderLayer->filterRenderer(); 368 LayoutRect filterSourceRect = filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect); 369 370 if (filterSourceRect.isEmpty()) { 371 // The dirty rect is not in view, just bail out. 372 m_haveFilterEffect = false; 373 return false; 374 } 375 376 // Get the zoom factor to scale the filterSourceRect input 377 const RenderLayerModelObject* renderer = renderLayer->renderer(); 378 const RenderStyle* style = renderer ? renderer->style() : 0; 379 float zoom = style ? style->effectiveZoom() : 1.0f; 380 381 AffineTransform absoluteTransform; 382 absoluteTransform.translate(filterBoxRect.x(), filterBoxRect.y()); 383 filter->setAbsoluteTransform(absoluteTransform); 384 filter->setAbsoluteFilterRegion(AffineTransform().scale(zoom).mapRect(filterSourceRect)); 385 filter->setFilterRegion(absoluteTransform.inverse().mapRect(filterSourceRect)); 386 filter->lastEffect()->determineFilterPrimitiveSubregion(); 387 388 bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect); 389 if (filter->hasFilterThatMovesPixels()) { 390 if (hasUpdatedBackingStore) 391 m_repaintRect = filterSourceRect; 392 else { 393 m_repaintRect.unite(layerRepaintRect); 394 m_repaintRect.intersect(filterSourceRect); 395 } 396 } 397 return true; 398 } 399 400 GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* oldContext) 401 { 402 ASSERT(m_renderLayer); 403 404 FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); 405 filter->allocateBackingStoreIfNeeded(); 406 // Paint into the context that represents the SourceGraphic of the filter. 407 GraphicsContext* sourceGraphicsContext = filter->inputContext(); 408 if (!sourceGraphicsContext || !isFilterSizeValid(filter->absoluteFilterRegion())) { 409 // Disable the filters and continue. 410 m_haveFilterEffect = false; 411 return oldContext; 412 } 413 414 m_savedGraphicsContext = oldContext; 415 416 // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer. 417 sourceGraphicsContext->save(); 418 // FIXME: can we just use sourceImageRect for everything, and get rid of 419 // m_repaintRect? 420 FloatPoint offset = filter->sourceImageRect().location(); 421 sourceGraphicsContext->translate(-offset.x(), -offset.y()); 422 sourceGraphicsContext->clearRect(m_repaintRect); 423 sourceGraphicsContext->clip(m_repaintRect); 424 425 return sourceGraphicsContext; 426 } 427 428 GraphicsContext* FilterEffectRendererHelper::applyFilterEffect() 429 { 430 ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer()); 431 FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); 432 filter->inputContext()->restore(); 433 434 filter->apply(); 435 436 // Get the filtered output and draw it in place. 437 m_savedGraphicsContext->drawImageBuffer(filter->output(), filter->outputRect(), CompositeSourceOver); 438 439 filter->clearIntermediateResults(); 440 441 return m_savedGraphicsContext; 442 } 443 444 } // namespace WebCore 445 446