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