• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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