1/*
2 * Copyright 2013 Google Inc.
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 "SkDropShadowImageFilter.h"
9
10#include "SkBitmap.h"
11#include "SkBlurImageFilter.h"
12#include "SkCanvas.h"
13#include "SkColorMatrixFilter.h"
14#include "SkDevice.h"
15#include "SkReadBuffer.h"
16#include "SkWriteBuffer.h"
17
18SkDropShadowImageFilter::SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigma,
19                                                 SkColor color, SkImageFilter* input)
20    : INHERITED(input)
21    , fDx(dx)
22    , fDy(dy)
23    , fSigmaX(sigma)
24    , fSigmaY(sigma)
25    , fColor(color)
26{
27}
28
29SkDropShadowImageFilter::SkDropShadowImageFilter(SkScalar dx, SkScalar dy,
30                                                 SkScalar sigmaX, SkScalar sigmaY, SkColor color,
31                                                 SkImageFilter* input, const CropRect* cropRect)
32    : INHERITED(input, cropRect)
33    , fDx(dx)
34    , fDy(dy)
35    , fSigmaX(sigmaX)
36    , fSigmaY(sigmaY)
37    , fColor(color)
38{
39}
40
41SkDropShadowImageFilter::SkDropShadowImageFilter(SkReadBuffer& buffer)
42 : INHERITED(1, buffer) {
43    fDx = buffer.readScalar();
44    fDy = buffer.readScalar();
45    fSigmaX = buffer.readScalar();
46    fSigmaY = buffer.readScalar();
47    fColor = buffer.readColor();
48    buffer.validate(SkScalarIsFinite(fDx) &&
49                    SkScalarIsFinite(fDy) &&
50                    SkScalarIsFinite(fSigmaX) &&
51                    SkScalarIsFinite(fSigmaY));
52}
53
54void SkDropShadowImageFilter::flatten(SkWriteBuffer& buffer) const
55{
56    this->INHERITED::flatten(buffer);
57    buffer.writeScalar(fDx);
58    buffer.writeScalar(fDy);
59    buffer.writeScalar(fSigmaX);
60    buffer.writeScalar(fSigmaY);
61    buffer.writeColor(fColor);
62}
63
64bool SkDropShadowImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
65                                            const Context& ctx,
66                                            SkBitmap* result, SkIPoint* offset) const
67{
68    SkBitmap src = source;
69    SkIPoint srcOffset = SkIPoint::Make(0, 0);
70    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset))
71        return false;
72
73    SkIRect bounds;
74    if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) {
75        return false;
76    }
77
78    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
79    if (NULL == device.get()) {
80        return false;
81    }
82    SkCanvas canvas(device.get());
83
84    SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
85    ctx.ctm().mapVectors(&sigma, 1);
86    sigma.fX = SkMaxScalar(0, sigma.fX);
87    sigma.fY = SkMaxScalar(0, sigma.fY);
88    SkAutoTUnref<SkImageFilter> blurFilter(SkBlurImageFilter::Create(sigma.fX, sigma.fY));
89    SkAutoTUnref<SkColorFilter> colorFilter(
90        SkColorFilter::CreateModeFilter(fColor, SkXfermode::kSrcIn_Mode));
91    SkPaint paint;
92    paint.setImageFilter(blurFilter.get());
93    paint.setColorFilter(colorFilter.get());
94    paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
95    SkVector offsetVec = SkVector::Make(fDx, fDy);
96    ctx.ctm().mapVectors(&offsetVec, 1);
97    canvas.translate(SkIntToScalar(srcOffset.fX - bounds.fLeft),
98                     SkIntToScalar(srcOffset.fY - bounds.fTop));
99    canvas.drawBitmap(src, offsetVec.fX, offsetVec.fY, &paint);
100    canvas.drawBitmap(src, 0, 0);
101    *result = device->accessBitmap(false);
102    offset->fX = bounds.fLeft;
103    offset->fY = bounds.fTop;
104    return true;
105}
106
107void SkDropShadowImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
108    if (getInput(0)) {
109        getInput(0)->computeFastBounds(src, dst);
110    } else {
111        *dst = src;
112    }
113
114    SkRect shadowBounds = *dst;
115    shadowBounds.offset(fDx, fDy);
116    shadowBounds.outset(SkScalarMul(fSigmaX, SkIntToScalar(3)),
117                        SkScalarMul(fSigmaY, SkIntToScalar(3)));
118    dst->join(shadowBounds);
119}
120
121bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
122                                             SkIRect* dst) const {
123    SkIRect bounds = src;
124    if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) {
125        return false;
126    }
127    SkVector offsetVec = SkVector::Make(fDx, fDy);
128    ctm.mapVectors(&offsetVec, 1);
129    bounds.offset(-SkScalarCeilToInt(offsetVec.x()),
130                  -SkScalarCeilToInt(offsetVec.y()));
131    SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
132    ctm.mapVectors(&sigma, 1);
133    bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
134                  SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
135    bounds.join(src);
136    *dst = bounds;
137    return true;
138}
139