1 /*
2  * Copyright 2014 The Android Open Source Project
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 "src/core/SkMatrixImageFilter.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkRect.h"
12 #include "include/effects/SkImageFilters.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 
SkMatrixImageFilter(const SkMatrix & transform,const SkSamplingOptions & sampling,sk_sp<SkImageFilter> input)19 SkMatrixImageFilter::SkMatrixImageFilter(const SkMatrix& transform,
20                                          const SkSamplingOptions& sampling,
21                                          sk_sp<SkImageFilter> input)
22     : INHERITED(&input, 1, nullptr)
23     , fTransform(transform)
24     , fSampling(sampling) {
25     // Pre-cache so future calls to fTransform.getType() are threadsafe.
26     (void)fTransform.getType();
27 }
28 
Make(const SkMatrix & transform,const SkSamplingOptions & sampling,sk_sp<SkImageFilter> input)29 sk_sp<SkImageFilter> SkMatrixImageFilter::Make(const SkMatrix& transform,
30                                                const SkSamplingOptions& sampling,
31                                                sk_sp<SkImageFilter> input) {
32     return sk_sp<SkImageFilter>(new SkMatrixImageFilter(transform,
33                                                         sampling,
34                                                         std::move(input)));
35 }
36 
MatrixTransform(const SkMatrix & transform,const SkSamplingOptions & sampling,sk_sp<SkImageFilter> input)37 sk_sp<SkImageFilter> SkImageFilters::MatrixTransform(
38         const SkMatrix& transform, const SkSamplingOptions& sampling, sk_sp<SkImageFilter> input) {
39     return SkMatrixImageFilter::Make(transform, sampling, std::move(input));
40 }
41 
CreateProc(SkReadBuffer & buffer)42 sk_sp<SkFlattenable> SkMatrixImageFilter::CreateProc(SkReadBuffer& buffer) {
43     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
44     SkMatrix matrix;
45     buffer.readMatrix(&matrix);
46 
47     auto sampling = [&]() {
48         if (buffer.isVersionLT(SkPicturePriv::kMatrixImageFilterSampling_Version)) {
49             return SkSamplingOptions(buffer.read32LE(kLast_SkFilterQuality),
50                                      SkSamplingOptions::kMedium_asMipmapLinear);
51         } else {
52             return SkSamplingPriv::Read(buffer);
53         }
54     }();
55     return Make(matrix, sampling, common.getInput(0));
56 }
57 
flatten(SkWriteBuffer & buffer) const58 void SkMatrixImageFilter::flatten(SkWriteBuffer& buffer) const {
59     this->INHERITED::flatten(buffer);
60     buffer.writeMatrix(fTransform);
61     SkSamplingPriv::Write(buffer, fSampling);
62 }
63 
64 ///////////////////////////////////////////////////////////////////////////////////////////////////
65 
onFilterImage(const Context & ctx,SkIPoint * offset) const66 sk_sp<SkSpecialImage> SkMatrixImageFilter::onFilterImage(const Context& ctx,
67                                                          SkIPoint* offset) const {
68 
69     SkIPoint inputOffset = SkIPoint::Make(0, 0);
70     sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
71     if (!input) {
72         return nullptr;
73     }
74 
75     SkMatrix matrix;
76     if (!ctx.ctm().invert(&matrix)) {
77         return nullptr;
78     }
79     matrix.postConcat(fTransform);
80     matrix.postConcat(ctx.ctm());
81 
82     const SkIRect srcBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
83                                                 input->width(), input->height());
84     const SkRect srcRect = SkRect::Make(srcBounds);
85 
86     SkRect dstRect;
87     matrix.mapRect(&dstRect, srcRect);
88     SkIRect dstBounds;
89     dstRect.roundOut(&dstBounds);
90 
91     sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstBounds.size()));
92     if (!surf) {
93         return nullptr;
94     }
95 
96     SkCanvas* canvas = surf->getCanvas();
97     SkASSERT(canvas);
98 
99     canvas->clear(0x0);
100 
101     canvas->translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y()));
102     canvas->concat(matrix);
103 
104     SkPaint paint;
105     paint.setAntiAlias(true);
106     paint.setBlendMode(SkBlendMode::kSrc);
107 
108     input->draw(canvas, srcRect.x(), srcRect.y(), fSampling, &paint);
109 
110     offset->fX = dstBounds.fLeft;
111     offset->fY = dstBounds.fTop;
112     return surf->makeImageSnapshot();
113 }
114 
computeFastBounds(const SkRect & src) const115 SkRect SkMatrixImageFilter::computeFastBounds(const SkRect& src) const {
116     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
117     SkRect dst;
118     fTransform.mapRect(&dst, bounds);
119     return dst;
120 }
121 
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const122 SkIRect SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
123                                                 MapDirection dir, const SkIRect* inputRect) const {
124     SkMatrix matrix;
125     if (!ctm.invert(&matrix)) {
126         return src;
127     }
128     if (kForward_MapDirection == dir) {
129         matrix.postConcat(fTransform);
130     } else {
131         SkMatrix transformInverse;
132         if (!fTransform.invert(&transformInverse)) {
133             return src;
134         }
135         matrix.postConcat(transformInverse);
136     }
137     matrix.postConcat(ctm);
138     SkRect floatBounds;
139     matrix.mapRect(&floatBounds, SkRect::Make(src));
140     SkIRect result = floatBounds.roundOut();
141 
142     if (kReverse_MapDirection == dir && SkSamplingOptions() != fSampling) {
143         // When filtering we might need some pixels in the source that might be otherwise
144         // clipped off.
145         result.outset(1, 1);
146     }
147 
148     return result;
149 }
150