blob: fb9799439aa6f523357e17f6de27cf8e5ade70ff [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/effects/SkImageFilters.h"
#include "include/core/SkFlattenable.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSamplingOptions.h"
#include "include/private/base/SkAssert.h"
#include "src/core/SkImageFilterTypes.h"
#include "src/core/SkImageFilter_Base.h"
#include "src/core/SkPicturePriv.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkSamplingPriv.h"
#include "src/core/SkWriteBuffer.h"
#include <optional>
#include <utility>
namespace {
class SkImageImageFilter final : public SkImageFilter_Base {
public:
SkImageImageFilter(sk_sp<SkImage> image,
const SkRect& srcRect,
const SkRect& dstRect,
const SkSamplingOptions& sampling)
: SkImageFilter_Base(nullptr, 0)
, fImage(std::move(image))
, fSrcRect(srcRect)
, fDstRect(dstRect)
, fSampling(sampling) {
// The dst rect should be non-empty
SkASSERT(fImage && !dstRect.isEmpty());
}
SkRect computeFastBounds(const SkRect&) const override { return SkRect(fDstRect); }
protected:
void flatten(SkWriteBuffer&) const override;
private:
friend void ::SkRegisterImageImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkImageImageFilter)
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
skif::FilterResult onFilterImage(const skif::Context&) const override;
skif::LayerSpace<SkIRect> onGetInputLayerBounds(
const skif::Mapping& mapping,
const skif::LayerSpace<SkIRect>& desiredOutput,
std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
const skif::Mapping& mapping,
std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
sk_sp<SkImage> fImage;
// The src rect is relative to the image's contents, so is not technically in the parameter
// coordinate space that responds to the layer matrix (unlike fDstRect).
SkRect fSrcRect;
skif::ParameterSpace<SkRect> fDstRect;
SkSamplingOptions fSampling;
};
} // end namespace
sk_sp<SkImageFilter> SkImageFilters::Image(sk_sp<SkImage> image,
const SkRect& srcRect,
const SkRect& dstRect,
const SkSamplingOptions& sampling) {
if (srcRect.isEmpty() || dstRect.isEmpty() || !image) {
// There is no content to draw, so the filter should produce transparent black
return SkImageFilters::Empty();
} else {
SkRect imageBounds = SkRect::Make(image->dimensions());
if (imageBounds.contains(srcRect)) {
// No change to srcRect and dstRect needed
return sk_sp<SkImageFilter>(new SkImageImageFilter(
std::move(image), srcRect, dstRect, sampling));
} else {
SkMatrix srcToDst = SkMatrix::RectToRect(srcRect, dstRect);
if (!imageBounds.intersect(srcRect)) {
// No overlap, so draw empty
return SkImageFilters::Empty();
}
// Adjust dstRect to match the updated src (which is stored in imageBounds)
SkRect mappedBounds = srcToDst.mapRect(imageBounds);
if (mappedBounds.isEmpty()) {
return SkImageFilters::Empty();
}
return sk_sp<SkImageFilter>(new SkImageImageFilter(
std::move(image), imageBounds, mappedBounds, sampling));
}
}
}
void SkRegisterImageImageFilterFlattenable() {
SK_REGISTER_FLATTENABLE(SkImageImageFilter);
// TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
SkFlattenable::Register("SkImageSourceImpl", SkImageImageFilter::CreateProc);
}
sk_sp<SkFlattenable> SkImageImageFilter::CreateProc(SkReadBuffer& buffer) {
SkSamplingOptions sampling;
if (buffer.isVersionLT(SkPicturePriv::kImageFilterImageSampling_Version)) {
sampling = SkSamplingPriv::FromFQ(buffer.checkFilterQuality(), kLinear_SkMediumAs);
} else {
sampling = buffer.readSampling();
}
SkRect src, dst;
buffer.readRect(&src);
buffer.readRect(&dst);
sk_sp<SkImage> image(buffer.readImage());
if (!image) {
return nullptr;
}
return SkImageFilters::Image(std::move(image), src, dst, sampling);
}
void SkImageImageFilter::flatten(SkWriteBuffer& buffer) const {
buffer.writeSampling(fSampling);
buffer.writeRect(fSrcRect);
buffer.writeRect(SkRect(fDstRect));
buffer.writeImage(fImage.get());
}
///////////////////////////////////////////////////////////////////////////////////////////////////
skif::FilterResult SkImageImageFilter::onFilterImage(const skif::Context& ctx) const {
return skif::FilterResult::MakeFromImage(ctx, fImage, fSrcRect, fDstRect, fSampling);
}
skif::LayerSpace<SkIRect> SkImageImageFilter::onGetInputLayerBounds(
const skif::Mapping&,
const skif::LayerSpace<SkIRect>&,
std::optional<skif::LayerSpace<SkIRect>>) const {
// This is a leaf filter, it requires no input and no further recursion
return skif::LayerSpace<SkIRect>::Empty();
}
std::optional<skif::LayerSpace<SkIRect>> SkImageImageFilter::onGetOutputLayerBounds(
const skif::Mapping& mapping,
std::optional<skif::LayerSpace<SkIRect>>) const {
// The output is the transformed bounds of the image.
return mapping.paramToLayer(fDstRect).roundOut();
}