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 "platform/graphics/filters/custom/CustomFilterGlobalContext.h" 49 #include "platform/graphics/filters/custom/CustomFilterValidatedProgram.h" 50 #include "platform/graphics/filters/custom/FECustomFilter.h" 51 #include "platform/graphics/filters/custom/ValidatedCustomFilterOperation.h" 52 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h" 53 #include "wtf/MathExtras.h" 54 #include <algorithm> 55 56 namespace WebCore { 57 58 static inline void endMatrixRow(Vector<float>& parameters) 59 { 60 parameters.append(0); 61 parameters.append(0); 62 } 63 64 static inline void lastMatrixRow(Vector<float>& parameters) 65 { 66 parameters.append(0); 67 parameters.append(0); 68 parameters.append(0); 69 parameters.append(1); 70 parameters.append(0); 71 } 72 73 inline bool isFilterSizeValid(FloatRect rect) 74 { 75 if (rect.width() < 0 || rect.width() > kMaxFilterSize 76 || rect.height() < 0 || rect.height() > kMaxFilterSize) 77 return false; 78 return true; 79 } 80 81 static PassRefPtr<FECustomFilter> createCustomFilterEffect(Filter* filter, Document* document, ValidatedCustomFilterOperation* operation) 82 { 83 if (!document) 84 return 0; 85 86 CustomFilterGlobalContext* globalContext = document->renderView()->customFilterGlobalContext(); 87 globalContext->prepareContextIfNeeded(); 88 if (!globalContext->context()) 89 return 0; 90 91 return FECustomFilter::create(filter, globalContext->context(), operation->validatedProgram(), operation->parameters(), 92 operation->meshRows(), operation->meshColumns(), operation->meshType()); 93 } 94 95 FilterEffectRenderer::FilterEffectRenderer() 96 : Filter(AffineTransform()) 97 , m_graphicsBufferAttached(false) 98 , m_hasFilterThatMovesPixels(false) 99 , m_hasCustomShaderFilter(false) 100 { 101 setFilterResolution(FloatSize(1, 1)); 102 m_sourceGraphic = SourceGraphic::create(this); 103 } 104 105 FilterEffectRenderer::~FilterEffectRenderer() 106 { 107 } 108 109 GraphicsContext* FilterEffectRenderer::inputContext() 110 { 111 return sourceImage() ? sourceImage()->context() : 0; 112 } 113 114 bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations) 115 { 116 m_hasCustomShaderFilter = false; 117 m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels(); 118 119 // Inverse zoom the pre-zoomed CSS shorthand filters, so that they are in the same zoom as the unzoomed reference filters. 120 const RenderStyle* style = renderer->style(); 121 // FIXME: The effects now contain high dpi information, but the software path doesn't (yet) scale its backing. 122 // When the proper dpi dependant backing size is allocated, we should remove deviceScaleFactor(...) here. 123 float invZoom = 1.0f / ((style ? style->effectiveZoom() : 1.0f) * deviceScaleFactor(renderer->frame())); 124 125 RefPtr<FilterEffect> previousEffect = m_sourceGraphic; 126 for (size_t i = 0; i < operations.operations().size(); ++i) { 127 RefPtr<FilterEffect> effect; 128 FilterOperation* filterOperation = operations.operations().at(i).get(); 129 switch (filterOperation->type()) { 130 case FilterOperation::REFERENCE: { 131 effect = ReferenceFilterBuilder::build(this, renderer, previousEffect.get(), toReferenceFilterOperation(filterOperation)); 132 break; 133 } 134 case FilterOperation::GRAYSCALE: { 135 Vector<float> inputParameters; 136 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->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 Vector<float> inputParameters; 163 double oneMinusAmount = clampTo(1 - toBasicColorMatrixFilterOperation(filterOperation)->amount(), 0.0, 1.0); 164 165 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent 166 // for information on parameters. 167 168 inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount)); 169 inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount)); 170 inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount)); 171 endMatrixRow(inputParameters); 172 173 inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount)); 174 inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount)); 175 inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount)); 176 endMatrixRow(inputParameters); 177 178 inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount)); 179 inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount)); 180 inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount)); 181 endMatrixRow(inputParameters); 182 183 lastMatrixRow(inputParameters); 184 185 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters); 186 break; 187 } 188 case FilterOperation::SATURATE: { 189 Vector<float> inputParameters; 190 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount())); 191 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters); 192 break; 193 } 194 case FilterOperation::HUE_ROTATE: { 195 Vector<float> inputParameters; 196 inputParameters.append(narrowPrecisionToFloat(toBasicColorMatrixFilterOperation(filterOperation)->amount())); 197 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters); 198 break; 199 } 200 case FilterOperation::INVERT: { 201 BasicComponentTransferFilterOperation* componentTransferOperation = toBasicComponentTransferFilterOperation(filterOperation); 202 ComponentTransferFunction transferFunction; 203 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 204 Vector<float> transferParameters; 205 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount())); 206 transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount())); 207 transferFunction.tableValues = transferParameters; 208 209 ComponentTransferFunction nullFunction; 210 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 211 break; 212 } 213 case FilterOperation::OPACITY: { 214 ComponentTransferFunction transferFunction; 215 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE; 216 Vector<float> transferParameters; 217 transferParameters.append(0); 218 transferParameters.append(narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount())); 219 transferFunction.tableValues = transferParameters; 220 221 ComponentTransferFunction nullFunction; 222 effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction); 223 break; 224 } 225 case FilterOperation::BRIGHTNESS: { 226 ComponentTransferFunction transferFunction; 227 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 228 transferFunction.slope = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()); 229 transferFunction.intercept = 0; 230 231 ComponentTransferFunction nullFunction; 232 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 233 break; 234 } 235 case FilterOperation::CONTRAST: { 236 ComponentTransferFunction transferFunction; 237 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; 238 float amount = narrowPrecisionToFloat(toBasicComponentTransferFilterOperation(filterOperation)->amount()); 239 transferFunction.slope = amount; 240 transferFunction.intercept = -0.5 * amount + 0.5; 241 242 ComponentTransferFunction nullFunction; 243 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); 244 break; 245 } 246 case FilterOperation::BLUR: { 247 float stdDeviation = floatValueForLength(toBlurFilterOperation(filterOperation)->stdDeviation(), 0) * invZoom; 248 effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation); 249 break; 250 } 251 case FilterOperation::DROP_SHADOW: { 252 DropShadowFilterOperation* dropShadowOperation = toDropShadowFilterOperation(filterOperation); 253 float stdDeviation = dropShadowOperation->stdDeviation() * invZoom; 254 float x = dropShadowOperation->x() * invZoom; 255 float y = dropShadowOperation->y() * invZoom; 256 effect = FEDropShadow::create(this, stdDeviation, stdDeviation, x, y, dropShadowOperation->color(), 1); 257 break; 258 } 259 case FilterOperation::CUSTOM: 260 // CUSTOM operations are always converted to VALIDATED_CUSTOM before getting here. 261 // The conversion happens in RenderLayer::computeFilterOperations. 262 ASSERT_NOT_REACHED(); 263 break; 264 case FilterOperation::VALIDATED_CUSTOM: { 265 Document* document = renderer ? &renderer->document() : 0; 266 effect = createCustomFilterEffect(this, document, toValidatedCustomFilterOperation(filterOperation)); 267 if (effect) 268 m_hasCustomShaderFilter = true; 269 break; 270 } 271 default: 272 break; 273 } 274 275 if (effect) { 276 if (filterOperation->type() != FilterOperation::REFERENCE) { 277 // Unlike SVG, filters applied here should not clip to their primitive subregions. 278 effect->setClipsToBounds(false); 279 effect->setOperatingColorSpace(ColorSpaceDeviceRGB); 280 effect->inputEffects().append(previousEffect); 281 } 282 previousEffect = effect.release(); 283 } 284 } 285 286 // We need to keep the old effects alive until this point, so that filters like FECustomFilter 287 // can share cached resources across frames. 288 m_lastEffect = previousEffect; 289 290 // If we didn't make any effects, tell our caller we are not valid 291 if (!m_lastEffect.get()) 292 return false; 293 294 return true; 295 } 296 297 bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& filterRect) 298 { 299 if (!filterRect.isZero() && isFilterSizeValid(filterRect)) { 300 FloatRect currentSourceRect = sourceImageRect(); 301 if (filterRect != currentSourceRect) { 302 setSourceImageRect(filterRect); 303 return true; 304 } 305 } 306 return false; 307 } 308 309 void FilterEffectRenderer::allocateBackingStoreIfNeeded() 310 { 311 // At this point the effect chain has been built, and the 312 // source image sizes set. We just need to attach the graphic 313 // buffer if we have not yet done so. 314 if (!m_graphicsBufferAttached) { 315 IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height()); 316 if (!sourceImage() || sourceImage()->size() != logicalSize) { 317 OwnPtr<ImageBufferSurface> surface; 318 if (isAccelerated()) { 319 surface = adoptPtr(new AcceleratedImageBufferSurface(logicalSize)); 320 } 321 if (!surface || !surface->isValid()) { 322 surface = adoptPtr(new UnacceleratedImageBufferSurface(logicalSize)); 323 } 324 setSourceImage(ImageBuffer::create(surface.release())); 325 } 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(MapRectForward); 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