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 SkSamplingPriv::FromFQ(buffer.read32LE(kLast_SkLegacyFQ), kLinear_SkMediumAs);
50 } else {
51 return SkSamplingPriv::Read(buffer);
52 }
53 }();
54 return Make(matrix, sampling, common.getInput(0));
55 }
56
flatten(SkWriteBuffer & buffer) const57 void SkMatrixImageFilter::flatten(SkWriteBuffer& buffer) const {
58 this->INHERITED::flatten(buffer);
59 buffer.writeMatrix(fTransform);
60 SkSamplingPriv::Write(buffer, fSampling);
61 }
62
63 ///////////////////////////////////////////////////////////////////////////////////////////////////
64
onFilterImage(const Context & ctx,SkIPoint * offset) const65 sk_sp<SkSpecialImage> SkMatrixImageFilter::onFilterImage(const Context& ctx,
66 SkIPoint* offset) const {
67
68 SkIPoint inputOffset = SkIPoint::Make(0, 0);
69 sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
70 if (!input) {
71 return nullptr;
72 }
73
74 SkMatrix matrix;
75 if (!ctx.ctm().invert(&matrix)) {
76 return nullptr;
77 }
78 matrix.postConcat(fTransform);
79 matrix.postConcat(ctx.ctm());
80
81 const SkIRect srcBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
82 input->width(), input->height());
83 const SkRect srcRect = SkRect::Make(srcBounds);
84
85 SkRect dstRect;
86 matrix.mapRect(&dstRect, srcRect);
87 SkIRect dstBounds;
88 dstRect.roundOut(&dstBounds);
89
90 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstBounds.size()));
91 if (!surf) {
92 return nullptr;
93 }
94
95 SkCanvas* canvas = surf->getCanvas();
96 SkASSERT(canvas);
97
98 canvas->clear(0x0);
99
100 canvas->translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y()));
101 canvas->concat(matrix);
102
103 SkPaint paint;
104 paint.setAntiAlias(true);
105 paint.setBlendMode(SkBlendMode::kSrc);
106
107 input->draw(canvas, srcRect.x(), srcRect.y(), fSampling, &paint);
108
109 offset->fX = dstBounds.fLeft;
110 offset->fY = dstBounds.fTop;
111 return surf->makeImageSnapshot();
112 }
113
computeFastBounds(const SkRect & src) const114 SkRect SkMatrixImageFilter::computeFastBounds(const SkRect& src) const {
115 SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
116 SkRect dst;
117 fTransform.mapRect(&dst, bounds);
118 return dst;
119 }
120
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const121 SkIRect SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
122 MapDirection dir, const SkIRect* inputRect) const {
123 SkMatrix matrix;
124 if (!ctm.invert(&matrix)) {
125 return src;
126 }
127 if (kForward_MapDirection == dir) {
128 matrix.postConcat(fTransform);
129 } else {
130 SkMatrix transformInverse;
131 if (!fTransform.invert(&transformInverse)) {
132 return src;
133 }
134 matrix.postConcat(transformInverse);
135 }
136 matrix.postConcat(ctm);
137 SkRect floatBounds;
138 matrix.mapRect(&floatBounds, SkRect::Make(src));
139 SkIRect result = floatBounds.roundOut();
140
141 if (kReverse_MapDirection == dir && SkSamplingOptions() != fSampling) {
142 // When filtering we might need some pixels in the source that might be otherwise
143 // clipped off.
144 result.outset(1, 1);
145 }
146
147 return result;
148 }
149