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,
19                                                 SkScalar sigmaX, SkScalar sigmaY, SkColor color,
20                                                 ShadowMode shadowMode, SkImageFilter* input,
21                                                 const CropRect* cropRect)
22    : INHERITED(1, &input, cropRect)
23    , fDx(dx)
24    , fDy(dy)
25    , fSigmaX(sigmaX)
26    , fSigmaY(sigmaY)
27    , fColor(color)
28    , fShadowMode(shadowMode)
29{
30}
31
32SkFlattenable* SkDropShadowImageFilter::CreateProc(SkReadBuffer& buffer) {
33    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
34    SkScalar dx = buffer.readScalar();
35    SkScalar dy = buffer.readScalar();
36    SkScalar sigmaX = buffer.readScalar();
37    SkScalar sigmaY = buffer.readScalar();
38    SkColor color = buffer.readColor();
39    ShadowMode shadowMode = buffer.isVersionLT(SkReadBuffer::kDropShadowMode_Version) ?
40                            kDrawShadowAndForeground_ShadowMode :
41                            static_cast<ShadowMode>(buffer.readInt());
42    return Create(dx, dy, sigmaX, sigmaY, color, shadowMode, common.getInput(0),
43                  &common.cropRect());
44}
45
46void SkDropShadowImageFilter::flatten(SkWriteBuffer& buffer) const {
47    this->INHERITED::flatten(buffer);
48    buffer.writeScalar(fDx);
49    buffer.writeScalar(fDy);
50    buffer.writeScalar(fSigmaX);
51    buffer.writeScalar(fSigmaY);
52    buffer.writeColor(fColor);
53    buffer.writeInt(static_cast<int>(fShadowMode));
54}
55
56bool SkDropShadowImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
57                                            const Context& ctx,
58                                            SkBitmap* result, SkIPoint* offset) const
59{
60    SkBitmap src = source;
61    SkIPoint srcOffset = SkIPoint::Make(0, 0);
62    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset))
63        return false;
64
65    SkIRect bounds;
66    if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) {
67        return false;
68    }
69
70    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
71    if (NULL == device.get()) {
72        return false;
73    }
74    SkCanvas canvas(device.get());
75
76    SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
77    ctx.ctm().mapVectors(&sigma, 1);
78    sigma.fX = SkMaxScalar(0, sigma.fX);
79    sigma.fY = SkMaxScalar(0, sigma.fY);
80    SkAutoTUnref<SkImageFilter> blurFilter(SkBlurImageFilter::Create(sigma.fX, sigma.fY));
81    SkAutoTUnref<SkColorFilter> colorFilter(
82        SkColorFilter::CreateModeFilter(fColor, SkXfermode::kSrcIn_Mode));
83    SkPaint paint;
84    paint.setImageFilter(blurFilter.get());
85    paint.setColorFilter(colorFilter.get());
86    paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
87    SkVector offsetVec = SkVector::Make(fDx, fDy);
88    ctx.ctm().mapVectors(&offsetVec, 1);
89    canvas.translate(SkIntToScalar(srcOffset.fX - bounds.fLeft),
90                     SkIntToScalar(srcOffset.fY - bounds.fTop));
91    canvas.drawBitmap(src, offsetVec.fX, offsetVec.fY, &paint);
92    if (fShadowMode == kDrawShadowAndForeground_ShadowMode) {
93        canvas.drawBitmap(src, 0, 0);
94    }
95    *result = device->accessBitmap(false);
96    offset->fX = bounds.fLeft;
97    offset->fY = bounds.fTop;
98    return true;
99}
100
101void SkDropShadowImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
102    if (getInput(0)) {
103        getInput(0)->computeFastBounds(src, dst);
104    } else {
105        *dst = src;
106    }
107
108    SkRect shadowBounds = *dst;
109    shadowBounds.offset(fDx, fDy);
110    shadowBounds.outset(SkScalarMul(fSigmaX, SkIntToScalar(3)),
111                        SkScalarMul(fSigmaY, SkIntToScalar(3)));
112    if (fShadowMode == kDrawShadowAndForeground_ShadowMode) {
113        dst->join(shadowBounds);
114    } else {
115        *dst = shadowBounds;
116    }
117}
118
119bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
120                                             SkIRect* dst) const {
121    SkIRect bounds = src;
122    SkVector offsetVec = SkVector::Make(fDx, fDy);
123    ctm.mapVectors(&offsetVec, 1);
124    bounds.offset(-SkScalarCeilToInt(offsetVec.x()),
125                  -SkScalarCeilToInt(offsetVec.y()));
126    SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
127    ctm.mapVectors(&sigma, 1);
128    bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
129                  SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
130    if (fShadowMode == kDrawShadowAndForeground_ShadowMode) {
131        bounds.join(src);
132    }
133    if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
134        return false;
135    }
136    *dst = bounds;
137    return true;
138}
139
140#ifndef SK_IGNORE_TO_STRING
141void SkDropShadowImageFilter::toString(SkString* str) const {
142    str->appendf("SkDropShadowImageFilter: (");
143
144    str->appendf("dX: %f ", fDx);
145    str->appendf("dY: %f ", fDy);
146    str->appendf("sigmaX: %f ", fSigmaX);
147    str->appendf("sigmaY: %f ", fSigmaY);
148
149    str->append("Color: ");
150    str->appendHex(fColor);
151
152    static const char* gModeStrings[] = {
153        "kDrawShadowAndForeground", "kDrawShadowOnly"
154    };
155
156    SK_COMPILE_ASSERT(kShadowModeCount == SK_ARRAY_COUNT(gModeStrings), enum_mismatch);
157
158    str->appendf(" mode: %s", gModeStrings[fShadowMode]);
159
160    str->append(")");
161}
162#endif
163