1/*
2 * Copyright 2011 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 "SkBlurDrawLooper.h"
9#include "SkBlurMask.h"     // just for SkBlurMask::ConvertRadiusToSigma
10#include "SkBlurMaskFilter.h"
11#include "SkCanvas.h"
12#include "SkColorFilter.h"
13#include "SkReadBuffer.h"
14#include "SkWriteBuffer.h"
15#include "SkMaskFilter.h"
16#include "SkPaint.h"
17#include "SkString.h"
18#include "SkStringUtils.h"
19
20SkBlurDrawLooper::SkBlurDrawLooper(SkColor color, SkScalar sigma,
21                                   SkScalar dx, SkScalar dy, uint32_t flags) {
22    this->init(sigma, dx, dy, color, flags);
23}
24
25// only call from constructor
26void SkBlurDrawLooper::initEffects() {
27    SkASSERT(fBlurFlags <= kAll_BlurFlag);
28    if (fSigma > 0) {
29        uint32_t flags = fBlurFlags & kIgnoreTransform_BlurFlag ?
30                            SkBlurMaskFilter::kIgnoreTransform_BlurFlag :
31                            SkBlurMaskFilter::kNone_BlurFlag;
32
33        flags |= fBlurFlags & kHighQuality_BlurFlag ?
34                    SkBlurMaskFilter::kHighQuality_BlurFlag :
35                    SkBlurMaskFilter::kNone_BlurFlag;
36
37        fBlur = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, fSigma, flags);
38    } else {
39        fBlur = NULL;
40    }
41
42    if (fBlurFlags & kOverrideColor_BlurFlag) {
43        // Set alpha to 1 for the override since transparency will already
44        // be baked into the blurred mask.
45        SkColor opaqueColor = SkColorSetA(fBlurColor, 255);
46        //The SrcIn xfer mode will multiply 'color' by the incoming alpha
47        fColorFilter = SkColorFilter::CreateModeFilter(opaqueColor,
48                                                       SkXfermode::kSrcIn_Mode);
49    } else {
50        fColorFilter = NULL;
51    }
52}
53
54void SkBlurDrawLooper::init(SkScalar sigma, SkScalar dx, SkScalar dy,
55                            SkColor color, uint32_t flags) {
56    fSigma = sigma;
57    fDx = dx;
58    fDy = dy;
59    fBlurColor = color;
60    fBlurFlags = flags;
61
62    this->initEffects();
63}
64
65#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
66SkBlurDrawLooper::SkBlurDrawLooper(SkReadBuffer& buffer) : INHERITED(buffer) {
67
68    fSigma = buffer.readScalar();
69    fDx = buffer.readScalar();
70    fDy = buffer.readScalar();
71    fBlurColor = buffer.readColor();
72    fBlurFlags = buffer.readUInt() & kAll_BlurFlag;
73
74    this->initEffects();
75}
76#endif
77
78SkFlattenable* SkBlurDrawLooper::CreateProc(SkReadBuffer& buffer) {
79    const SkColor color = buffer.readColor();
80    const SkScalar sigma = buffer.readScalar();
81    const SkScalar dx = buffer.readScalar();
82    const SkScalar dy = buffer.readScalar();
83    const uint32_t flags = buffer.read32();
84    return Create(color, sigma, dx, dy, flags);
85}
86
87void SkBlurDrawLooper::flatten(SkWriteBuffer& buffer) const {
88    buffer.writeColor(fBlurColor);
89    buffer.writeScalar(fSigma);
90    buffer.writeScalar(fDx);
91    buffer.writeScalar(fDy);
92    buffer.write32(fBlurFlags);
93}
94
95SkBlurDrawLooper::~SkBlurDrawLooper() {
96    SkSafeUnref(fBlur);
97    SkSafeUnref(fColorFilter);
98}
99
100bool SkBlurDrawLooper::asABlurShadow(BlurShadowRec* rec) const {
101    if (fSigma <= 0 || (fBlurFlags & fBlurFlags & kIgnoreTransform_BlurFlag)) {
102        return false;
103    }
104
105    if (rec) {
106        rec->fSigma = fSigma;
107        rec->fColor = fBlurColor;
108        rec->fOffset.set(fDx, fDy);
109        rec->fStyle = kNormal_SkBlurStyle;
110        rec->fQuality = (fBlurFlags & kHighQuality_BlurFlag) ?
111                        kHigh_SkBlurQuality : kLow_SkBlurQuality;
112    }
113    return true;
114}
115
116////////////////////////////////////////////////////////////////////////////////////////
117
118SkDrawLooper::Context* SkBlurDrawLooper::createContext(SkCanvas*, void* storage) const {
119    return SkNEW_PLACEMENT_ARGS(storage, BlurDrawLooperContext, (this));
120}
121
122SkBlurDrawLooper::BlurDrawLooperContext::BlurDrawLooperContext(
123        const SkBlurDrawLooper* looper)
124    : fLooper(looper), fState(SkBlurDrawLooper::kBeforeEdge) {}
125
126bool SkBlurDrawLooper::BlurDrawLooperContext::next(SkCanvas* canvas,
127                                                   SkPaint* paint) {
128    switch (fState) {
129        case kBeforeEdge:
130            // we do nothing if a maskfilter is already installed
131            if (paint->getMaskFilter()) {
132                fState = kDone;
133                return false;
134            }
135#ifdef SK_BUILD_FOR_ANDROID
136            SkColor blurColor;
137            blurColor = fLooper->fBlurColor;
138            if (SkColorGetA(blurColor) == 255) {
139                blurColor = SkColorSetA(blurColor, paint->getAlpha());
140            }
141            paint->setColor(blurColor);
142#else
143            paint->setColor(fLooper->fBlurColor);
144#endif
145            paint->setMaskFilter(fLooper->fBlur);
146            paint->setColorFilter(fLooper->fColorFilter);
147            canvas->save();
148            if (fLooper->fBlurFlags & kIgnoreTransform_BlurFlag) {
149                SkMatrix transform(canvas->getTotalMatrix());
150                transform.postTranslate(fLooper->fDx, fLooper->fDy);
151                canvas->setMatrix(transform);
152            } else {
153                canvas->translate(fLooper->fDx, fLooper->fDy);
154            }
155            fState = kAfterEdge;
156            return true;
157        case kAfterEdge:
158            canvas->restore();
159            fState = kDone;
160            return true;
161        default:
162            SkASSERT(kDone == fState);
163            return false;
164    }
165}
166
167#ifndef SK_IGNORE_TO_STRING
168void SkBlurDrawLooper::toString(SkString* str) const {
169    str->append("SkBlurDrawLooper: ");
170
171    str->append("dx: ");
172    str->appendScalar(fDx);
173
174    str->append(" dy: ");
175    str->appendScalar(fDy);
176
177    str->append(" color: ");
178    str->appendHex(fBlurColor);
179
180    str->append(" flags: (");
181    if (kNone_BlurFlag == fBlurFlags) {
182        str->append("None");
183    } else {
184        bool needsSeparator = false;
185        SkAddFlagToString(str, SkToBool(kIgnoreTransform_BlurFlag & fBlurFlags), "IgnoreTransform",
186                          &needsSeparator);
187        SkAddFlagToString(str, SkToBool(kOverrideColor_BlurFlag & fBlurFlags), "OverrideColor",
188                          &needsSeparator);
189        SkAddFlagToString(str, SkToBool(kHighQuality_BlurFlag & fBlurFlags), "HighQuality",
190                          &needsSeparator);
191    }
192    str->append(")");
193
194    // TODO: add optional "fBlurFilter->toString(str);" when SkMaskFilter::toString is added
195    // alternatively we could cache the radius in SkBlurDrawLooper and just add it here
196}
197#endif
198