2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #if ENABLE(CSS_FILTERS)
30 #include "FilterEffectRenderer.h"
33 #include "FEColorMatrix.h"
34 #include "FEComponentTransfer.h"
35 #include "FEDropShadow.h"
36 #include "FEGaussianBlur.h"
38 #include "FloatConversion.h"
39 #include "RenderLayer.h"
42 #include <wtf/MathExtras.h>
44 #if ENABLE(CSS_SHADERS) && ENABLE(WEBGL)
45 #include "CustomFilterGlobalContext.h"
46 #include "CustomFilterProgram.h"
47 #include "CustomFilterOperation.h"
48 #include "FECustomFilter.h"
49 #include "RenderView.h"
54 #include "CachedSVGDocument.h"
55 #include "SVGElement.h"
56 #include "SVGFilterPrimitiveStandardAttributes.h"
57 #include "SourceAlpha.h"
62 static inline void endMatrixRow(Vector<float>& parameters)
68 static inline void lastMatrixRow(Vector<float>& parameters)
77 inline bool isFilterSizeValid(FloatRect rect)
79 if (rect.width() < 0 || rect.width() > kMaxFilterSize
80 || rect.height() < 0 || rect.height() > kMaxFilterSize)
85 #if ENABLE(CSS_SHADERS) && ENABLE(WEBGL)
86 static bool isCSSCustomFilterEnabled(Document* document)
88 // We only want to enable shaders if WebGL is also enabled on this platform.
89 Settings* settings = document->settings();
90 return settings && settings->isCSSCustomFilterEnabled() && settings->webGLEnabled();
94 FilterEffectRenderer::FilterEffectRenderer()
99 , m_graphicsBufferAttached(false)
100 , m_hasFilterThatMovesPixels(false)
101 #if ENABLE(CSS_SHADERS)
102 , m_hasCustomShaderFilter(false)
105 setFilterResolution(FloatSize(1, 1));
106 m_sourceGraphic = SourceGraphic::create(this);
109 FilterEffectRenderer::~FilterEffectRenderer()
113 GraphicsContext* FilterEffectRenderer::inputContext()
115 return sourceImage() ? sourceImage()->context() : 0;
118 PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(Document* document, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation* op)
121 CachedSVGDocument* cachedSVGDocument = static_cast<CachedSVGDocument*>(op->data());
123 // If we have an SVG document, this is an external reference. Otherwise
124 // we look up the referenced node in the current document.
125 if (cachedSVGDocument)
126 document = cachedSVGDocument->document();
131 Element* filter = document->getElementById(op->fragment());
135 RefPtr<FilterEffect> effect;
137 // FIXME: Figure out what to do with SourceAlpha. Right now, we're
138 // using the alpha of the original input layer, which is obviously
139 // wrong. We should probably be extracting the alpha from the
140 // previousEffect, but this requires some more processing.
141 // This may need a spec clarification.
142 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(previousEffect, SourceAlpha::create(this));
144 for (Node* node = filter->firstChild(); node; node = node->nextSibling()) {
145 if (!node->isSVGElement())
148 SVGElement* element = static_cast<SVGElement*>(node);
149 if (!element->isFilterEffect())
152 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
154 effect = effectElement->build(builder.get(), this);
158 effectElement->setStandardAttributes(effect.get());
159 builder->add(effectElement->result(), effect);
160 m_effects.append(effect);
164 UNUSED_PARAM(document);
165 UNUSED_PARAM(previousEffect);
171 bool FilterEffectRenderer::build(Document* document, const FilterOperations& operations)
173 #if !ENABLE(CSS_SHADERS) || !ENABLE(WEBGL)
174 UNUSED_PARAM(document);
177 #if ENABLE(CSS_SHADERS)
178 m_hasCustomShaderFilter = false;
180 m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels();
181 if (m_hasFilterThatMovesPixels)
182 operations.getOutsets(m_topOutset, m_rightOutset, m_bottomOutset, m_leftOutset);
185 RefPtr<FilterEffect> previousEffect = m_sourceGraphic;
186 for (size_t i = 0; i < operations.operations().size(); ++i) {
187 RefPtr<FilterEffect> effect;
188 FilterOperation* filterOperation = operations.operations().at(i).get();
189 switch (filterOperation->getOperationType()) {
190 case FilterOperation::REFERENCE: {
191 effect = buildReferenceFilter(document, previousEffect, static_cast<ReferenceFilterOperation*>(filterOperation));
194 case FilterOperation::GRAYSCALE: {
195 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
196 Vector<float> inputParameters;
197 double oneMinusAmount = clampTo(1 - colorMatrixOperation->amount(), 0.0, 1.0);
199 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#grayscaleEquivalent
200 // for information on parameters.
202 inputParameters.append(narrowPrecisionToFloat(0.2126 + 0.7874 * oneMinusAmount));
203 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
204 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
205 endMatrixRow(inputParameters);
207 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
208 inputParameters.append(narrowPrecisionToFloat(0.7152 + 0.2848 * oneMinusAmount));
209 inputParameters.append(narrowPrecisionToFloat(0.0722 - 0.0722 * oneMinusAmount));
210 endMatrixRow(inputParameters);
212 inputParameters.append(narrowPrecisionToFloat(0.2126 - 0.2126 * oneMinusAmount));
213 inputParameters.append(narrowPrecisionToFloat(0.7152 - 0.7152 * oneMinusAmount));
214 inputParameters.append(narrowPrecisionToFloat(0.0722 + 0.9278 * oneMinusAmount));
215 endMatrixRow(inputParameters);
217 lastMatrixRow(inputParameters);
219 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
222 case FilterOperation::SEPIA: {
223 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
224 Vector<float> inputParameters;
225 double oneMinusAmount = clampTo(1 - colorMatrixOperation->amount(), 0.0, 1.0);
227 // See https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#sepiaEquivalent
228 // for information on parameters.
230 inputParameters.append(narrowPrecisionToFloat(0.393 + 0.607 * oneMinusAmount));
231 inputParameters.append(narrowPrecisionToFloat(0.769 - 0.769 * oneMinusAmount));
232 inputParameters.append(narrowPrecisionToFloat(0.189 - 0.189 * oneMinusAmount));
233 endMatrixRow(inputParameters);
235 inputParameters.append(narrowPrecisionToFloat(0.349 - 0.349 * oneMinusAmount));
236 inputParameters.append(narrowPrecisionToFloat(0.686 + 0.314 * oneMinusAmount));
237 inputParameters.append(narrowPrecisionToFloat(0.168 - 0.168 * oneMinusAmount));
238 endMatrixRow(inputParameters);
240 inputParameters.append(narrowPrecisionToFloat(0.272 - 0.272 * oneMinusAmount));
241 inputParameters.append(narrowPrecisionToFloat(0.534 - 0.534 * oneMinusAmount));
242 inputParameters.append(narrowPrecisionToFloat(0.131 + 0.869 * oneMinusAmount));
243 endMatrixRow(inputParameters);
245 lastMatrixRow(inputParameters);
247 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_MATRIX, inputParameters);
250 case FilterOperation::SATURATE: {
251 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
252 Vector<float> inputParameters;
253 inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount()));
254 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_SATURATE, inputParameters);
257 case FilterOperation::HUE_ROTATE: {
258 BasicColorMatrixFilterOperation* colorMatrixOperation = static_cast<BasicColorMatrixFilterOperation*>(filterOperation);
259 Vector<float> inputParameters;
260 inputParameters.append(narrowPrecisionToFloat(colorMatrixOperation->amount()));
261 effect = FEColorMatrix::create(this, FECOLORMATRIX_TYPE_HUEROTATE, inputParameters);
264 case FilterOperation::INVERT: {
265 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
266 ComponentTransferFunction transferFunction;
267 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
268 Vector<float> transferParameters;
269 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
270 transferParameters.append(narrowPrecisionToFloat(1 - componentTransferOperation->amount()));
271 transferFunction.tableValues = transferParameters;
273 ComponentTransferFunction nullFunction;
274 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
277 case FilterOperation::OPACITY: {
278 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
279 ComponentTransferFunction transferFunction;
280 transferFunction.type = FECOMPONENTTRANSFER_TYPE_TABLE;
281 Vector<float> transferParameters;
282 transferParameters.append(0);
283 transferParameters.append(narrowPrecisionToFloat(componentTransferOperation->amount()));
284 transferFunction.tableValues = transferParameters;
286 ComponentTransferFunction nullFunction;
287 effect = FEComponentTransfer::create(this, nullFunction, nullFunction, nullFunction, transferFunction);
290 case FilterOperation::BRIGHTNESS: {
291 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
292 ComponentTransferFunction transferFunction;
293 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
294 transferFunction.slope = 1;
295 transferFunction.intercept = narrowPrecisionToFloat(componentTransferOperation->amount());
297 ComponentTransferFunction nullFunction;
298 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
301 case FilterOperation::CONTRAST: {
302 BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation);
303 ComponentTransferFunction transferFunction;
304 transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
305 float amount = narrowPrecisionToFloat(componentTransferOperation->amount());
306 transferFunction.slope = amount;
307 transferFunction.intercept = -0.5 * amount + 0.5;
309 ComponentTransferFunction nullFunction;
310 effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction);
313 case FilterOperation::BLUR: {
314 BlurFilterOperation* blurOperation = static_cast<BlurFilterOperation*>(filterOperation);
315 float stdDeviation = floatValueForLength(blurOperation->stdDeviation(), 0);
316 effect = FEGaussianBlur::create(this, stdDeviation, stdDeviation);
319 case FilterOperation::DROP_SHADOW: {
320 DropShadowFilterOperation* dropShadowOperation = static_cast<DropShadowFilterOperation*>(filterOperation);
321 effect = FEDropShadow::create(this, dropShadowOperation->stdDeviation(), dropShadowOperation->stdDeviation(),
322 dropShadowOperation->x(), dropShadowOperation->y(), dropShadowOperation->color(), 1);
325 #if ENABLE(CSS_SHADERS)
326 case FilterOperation::CUSTOM: {
328 if (!isCSSCustomFilterEnabled(document))
331 CustomFilterOperation* customFilterOperation = static_cast<CustomFilterOperation*>(filterOperation);
332 RefPtr<CustomFilterProgram> program = customFilterOperation->program();
333 if (program->isLoaded()) {
334 CustomFilterGlobalContext* globalContext = document->renderView()->customFilterGlobalContext();
335 globalContext->prepareContextIfNeeded(document->view()->hostWindow());
336 effect = FECustomFilter::create(this, globalContext, program, customFilterOperation->parameters(),
337 customFilterOperation->meshRows(), customFilterOperation->meshColumns(),
338 customFilterOperation->meshBoxType(), customFilterOperation->meshType());
339 m_hasCustomShaderFilter = true;
350 // Unlike SVG, filters applied here should not clip to their primitive subregions.
351 effect->setClipsToBounds(false);
353 if (filterOperation->getOperationType() != FilterOperation::REFERENCE) {
354 effect->inputEffects().append(previousEffect);
355 m_effects.append(effect);
357 previousEffect = effect.release();
361 // If we didn't make any effects, tell our caller we are not valid
362 if (!m_effects.size())
365 setMaxEffectRects(m_sourceDrawingRegion);
370 bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& filterRect)
372 if (!filterRect.isZero() && isFilterSizeValid(filterRect)) {
373 FloatRect currentSourceRect = sourceImageRect();
374 if (filterRect != currentSourceRect) {
375 setSourceImageRect(filterRect);
382 void FilterEffectRenderer::allocateBackingStoreIfNeeded()
384 // At this point the effect chain has been built, and the
385 // source image sizes set. We just need to attach the graphic
386 // buffer if we have not yet done so.
387 if (!m_graphicsBufferAttached) {
388 IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height());
389 if (!sourceImage() || sourceImage()->logicalSize() != logicalSize)
390 setSourceImage(ImageBuffer::create(logicalSize, 1, ColorSpaceDeviceRGB, renderingMode()));
391 m_graphicsBufferAttached = true;
395 void FilterEffectRenderer::clearIntermediateResults()
397 m_sourceGraphic->clearResult();
398 for (size_t i = 0; i < m_effects.size(); ++i)
399 m_effects[i]->clearResult();
402 void FilterEffectRenderer::apply()
404 lastEffect()->apply();
407 LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect)
409 #if ENABLE(CSS_SHADERS)
410 if (hasCustomShaderFilter()) {
411 // When we have at least a custom shader in the chain, we need to compute the whole source image, because the shader can
412 // reference any pixel and we cannot control that.
413 return filterBoxRect;
416 // The result of this function is the area in the "filterBoxRect" that needs to be repainted, so that we fully cover the "dirtyRect".
417 LayoutRect rectForRepaint = dirtyRect;
418 if (hasFilterThatMovesPixels()) {
419 // Note that the outsets are reversed here because we are going backwards -> we have the dirty rect and
420 // need to find out what is the rectangle that might influence the result inside that dirty rect.
421 rectForRepaint.move(-m_rightOutset, -m_bottomOutset);
422 rectForRepaint.expand(m_leftOutset + m_rightOutset, m_topOutset + m_bottomOutset);
424 rectForRepaint.intersect(filterBoxRect);
425 return rectForRepaint;
428 bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
430 ASSERT(m_haveFilterEffect && renderLayer->filterRenderer());
431 m_renderLayer = renderLayer;
432 m_repaintRect = dirtyRect;
434 FilterEffectRenderer* filter = renderLayer->filterRenderer();
435 LayoutRect filterSourceRect = filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect);
436 m_paintOffset = filterSourceRect.location();
438 if (filterSourceRect.isEmpty()) {
439 // The dirty rect is not in view, just bail out.
440 m_haveFilterEffect = false;
444 bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect);
445 if (filter->hasFilterThatMovesPixels()) {
446 if (hasUpdatedBackingStore)
447 m_repaintRect = filterSourceRect;
449 m_repaintRect.unite(layerRepaintRect);
450 m_repaintRect.intersect(filterSourceRect);
456 GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* oldContext)
458 ASSERT(m_renderLayer);
460 FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
461 filter->allocateBackingStoreIfNeeded();
462 // Paint into the context that represents the SourceGraphic of the filter.
463 GraphicsContext* sourceGraphicsContext = filter->inputContext();
464 if (!sourceGraphicsContext || !isFilterSizeValid(filter->filterRegion())) {
465 // Disable the filters and continue.
466 m_haveFilterEffect = false;
470 m_savedGraphicsContext = oldContext;
472 // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer.
473 sourceGraphicsContext->save();
474 sourceGraphicsContext->translate(-m_paintOffset.x(), -m_paintOffset.y());
475 sourceGraphicsContext->clearRect(m_repaintRect);
476 sourceGraphicsContext->clip(m_repaintRect);
478 return sourceGraphicsContext;
481 GraphicsContext* FilterEffectRendererHelper::applyFilterEffect()
483 ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer());
484 FilterEffectRenderer* filter = m_renderLayer->filterRenderer();
485 filter->inputContext()->restore();
489 // Get the filtered output and draw it in place.
490 LayoutRect destRect = filter->outputRect();
491 destRect.move(m_paintOffset.x(), m_paintOffset.y());
493 m_savedGraphicsContext->drawImageBuffer(filter->output(), m_renderLayer->renderer()->style()->colorSpace(), pixelSnappedIntRect(destRect), CompositeSourceOver);
495 filter->clearIntermediateResults();
497 return m_savedGraphicsContext;
500 } // namespace WebCore
502 #endif // ENABLE(CSS_FILTERS)