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