1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkImage.h"
10 #include "include/core/SkString.h"
11 #include "include/effects/SkImageFilters.h"
12 #include "src/core/SkImageFilter_Base.h"
13 #include "src/core/SkReadBuffer.h"
14 #include "src/core/SkSamplingPriv.h"
15 #include "src/core/SkSpecialImage.h"
16 #include "src/core/SkSpecialSurface.h"
17 #include "src/core/SkWriteBuffer.h"
18
19 namespace {
20
21 class SkImageImageFilter final : public SkImageFilter_Base {
22 public:
SkImageImageFilter(sk_sp<SkImage> image,const SkRect & srcRect,const SkRect & dstRect,const SkSamplingOptions & sampling)23 SkImageImageFilter(sk_sp<SkImage> image, const SkRect& srcRect, const SkRect& dstRect,
24 const SkSamplingOptions& sampling)
25 : INHERITED(nullptr, 0, nullptr)
26 , fImage(std::move(image))
27 , fSrcRect(srcRect)
28 , fDstRect(dstRect)
29 , fSampling(sampling) {}
30
31 SkRect computeFastBounds(const SkRect& src) const override;
32
33 protected:
34 void flatten(SkWriteBuffer&) const override;
35
36 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
37
38 SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
39 MapDirection, const SkIRect* inputRect) const override;
40
onGetCTMCapability() const41 MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
42
43 private:
44 friend void ::SkRegisterImageImageFilterFlattenable();
45 SK_FLATTENABLE_HOOKS(SkImageImageFilter)
46
47 sk_sp<SkImage> fImage;
48 SkRect fSrcRect, fDstRect;
49 SkSamplingOptions fSampling;
50
51 using INHERITED = SkImageFilter_Base;
52 };
53
54 } // end namespace
55
Image(sk_sp<SkImage> image,const SkRect & srcRect,const SkRect & dstRect,const SkSamplingOptions & sampling)56 sk_sp<SkImageFilter> SkImageFilters::Image(sk_sp<SkImage> image,
57 const SkRect& srcRect,
58 const SkRect& dstRect,
59 const SkSamplingOptions& sampling) {
60 if (!image || srcRect.width() <= 0.0f || srcRect.height() <= 0.0f) {
61 return nullptr;
62 }
63
64 return sk_sp<SkImageFilter>(new SkImageImageFilter(
65 std::move(image), srcRect, dstRect, sampling));
66 }
67
SkRegisterImageImageFilterFlattenable()68 void SkRegisterImageImageFilterFlattenable() {
69 SK_REGISTER_FLATTENABLE(SkImageImageFilter);
70 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
71 SkFlattenable::Register("SkImageSourceImpl", SkImageImageFilter::CreateProc);
72 }
73
CreateProc(SkReadBuffer & buffer)74 sk_sp<SkFlattenable> SkImageImageFilter::CreateProc(SkReadBuffer& buffer) {
75 SkSamplingOptions sampling;
76 if (buffer.isVersionLT(SkPicturePriv::kImageFilterImageSampling_Version)) {
77 sampling = SkSamplingPriv::FromFQ(buffer.checkFilterQuality(), kLinear_SkMediumAs);
78 } else {
79 sampling = buffer.readSampling();
80 }
81
82 SkRect src, dst;
83 buffer.readRect(&src);
84 buffer.readRect(&dst);
85
86 sk_sp<SkImage> image(buffer.readImage());
87 if (!image) {
88 return nullptr;
89 }
90
91 return SkImageFilters::Image(std::move(image), src, dst, sampling);
92 }
93
flatten(SkWriteBuffer & buffer) const94 void SkImageImageFilter::flatten(SkWriteBuffer& buffer) const {
95 SkSamplingPriv::Write(buffer, fSampling);
96 buffer.writeRect(fSrcRect);
97 buffer.writeRect(fDstRect);
98 buffer.writeImage(fImage.get());
99 }
100
101 ///////////////////////////////////////////////////////////////////////////////////////////////////
102
onFilterImage(const Context & ctx,SkIPoint * offset) const103 sk_sp<SkSpecialImage> SkImageImageFilter::onFilterImage(const Context& ctx,
104 SkIPoint* offset) const {
105 const SkRect dstBounds = ctx.ctm().mapRect(fDstRect);
106 const SkIRect dstIBounds = dstBounds.roundOut();
107
108 // Quick check to see if we can return the image directly, which can be done if the transform
109 // ends up being an integer translate and sampling would have no effect on the output.
110 // TODO: This currently means cubic sampling can be skipped, even though it would change results
111 // for integer translation draws.
112 // TODO: This is prone to false negatives due to the floating point math; we could probably
113 // get away with dimensions and translates being epsilon close to integers.
114 const bool passthroughTransform = ctx.ctm().isScaleTranslate() &&
115 ctx.ctm().getScaleX() > 0.f &&
116 ctx.ctm().getScaleY() > 0.f;
117 const bool passthroughSrcOffsets = SkScalarIsInt(fSrcRect.fLeft) &&
118 SkScalarIsInt(fSrcRect.fTop);
119 const bool passthroughDstOffsets = SkScalarIsInt(dstBounds.fLeft) &&
120 SkScalarIsInt(dstBounds.fTop);
121 const bool passthroughDims =
122 SkScalarIsInt(fSrcRect.width()) && fSrcRect.width() == dstBounds.width() &&
123 SkScalarIsInt(fSrcRect.height()) && fSrcRect.height() == dstBounds.height();
124
125 if (passthroughTransform && passthroughSrcOffsets && passthroughDstOffsets && passthroughDims) {
126 // Can pass through fImage directly, applying the dst's location to 'offset'. If fSrcRect
127 // extends outside of the image, we adjust dst to match since those areas would have been
128 // transparent black anyways.
129 SkIRect srcIBounds = fSrcRect.roundOut();
130 SkIPoint srcOffset = srcIBounds.topLeft();
131 if (!srcIBounds.intersect(SkIRect::MakeWH(fImage->width(), fImage->height()))) {
132 return nullptr;
133 }
134
135 *offset = dstIBounds.topLeft() + srcIBounds.topLeft() - srcOffset;
136 return SkSpecialImage::MakeFromImage(ctx.getContext(), srcIBounds, fImage,
137 ctx.surfaceProps());
138 }
139
140 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstIBounds.size()));
141 if (!surf) {
142 return nullptr;
143 }
144
145 SkCanvas* canvas = surf->getCanvas();
146 // Subtract off the integer component of the translation (will be applied in offset, below).
147 canvas->translate(-dstIBounds.fLeft, -dstIBounds.fTop);
148 canvas->concat(ctx.ctm());
149 // TODO(skbug.com/5075): Canvases from GPU special surfaces come with unitialized content
150 canvas->clear(SK_ColorTRANSPARENT);
151 canvas->drawImageRect(fImage.get(), fSrcRect, fDstRect, fSampling, nullptr,
152 SkCanvas::kStrict_SrcRectConstraint);
153
154 *offset = dstIBounds.topLeft();
155 return surf->makeImageSnapshot();
156 }
157
computeFastBounds(const SkRect & src) const158 SkRect SkImageImageFilter::computeFastBounds(const SkRect& src) const {
159 return fDstRect;
160 }
161
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection direction,const SkIRect * inputRect) const162 SkIRect SkImageImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
163 MapDirection direction,
164 const SkIRect* inputRect) const {
165 if (kReverse_MapDirection == direction) {
166 return INHERITED::onFilterNodeBounds(src, ctm, direction, inputRect);
167 }
168
169 SkRect dstRect = fDstRect;
170 ctm.mapRect(&dstRect);
171 return dstRect.roundOut();
172 }
173