| /* |
| * 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 |