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 "SkMatrixImageFilter.h"
9
10#include "SkCanvas.h"
11#include "SkReadBuffer.h"
12#include "SkSpecialImage.h"
13#include "SkSpecialSurface.h"
14#include "SkWriteBuffer.h"
15#include "SkRect.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    SkFilterQuality quality = static_cast<SkFilterQuality>(buffer.readInt());
38    return Make(matrix, quality, 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(SkSpecialImage* source,
48                                                         const Context& ctx,
49                                                         SkIPoint* offset) const {
50
51    SkIPoint inputOffset = SkIPoint::Make(0, 0);
52    sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
53    if (!input) {
54        return nullptr;
55    }
56
57    SkMatrix matrix;
58    if (!ctx.ctm().invert(&matrix)) {
59        return nullptr;
60    }
61    matrix.postConcat(fTransform);
62    matrix.postConcat(ctx.ctm());
63
64    const SkIRect srcBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
65                                                input->width(), input->height());
66    const SkRect srcRect = SkRect::Make(srcBounds);
67
68    SkRect dstRect;
69    matrix.mapRect(&dstRect, srcRect);
70    SkIRect dstBounds;
71    dstRect.roundOut(&dstBounds);
72
73    sk_sp<SkSpecialSurface> surf(input->makeSurface(ctx.outputProperties(), dstBounds.size()));
74    if (!surf) {
75        return nullptr;
76    }
77
78    SkCanvas* canvas = surf->getCanvas();
79    SkASSERT(canvas);
80
81    canvas->clear(0x0);
82
83    canvas->translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y()));
84    canvas->concat(matrix);
85
86    SkPaint paint;
87    paint.setAntiAlias(true);
88    paint.setBlendMode(SkBlendMode::kSrc);
89    paint.setFilterQuality(fFilterQuality);
90
91    input->draw(canvas, srcRect.x(), srcRect.y(), &paint);
92
93    offset->fX = dstBounds.fLeft;
94    offset->fY = dstBounds.fTop;
95    return surf->makeImageSnapshot();
96}
97
98SkRect SkMatrixImageFilter::computeFastBounds(const SkRect& src) const {
99    SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
100    SkRect dst;
101    fTransform.mapRect(&dst, bounds);
102    return dst;
103}
104
105SkIRect SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
106                                                MapDirection direction) const {
107    SkMatrix matrix;
108    if (!ctm.invert(&matrix)) {
109        return src;
110    }
111    if (kForward_MapDirection == direction) {
112        matrix.postConcat(fTransform);
113    } else {
114        SkMatrix transformInverse;
115        if (!fTransform.invert(&transformInverse)) {
116            return src;
117        }
118        matrix.postConcat(transformInverse);
119    }
120    matrix.postConcat(ctm);
121    SkRect floatBounds;
122    matrix.mapRect(&floatBounds, SkRect::Make(src));
123    return floatBounds.roundOut();
124}
125
126#ifndef SK_IGNORE_TO_STRING
127void SkMatrixImageFilter::toString(SkString* str) const {
128    str->appendf("SkMatrixImageFilter: (");
129
130    str->appendf("transform: (%f %f %f %f %f %f %f %f %f)",
131                 fTransform[SkMatrix::kMScaleX],
132                 fTransform[SkMatrix::kMSkewX],
133                 fTransform[SkMatrix::kMTransX],
134                 fTransform[SkMatrix::kMSkewY],
135                 fTransform[SkMatrix::kMScaleY],
136                 fTransform[SkMatrix::kMTransY],
137                 fTransform[SkMatrix::kMPersp0],
138                 fTransform[SkMatrix::kMPersp1],
139                 fTransform[SkMatrix::kMPersp2]);
140
141    str->append("<dt>FilterLevel:</dt><dd>");
142    static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
143    str->append(gFilterLevelStrings[fFilterQuality]);
144    str->append("</dd>");
145
146    str->appendf(")");
147}
148#endif
149