• 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/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