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 "src/core/SkReadBuffer.h"
13#include "src/core/SkSpecialImage.h"
14#include "src/core/SkSpecialSurface.h"
15#include "src/core/SkWriteBuffer.h"
16
17SkMatrixImageFilter::SkMatrixImageFilter(const SkMatrix& transform,
18 SkFilterQuality filterQuality,
19 sk_sp<SkImageFilter> input)
20 : INHERITED(&input, 1, nullptr)
21 , fTransform(transform)
22 , fFilterQuality(filterQuality) {
23}
24
25sk_sp<SkImageFilter> SkMatrixImageFilter::Make(const SkMatrix& transform,
26 SkFilterQuality filterQuality,
27 sk_sp<SkImageFilter> input) {
28 return sk_sp<SkImageFilter>(new SkMatrixImageFilter(transform,
29 filterQuality,
30 std::move(input)));
31}
32
33sk_sp<SkFlattenable> SkMatrixImageFilter::CreateProc(SkReadBuffer& buffer) {
34 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
35 SkMatrix matrix;
36 buffer.readMatrix(&matrix);
37
38 return Make(matrix, buffer.read32LE(kLast_SkFilterQuality), common.getInput(0));
39}
40
41void SkMatrixImageFilter::flatten(SkWriteBuffer& buffer) const {
42 this->INHERITED::flatten(buffer);
43 buffer.writeMatrix(fTransform);
44 buffer.writeInt(fFilterQuality);
45}
46
47sk_sp<SkSpecialImage> SkMatrixImageFilter::onFilterImage(const Context& ctx,
48 SkIPoint* offset) const {
49
50 SkIPoint inputOffset = SkIPoint::Make(0, 0);
51 sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
52 if (!input) {
53 return nullptr;
54 }
55
56 SkMatrix matrix;
57 if (!ctx.ctm().invert(&matrix)) {
58 return nullptr;
59 }
60 matrix.postConcat(fTransform);
61 matrix.postConcat(ctx.ctm());
62
63 const SkIRect srcBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
64 input->width(), input->height());
65 const SkRect srcRect = SkRect::Make(srcBounds);
66
67 SkRect dstRect;
68 matrix.mapRect(&dstRect, srcRect);
69 SkIRect dstBounds;
70 dstRect.roundOut(&dstBounds);
71
72 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstBounds.size()));
73 if (!surf) {
74 return nullptr;
75 }
76
77 SkCanvas* canvas = surf->getCanvas();
78 SkASSERT(canvas);
79
80 canvas->clear(0x0);
81
82 canvas->translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y()));
83 canvas->concat(matrix);
84
85 SkPaint paint;
86 paint.setAntiAlias(true);
87 paint.setBlendMode(SkBlendMode::kSrc);
88 paint.setFilterQuality(fFilterQuality);
89
90 input->draw(canvas, srcRect.x(), srcRect.y(), &paint);
91
92 offset->fX = dstBounds.fLeft;
93 offset->fY = dstBounds.fTop;
94 return surf->makeImageSnapshot();
95}
96
97SkRect SkMatrixImageFilter::computeFastBounds(const SkRect& src) const {
98 SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
99 SkRect dst;
100 fTransform.mapRect(&dst, bounds);
101 return dst;
102}
103
104SkIRect SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
105 MapDirection dir, const SkIRect* inputRect) const {
106 SkMatrix matrix;
107 if (!ctm.invert(&matrix)) {
108 return src;
109 }
110 if (kForward_MapDirection == dir) {
111 matrix.postConcat(fTransform);
112 } else {
113 SkMatrix transformInverse;
114 if (!fTransform.invert(&transformInverse)) {
115 return src;
116 }
117 matrix.postConcat(transformInverse);
118 }
119 matrix.postConcat(ctm);
120 SkRect floatBounds;
121 matrix.mapRect(&floatBounds, SkRect::Make(src));
122 SkIRect result = floatBounds.roundOut();
123
124 if (kReverse_MapDirection == dir && kNone_SkFilterQuality != fFilterQuality) {
125 // When filtering we might need some pixels in the source that might be otherwise
126 // clipped off.
127 result.outset(1, 1);
128 }
129
130 return result;
131}
132