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#include "SkBitmap.h"
10#include "SkCanvas.h"
11#include "SkDevice.h"
12#include "SkColorPriv.h"
13#include "SkReadBuffer.h"
14#include "SkWriteBuffer.h"
15#include "SkMatrix.h"
16#include "SkRect.h"
17
18SkMatrixImageFilter::SkMatrixImageFilter(const SkMatrix& transform,
19                                         SkPaint::FilterLevel filterLevel,
20                                         SkImageFilter* input,
21                                         uint32_t uniqueID)
22  : INHERITED(1, &input, NULL, uniqueID),
23    fTransform(transform),
24    fFilterLevel(filterLevel) {
25}
26
27SkMatrixImageFilter* SkMatrixImageFilter::Create(const SkMatrix& transform,
28                                                 SkPaint::FilterLevel filterLevel,
29                                                 SkImageFilter* input,
30                                                 uint32_t uniqueID) {
31    return SkNEW_ARGS(SkMatrixImageFilter, (transform, filterLevel, input, uniqueID));
32}
33
34#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
35SkMatrixImageFilter::SkMatrixImageFilter(SkReadBuffer& buffer)
36  : INHERITED(1, buffer) {
37    buffer.readMatrix(&fTransform);
38    fFilterLevel = static_cast<SkPaint::FilterLevel>(buffer.readInt());
39}
40#endif
41
42SkFlattenable* SkMatrixImageFilter::CreateProc(SkReadBuffer& buffer) {
43    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
44    SkMatrix matrix;
45    buffer.readMatrix(&matrix);
46    SkPaint::FilterLevel level = static_cast<SkPaint::FilterLevel>(buffer.readInt());
47    return Create(matrix, level, common.getInput(0), common.uniqueID());
48}
49
50void SkMatrixImageFilter::flatten(SkWriteBuffer& buffer) const {
51    this->INHERITED::flatten(buffer);
52    buffer.writeMatrix(fTransform);
53    buffer.writeInt(fFilterLevel);
54}
55
56SkMatrixImageFilter::~SkMatrixImageFilter() {
57}
58
59bool SkMatrixImageFilter::onFilterImage(Proxy* proxy,
60                                        const SkBitmap& source,
61                                        const Context& ctx,
62                                        SkBitmap* result,
63                                        SkIPoint* offset) const {
64    SkBitmap src = source;
65    SkIPoint srcOffset = SkIPoint::Make(0, 0);
66    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
67        return false;
68    }
69
70    SkRect dstRect;
71    SkIRect srcBounds, dstBounds;
72    src.getBounds(&srcBounds);
73    srcBounds.offset(srcOffset);
74    SkRect srcRect = SkRect::Make(srcBounds);
75    SkMatrix matrix;
76    if (!ctx.ctm().invert(&matrix)) {
77        return false;
78    }
79    matrix.postConcat(fTransform);
80    matrix.postConcat(ctx.ctm());
81    matrix.mapRect(&dstRect, srcRect);
82    dstRect.roundOut(&dstBounds);
83
84    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height()));
85    if (NULL == device.get()) {
86        return false;
87    }
88
89    SkCanvas canvas(device.get());
90    canvas.translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y()));
91    canvas.concat(matrix);
92    SkPaint paint;
93
94    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
95    paint.setFilterLevel(fFilterLevel);
96    canvas.drawBitmap(src, srcRect.x(), srcRect.y(), &paint);
97
98    *result = device.get()->accessBitmap(false);
99    offset->fX = dstBounds.fLeft;
100    offset->fY = dstBounds.fTop;
101    return true;
102}
103
104void SkMatrixImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
105    SkRect bounds = src;
106    if (getInput(0)) {
107        getInput(0)->computeFastBounds(src, &bounds);
108    }
109    SkMatrix matrix;
110    matrix.setTranslate(-bounds.x(), -bounds.y());
111    matrix.postConcat(fTransform);
112    matrix.postTranslate(bounds.x(), bounds.y());
113    matrix.mapRect(dst, bounds);
114}
115
116bool SkMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
117                                         SkIRect* dst) const {
118    SkMatrix transformInverse;
119    if (!fTransform.invert(&transformInverse)) {
120        return false;
121    }
122    SkMatrix matrix;
123    if (!ctm.invert(&matrix)) {
124        return false;
125    }
126    matrix.postConcat(transformInverse);
127    matrix.postConcat(ctm);
128    SkRect floatBounds;
129    matrix.mapRect(&floatBounds, SkRect::Make(src));
130    SkIRect bounds;
131    floatBounds.roundOut(&bounds);
132    if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
133        return false;
134    }
135
136    *dst = bounds;
137    return true;
138}
139