blob: 49615fde83eed430fe1e5c1578c069b236637354 [file] [log] [blame]
/*
* Copyright (C) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "FilterEffectRendererCoreImage.h"
#if USE(CORE_IMAGE)
#import "FEColorMatrix.h"
#import "FEComponentTransfer.h"
#import "Filter.h"
#import "FilterEffect.h"
#import "FilterOperation.h"
#import "FloatConversion.h"
#import "GraphicsContextCG.h"
#import "ImageBuffer.h"
#import "Logging.h"
#import "SourceGraphic.h"
#import <CoreImage/CIContext.h>
#import <CoreImage/CIFilter.h>
#import <CoreImage/CoreImage.h>
#import <wtf/NeverDestroyed.h>
#import <wtf/cocoa/TypeCastsCocoa.h>
namespace WebCore {
std::unique_ptr<FilterEffectRendererCoreImage> FilterEffectRendererCoreImage::tryCreate(FilterEffect& lastEffect)
{
if (canRenderUsingCIFilters(lastEffect))
return makeUnique<FilterEffectRendererCoreImage>();
return nullptr;
}
RetainPtr<CIContext> FilterEffectRendererCoreImage::sharedCIContext()
{
static NeverDestroyed<RetainPtr<CIContext>> ciContext = [CIContext contextWithOptions:@{ kCIContextWorkingColorSpace: bridge_id_cast(adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB))).get() }];
return ciContext;
}
static bool isNullOrLinearComponentTransferFunction(const FEComponentTransfer& effect)
{
auto isNullOrLinear = [] (const ComponentTransferFunction& function) {
return function.type == FECOMPONENTTRANSFER_TYPE_UNKNOWN
|| function.type == FECOMPONENTTRANSFER_TYPE_LINEAR;
};
return isNullOrLinear(effect.redFunction()) && isNullOrLinear(effect.greenFunction())
&& isNullOrLinear(effect.blueFunction()) && isNullOrLinear(effect.alphaFunction());
}
bool FilterEffectRendererCoreImage::supportsCoreImageRendering(FilterEffect& effect)
{
// FIXME: change return value to true once they are implemented
switch (effect.filterType()) {
case FilterEffect::Type::SourceGraphic:
return true;
case FilterEffect::Type::FEColorMatrix: {
switch (downcast<FEColorMatrix>(effect).type()) {
case FECOLORMATRIX_TYPE_UNKNOWN:
case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
return false;
case FECOLORMATRIX_TYPE_MATRIX:
case FECOLORMATRIX_TYPE_SATURATE:
case FECOLORMATRIX_TYPE_HUEROTATE:
return true;
}
}
case FilterEffect::Type::FEComponentTransfer:
return isNullOrLinearComponentTransferFunction(downcast<FEComponentTransfer>(effect));
default:
return false;
}
return false;
}
void FilterEffectRendererCoreImage::applyEffects(const Filter& filter, FilterEffect& lastEffect)
{
m_outputImage = connectCIFilters(filter, lastEffect);
if (!m_outputImage)
return;
renderToImageBuffer(lastEffect);
}
RetainPtr<CIImage> FilterEffectRendererCoreImage::connectCIFilters(const Filter& filter, FilterEffect& effect)
{
Vector<RetainPtr<CIImage>> inputImages;
for (auto in : effect.inputEffects()) {
auto inputImage = connectCIFilters(filter, *in);
if (!inputImage)
return nullptr;
inputImages.append(inputImage);
}
effect.determineAbsolutePaintRect(filter);
if (effect.absolutePaintRect().isEmpty() || ImageBuffer::sizeNeedsClamping(effect.absolutePaintRect().size()))
return nullptr;
switch (effect.filterType()) {
case FilterEffect::Type::SourceGraphic:
return imageForSourceGraphic(filter);
case FilterEffect::Type::FEColorMatrix:
return imageForFEColorMatrix(downcast<FEColorMatrix>(effect), inputImages);
case FilterEffect::Type::FEComponentTransfer:
return imageForFEComponentTransfer(downcast<FEComponentTransfer>(effect), inputImages);
default:
return nullptr;
}
return nullptr;
}
RetainPtr<CIImage> FilterEffectRendererCoreImage::imageForSourceGraphic(const Filter& filter)
{
ImageBuffer* sourceImage = filter.sourceImage();
if (!sourceImage)
return nullptr;
if (is<IOSurfaceImageBuffer>(*sourceImage))
return [CIImage imageWithIOSurface:downcast<IOSurfaceImageBuffer>(*sourceImage).surface().surface()];
return [CIImage imageWithCGImage:sourceImage->copyNativeImage()->platformImage().get()];
}
RetainPtr<CIImage> FilterEffectRendererCoreImage::imageForFEColorMatrix(const FEColorMatrix& effect, const Vector<RetainPtr<CIImage>>& inputImages)
{
auto inputImage = inputImages.at(0);
auto values = FEColorMatrix::normalizedFloats(effect.values());
float components[9];
switch (effect.type()) {
case FECOLORMATRIX_TYPE_SATURATE:
FEColorMatrix::calculateSaturateComponents(components, values[0]);
break;
case FECOLORMATRIX_TYPE_HUEROTATE:
FEColorMatrix::calculateHueRotateComponents(components, values[0]);
break;
case FECOLORMATRIX_TYPE_MATRIX:
break;
case FECOLORMATRIX_TYPE_UNKNOWN:
case FECOLORMATRIX_TYPE_LUMINANCETOALPHA: // FIXME: Add Luminance to Alpha Implementation
return nullptr;
}
auto *colorMatrixFilter = [CIFilter filterWithName:@"CIColorMatrix"];
[colorMatrixFilter setValue:inputImage.get() forKey:kCIInputImageKey];
switch (effect.type()) {
case FECOLORMATRIX_TYPE_SATURATE:
case FECOLORMATRIX_TYPE_HUEROTATE: {
[colorMatrixFilter setValue:[CIVector vectorWithX:components[0] Y:components[1] Z:components[2] W:0] forKey:@"inputRVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:components[3] Y:components[4] Z:components[5] W:0] forKey:@"inputGVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:components[6] Y:components[7] Z:components[8] W:0] forKey:@"inputBVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:0 Y:0 Z:0 W:1] forKey:@"inputAVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:0 Y:0 Z:0 W:0] forKey:@"inputBiasVector"];
break;
}
case FECOLORMATRIX_TYPE_MATRIX: {
[colorMatrixFilter setValue:[CIVector vectorWithX:values[0] Y:values[1] Z:values[2] W:values[3]] forKey:@"inputRVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:values[5] Y:values[6] Z:values[7] W:values[8]] forKey:@"inputGVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:values[10] Y:values[11] Z:values[12] W:values[13]] forKey:@"inputBVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:values[15] Y:values[16] Z:values[17] W:values[18]] forKey:@"inputAVector"];
[colorMatrixFilter setValue:[CIVector vectorWithX:values[4] Y:values[9] Z:values[14] W:values[19]] forKey:@"inputBiasVector"];
break;
}
case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
case FECOLORMATRIX_TYPE_UNKNOWN:
return nullptr;
}
return colorMatrixFilter.outputImage;
}
RetainPtr<CIImage> FilterEffectRendererCoreImage::imageForFEComponentTransfer(const FEComponentTransfer& effect, Vector<RetainPtr<CIImage>>& inputImages)
{
// FIXME: Implement the rest of FEComponentTransfer functions
ASSERT(isNullOrLinearComponentTransferFunction(effect));
auto inputImage = inputImages.at(0);
auto filter = [CIFilter filterWithName:@"CIColorPolynomial"];
[filter setValue:inputImage.get() forKey:kCIInputImageKey];
auto setCoefficients = [&] (NSString *key, const ComponentTransferFunction& function) {
if (function.type == FECOMPONENTTRANSFER_TYPE_LINEAR)
[filter setValue:[CIVector vectorWithX:function.intercept Y:function.slope Z:0 W:0] forKey:key];
};
setCoefficients(@"inputRedCoefficients", effect.redFunction());
setCoefficients(@"inputGreenCoefficients", effect.greenFunction());
setCoefficients(@"inputBlueCoefficients", effect.blueFunction());
setCoefficients(@"inputAlphaCoefficients", effect.alphaFunction());
return filter.outputImage;
}
bool FilterEffectRendererCoreImage::canRenderUsingCIFilters(FilterEffect& effect)
{
if (!supportsCoreImageRendering(effect))
return false;
for (auto in : effect.inputEffects()) {
if (!supportsCoreImageRendering(*in) || !canRenderUsingCIFilters(*in))
return false;
}
return true;
}
ImageBuffer* FilterEffectRendererCoreImage::output() const
{
LOG_WITH_STREAM(Filters, stream << "Rendering " << this << " using CoreImage\n");
return m_outputImageBuffer.get();
}
void FilterEffectRendererCoreImage::renderToImageBuffer(FilterEffect& lastEffect)
{
FloatSize clampedSize = ImageBuffer::clampedSize(lastEffect.absolutePaintRect().size());
m_outputImageBuffer = IOSurfaceImageBuffer::create(clampedSize, 1, lastEffect.resultColorSpace(), PixelFormat::BGRA8);
if (!m_outputImageBuffer) {
clearResult();
return;
}
[sharedCIContext().get() render: m_outputImage.get() toIOSurface: m_outputImageBuffer->surface().surface() bounds:destRect(lastEffect) colorSpace:lastEffect.resultColorSpace().platformColorSpace()];
}
FloatRect FilterEffectRendererCoreImage::destRect(const FilterEffect& lastEffect) const
{
IntSize destSize = lastEffect.absolutePaintRect().size();
FloatRect destRect = FloatRect(FloatPoint(), destSize);
return destRect;
}
void FilterEffectRendererCoreImage::clearResult()
{
m_outputImageBuffer = nullptr;
m_outputImage = nullptr;
}
FilterEffectRendererCoreImage::FilterEffectRendererCoreImage()
: FilterEffectRenderer()
{
}
} // namespace WebCore
#endif