blob: c120ffb39623d9ae081c68f222ad524276c6d955 [file] [log] [blame]
/*
* Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
* Copyright (C) 2012 University of Szeged
* Copyright (C) 2015-2022 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "FilterEffect.h"
#include "Filter.h"
#include "FilterEffectApplier.h"
#include "FilterEffectGeometry.h"
#include "FilterResults.h"
#include "ImageBuffer.h"
#include "Logging.h"
#include <wtf/text/TextStream.h>
namespace WebCore {
FilterImageVector FilterEffect::takeImageInputs(FilterImageVector& stack) const
{
unsigned inputsSize = numberOfImageInputs();
ASSERT(stack.size() >= inputsSize);
if (!inputsSize)
return { };
Vector<Ref<FilterImage>> inputs;
inputs.reserveInitialCapacity(inputsSize);
for (; inputsSize; --inputsSize)
inputs.uncheckedAppend(stack.takeLast());
return inputs;
}
FloatRect FilterEffect::calculatePrimitiveSubregion(const Filter& filter, const FilterImageVector& inputs, const std::optional<FilterEffectGeometry>& geometry) const
{
// This function implements https://www.w3.org/TR/filter-effects-1/#FilterPrimitiveSubRegion.
FloatRect primitiveSubregion;
// If there is no input effects, take the effect boundaries as unite rect. Don't use the input's subregion for FETile.
if (!inputs.isEmpty() && filterType() != FilterEffect::Type::FETile) {
for (auto& input : inputs)
primitiveSubregion.unite(input->primitiveSubregion());
} else
primitiveSubregion = filter.filterRegion();
// Clip the primitive subregion to the effect geometry.
if (geometry) {
if (auto x = geometry->x())
primitiveSubregion.setX(*x);
if (auto y = geometry->y())
primitiveSubregion.setY(*y);
if (auto width = geometry->width())
primitiveSubregion.setWidth(*width);
if (auto height = geometry->height())
primitiveSubregion.setHeight(*height);
}
return primitiveSubregion;
}
FloatRect FilterEffect::calculateImageRect(const Filter& filter, const FilterImageVector& inputs, const FloatRect& primitiveSubregion) const
{
FloatRect imageRect;
for (auto& input : inputs)
imageRect.unite(input->imageRect());
return filter.clipToMaxEffectRect(imageRect, primitiveSubregion);
}
std::unique_ptr<FilterEffectApplier> FilterEffect::createApplier(const Filter& filter) const
{
if (filter.renderingMode() == RenderingMode::Accelerated)
return createAcceleratedApplier();
return createSoftwareApplier();
}
void FilterEffect::transformInputsColorSpace(const FilterImageVector& inputs) const
{
for (auto& input : inputs)
input->transformToColorSpace(operatingColorSpace());
}
void FilterEffect::correctPremultipliedInputs(const FilterImageVector& inputs) const
{
// Correct any invalid pixels, if necessary, in the result of a filter operation.
// This method is used to ensure valid pixel values on filter inputs and the final result.
// Only the arithmetic composite filter ever needs to perform correction.
for (auto& input : inputs)
input->correctPremultipliedPixelBuffer();
}
RefPtr<FilterImage> FilterEffect::apply(const Filter& filter, FilterImage& input, FilterResults& results)
{
return apply(filter, FilterImageVector { Ref { input } }, results);
}
RefPtr<FilterImage> FilterEffect::apply(const Filter& filter, const FilterImageVector& inputs, FilterResults& results, const std::optional<FilterEffectGeometry>& geometry)
{
ASSERT(inputs.size() == numberOfImageInputs());
if (auto result = results.effectResult(*this))
return result;
auto primitiveSubregion = calculatePrimitiveSubregion(filter, inputs, geometry);
auto imageRect = calculateImageRect(filter, inputs, primitiveSubregion);
auto absoluteImageRect = enclosingIntRect(filter.scaledByFilterScale(imageRect));
if (absoluteImageRect.isEmpty() || ImageBuffer::sizeNeedsClamping(absoluteImageRect.size()))
return nullptr;
auto isAlphaImage = resultIsAlphaImage(inputs);
auto isValidPremultiplied = resultIsValidPremultiplied();
auto imageColorSpace = resultColorSpace(inputs);
auto applier = createApplier(filter);
if (!applier)
return nullptr;
auto result = FilterImage::create(primitiveSubregion, imageRect, absoluteImageRect, isAlphaImage, isValidPremultiplied, filter.renderingMode(), imageColorSpace, results.allocator());
if (!result)
return nullptr;
LOG_WITH_STREAM(Filters, stream
<< "FilterEffect " << filterName() << " " << this << " apply():"
<< "\n filterPrimitiveSubregion " << primitiveSubregion
<< "\n absolutePaintRect " << absoluteImageRect
<< "\n maxEffectRect " << filter.maxEffectRect(primitiveSubregion)
<< "\n filter scale " << filter.filterScale());
transformInputsColorSpace(inputs);
if (isValidPremultiplied)
correctPremultipliedInputs(inputs);
if (!applier->apply(filter, inputs, *result))
return nullptr;
results.setEffectResult(*this, inputs, { *result });
return result;
}
TextStream& FilterEffect::externalRepresentation(TextStream& ts, FilterRepresentation representation) const
{
// FIXME: We should dump the subRegions of the filter primitives here later. This isn't
// possible at the moment, because we need more detailed informations from the target object.
if (representation == FilterRepresentation::Debugging) {
TextStream::IndentScope indentScope(ts);
ts.dumpProperty("operating colorspace", operatingColorSpace());
ts << "\n" << indent;
}
return ts;
}
TextStream& operator<<(TextStream& ts, const FilterEffect& effect)
{
// Use a new stream because we want multiline mode for logging filters.
TextStream filterStream;
effect.externalRepresentation(filterStream, FilterRepresentation::Debugging);
return ts << filterStream.release();
}
} // namespace WebCore